~/.config/quickshellsymlinks to this checkout. Starting/reloadingqs, calling its IPC, or restarting desktop services affects the running session; do so only when explicitly requested.settings.jsonandmenus.jsonare watched live, so edits can take effect without a restart.lock.qmlacquires a real Wayland session lock. Despite its name,lock-test.qmllaunches UI with the realLockContext/PAM, not an offline test. Usetests/lock/for safe verification.- Runtime helpers assume installation at
~/.config/quickshell; running wallpaper, clipboard, translation, or weather helpers directly can change desktop state, access personal data, or make network requests. Prefer mocked offline tests. Never print or commitDEEPL_API.
shell.qmlowns shared services and creates aBarper screen. Keep feature logic out ofbar/Bar.qml; bar modules are adapters to their popup/service, not owners of duplicate per-screen backend services.components/PopupManager.qmlowns bar popup windows and their motion.PopupMotion.qmlalso serves standalone popups; keep surfaces mapped until close animations finish and preserve reopen-during-close behavior.- Popups may be destroyed on close. Keep persistent selections/preferences in their controller/service, reset transient help/confirmation state, and release discovery/process resources on destruction. Preserve both keyboard and mouse controls.
- Use direct object calls internally; IPC is the external control boundary, with unique target names. Do not add dynamic QML module loading unless requested.
- Keep icon and text sizing independent inside widgets: both
config.iconSizeandconfig.fontSizeare numeric base sizes. Scale them with multiplication (config.iconSize * 0.88,config.fontSize * 0.88), not function calls. Apply any widget-specific scaling separately to each; never bind icon size to the text font size. Reusable controls should expose an independent icon-size override while followingconfig.iconSizeby default. - For ordinary text in controls, menus and popups, use
config.textSizeNormal(headers, prompts, input/main content),config.textSizeMedium(buttons, list labels, compact body text), orconfig.textSizeSmall(secondary details, timestamps, status/help). These derive fromfontSize; do not repeat their ratios. Avoid incidental multipliers and oversized headers; reserve custom text scaling for genuinely special displays. Icon sizing and special displays such as Weather, the lock screen and large clocks stay separate. - For large menu/popup interiors, use
config.spacingTight(4 px) for related items,config.spacingControl(8 px) between controls,config.panelPadding(12 px) for panel/section insets, andconfig.previewPadding(16 px) for previews. Measure outer padding inside the surface border, center header/footer content, and keep these internal values separate from screen placement viapopupGap. Preserve intentional dense-list and image-layout exceptions. - Keep keyboard navigation consistent with visual order: Left/Right for horizontal control rows, Up/Down for vertical lists, and Tab/Shift+Tab for forward/backward focus traversal in that same order. Arrow navigation and Tab navigation must update the same focused/selected control and visual highlight; Enter/Space activates it. Preserve text-field editing keys and existing popup shortcuts, and test both navigation paths when changing interactive layouts.
- Avoid copy-paste duplication. Reuse the shared pieces (
PanelSurface,PanelSeparator,BarToast,MenuRow,PopupIpcHandler,CriticalColors.js,HistoryModel.iconSource(),Value.isObject()) instead of re-implementing them per feature. When a block is needed in more than one place, extract a shared component or extend an existing one with an override property; do not fork a near-identical copy. Keep shared QML appearance-only and passconfigin rather than reading settings directly.
- Long-running code (the shell, daemons, watchers) reacts to events instead of polling with a timer. Polling is the absolutely last resort
services/config/Schema.jsis the source of truth forsettings.json, exposed byservices/Config.qml. Some README settings prose is stale: animations currently expose onlyenabled; style/duration belong in each QML animation owner.- Personal menu commands belong in root
menus.json, validated bymenus/MenuSchema.jsand loaded byMenuConfig.qml. Lists areuser-menu1–user-menu4; Power and built-in action handlers stay inMainMenu.qml. Preserve last-valid reload behavior and cancel stale selection/confirmation when lists change. - UI preferences are separate from tracked defaults:
SettingsService.qmlwrites$XDG_DATA_HOME/quickshell-settings.json, and Translator writesquickshell-translator.json(fallback directory~/.local/share). Do not overwrite these files while testing. - Theme colors come from
ThemeService.qml; Hellwal output in~/.cache/hellwal/colors.cssis generated. Lock-screen weather only reads the weather cache; keep missing/invalid cache data harmless and avoid adding network requests to the locker.
- No root build manifest or CI pipeline. From the repo root:
python -B tests/run.pyruns all offline suites;python -B tests/run.py launcher menusselects features;python -B tests/run.py --listlists them. The runner uses offscreen Qt, disables bytecode, and never starts/reloads the desktop shell. - All tests and test-only fixtures belong in
tests/<feature>/, not runtime folders. Register suites intests/run.py. Python tests often copy real QML into temporary directories and replace integrations with doubles; moving runtime files/imports may require updating these extraction paths too. Reusetests/qml_support.py(run_qml_tests(),copy_control_config()) andtests/components/ControlConfig.qmlinstead of repeating runner setup or inline config blocks in each fixture. - Single Python file:
python -B -m unittest discover -s tests/lock -p test_surface.py. Single QML file:QT_QPA_PLATFORM=offscreen /usr/lib/qt6/bin/qmltestrunner -input tests/components/tst_PopupMotion.qml -o -,txt. - Use Qt 6 explicitly:
/usr/lib/qt6/bin/qmlformat -i FILE.qml. The genericqmlformaton PATH can be an older parser that rejects valid functions/QML.qmllintdoes not reliably resolve Quickshell imports here; do not treat unresolved-import warnings as a clean gate. - Before verification, format every QML file you changed (including test fixtures) with
/usr/lib/qt6/bin/qmlformat -i FILE.qml. Keep routine formatting scoped to changed files; do not use--sort-imports, since import order can affect type resolution. Run the relevant offline tests after formatting. - Python syntax without bytecode:
python -c 'import ast, pathlib; p=pathlib.Path("FILE"); ast.parse(p.read_text(), filename=str(p))'. Finish edits withgit diff --check.
- Keep README to basic features, usage, and structure; put setup details/examples in
assets/INSTALL.md, not lengthy implementation explanations in README.