| Software | Version | Purpose |
|---|---|---|
| Qt | 6.x | QML runtime |
| Quickshell | Latest | Shell framework |
| DankMaterialShell | Latest | Host shell environment |
| gcal CLI | Latest | Google Calendar integration |
- DankMaterialShell must be installed and running
- gcal CLI tool must be in PATH
- Google Cloud project with Calendar API enabled
- OAuth 2.0 credentials downloaded
cd ~/Code
git clone <repository-url> dms-plugins
cd dms-pluginsDMS scans for plugins in configured directories. Ensure dms-plugins/ is in the scan path:
# Option 1: Symlink to default plugin directory
ln -s ~/Code/dms-plugins/MeetingWidget ~/.local/share/DankMaterialShell/plugins/MeetingWidget
# Option 2: Add to DMS plugin scan paths in settings-
Create Google Cloud Project
- Go to Google Cloud Console
- Create new project or select existing
-
Enable Calendar API
- Navigate to APIs & Services → Library
- Search for "Google Calendar API"
- Click Enable
-
Configure OAuth Consent
- Go to OAuth consent screen
- Select External user type
- Add your email as test user
- Add scope:
https://www.googleapis.com/auth/calendar.readonly
-
Create OAuth Credentials
- Go to Credentials → Create Credentials → OAuth client ID
- Select "Desktop application"
- Add redirect URI:
http://localhost:8085/callback - Download JSON
-
Install Credentials
mkdir -p ~/.config/DankMaterialShell mv ~/Downloads/client_secret_*.json ~/.config/DankMaterialShell/gcal-credentials.json chmod 600 ~/.config/DankMaterialShell/gcal-credentials.json
-
Authenticate
dms gcal auth # or with custom port: dms gcal auth --port 9000
-
Edit QML Files
- Changes are picked up on next DMS reload
- No build step required
-
Test Changes
# Reload Quickshell to pick up changes quickshell --reload # Or restart DMS entirely dms restart
-
Debug Output
- Use
console.log()in QML - View logs:
journalctl -f -u dmsor terminal output
- Use
MeetingWidget/
├── plugin.json # Required: Plugin manifest
├── README.md # Required: User documentation
├── <PluginName>.qml # Required: Main component
├── <PluginName>Tab.qml # Optional: Dashboard tab
├── <PluginName>Settings.qml # Optional: Settings panel
└── services/ # Optional: Service components
└── <Service>.qml
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
Singleton {
id: root
property var data: []
function refresh() {
// ...
}
}Process {
id: myProcess
command: ["my-cli", "subcommand"]
running: false
stdout: StdioCollector {
onStreamFinished: {
try {
let result = JSON.parse(text)
// Handle result
} catch (e) {
console.log("Parse error:", e)
}
}
}
}import qs.Modules.Plugins
PluginComponent {
id: root
// Read settings via pluginData
readonly property int myValue: pluginData.myValue || 5
// Bar widget variants
horizontalBarPill: Component { /* ... */ }
verticalBarPill: Component { /* ... */ }
// Simple click handler (no params)
pillClickAction: () => {
mySlideout.toggle()
}
// Or with position params for popouts
// pillClickAction: (x, y, width, section, screen) => { ... }
}import qs.Modules.Plugins
PluginSettings {
id: root
pluginId: "meetingWidget"
ToggleSetting {
settingKey: "myToggle"
label: "Enable Feature"
defaultValue: true
}
SliderSetting {
settingKey: "myNumber"
label: "Value"
minimum: 1
maximum: 100
defaultValue: 50
}
ColorSetting {
settingKey: "myColor"
label: "Color"
defaultValue: "#ffffff"
}
}-
Widget Display
- Verify bar pill renders correctly
- Check horizontal and vertical variants
- Test click opens dashboard
-
Calendar Integration
- Verify events load after auth
- Check countdown timer updates
- Test join button opens URL
-
Settings
- Change settings and verify effect
- Test color pickers
- Verify persistence across restarts
# Check OAuth status
gcal status
# Fetch events (raw output)
gcal events
# Debug authentication
gcal auth --verbose| Issue | Solution |
|---|---|
| "Not configured" in widget | Run gcal auth |
| Events not loading | Check gcal status for errors |
| Widget not appearing | Ensure plugin is enabled in Settings |
| Settings not saving | Check plugin permissions in manifest |
Add debug output to track state:
Component.onCompleted: {
console.log("MyComponent: initializing...")
}
onPropertyChanged: {
console.log("Property changed:", newValue)
}- Quickshell logs: Terminal or
journalctl -u quickshell - DMS logs:
~/.local/share/DankMaterialShell/logs/ - gcal logs: Terminal output during auth
- 4-space indentation
- camelCase for properties and functions
- PascalCase for component names
- Descriptive property names
- QML files lint without errors
- Manual testing completed
- README updated if needed
- No hardcoded paths
- Console.log statements removed (except for debug builds)