115 lines
7.0 KiB
Markdown
115 lines
7.0 KiB
Markdown
# CIM Service Suite — Design System ("Instrument")
|
|
|
|
> **Scope: this is the binding visual standard for the whole suite.** Every current
|
|
> and future module follows it so the unified app looks like one product. The
|
|
> authoritative specification — every color token, type style, geometry value, and
|
|
> component rule — is **[`Instrument Design Spec.md`](Instrument%20Design%20Spec.md)**.
|
|
> This document is the working guide: where the implementation lives, how to adopt
|
|
> it in a module, and the rules that keep it coherent. If a new module needs
|
|
> something the system doesn't cover, **extend the theme/kit packages** — never
|
|
> introduce per-module colors, fonts, or one-off styling.
|
|
|
|
The mockups (`Instrument Suite (standalone).html`) are a visual reference ONLY —
|
|
never pixel-measure or scrape values from them; the spec's tables are the source of
|
|
truth. Rollout history: `docs/superpowers/specs/2026-06-10-instrument-design-system-rollout-design.md`
|
|
(six phases, all merged — per-phase records in `docs/BACKLOG.md` BL-DS1..BL-DS-P6).
|
|
|
|
## Implementation map
|
|
|
|
```
|
|
cim_suite/core/ui/theme/ The theme layer — the only place colors/fonts/QSS live
|
|
tokens.py Two theme dicts (light/dark) keyed by spec token names,
|
|
+ SEVERITY/STALENESS/CHART maps, Space/Radius/Metrics
|
|
fonts.py Bundled IBM Plex Sans + IBM Plex Mono (OFL), loaded at startup
|
|
type_styles.py The spec's named type styles as QFont factories
|
|
(letter-spacing/weight included — QSS can't express tracking)
|
|
stylesheet.py build_qss(...) — the entire QSS, generated from tokens
|
|
manager.py current() accessor + themeChanged signal + persistence
|
|
__init__.py apply_theme(app, theme=None) — fonts + QSS + Fusion base
|
|
|
|
cim_suite/core/ui/kit/ The component kit (spec §5) — built once, adopted per module
|
|
delegate.py InstrumentDelegate: cell kinds, §1.2 status tags, edge bars,
|
|
alarm-row tint, hover edit affordance, styled editor,
|
|
validation, write feedback (mark_pending/resolve), QUIET_ROLE
|
|
settings_list.py SettingsList (§5.5): groups, hints, RO chips, toggles, choices
|
|
summary_strip.py SummaryStrip (§5.4 footer) + the shared EDIT_HINT constant
|
|
units_header.py UnitsHeaderView: microcaps header + mono units line
|
|
tab_widget.py InstrumentTabWidget: §5.3 underline tabs
|
|
toggle_switch.py ToggleSwitch (§5.4 boolean columns)
|
|
connection_chip.py ConnectionChip (§5.2 toolbar pill)
|
|
sidebar.py Sidebar (§5.7 device list)
|
|
activity_log.py ActivityLogCard (§5.8)
|
|
icons.py currentColor-tinted SVG icons
|
|
|
|
cim_suite/core/ui/chrome/ Window chrome (spec §4): frameless TitleBar, AccentStrip,
|
|
StatusFooter, ChromeDialog + message_box confirms
|
|
```
|
|
|
|
`apply_theme(app, theme=None)` is called once by the shell; it loads the saved theme
|
|
choice when `theme` is omitted. Theme choice persists in the JSON config under
|
|
`ui.json`. **Theme switching:** `set_theme(app, name)` regenerates and reapplies the
|
|
QSS; anything that paints token colors in code must read `theme.current()` **at paint
|
|
time** (delegates do this) or repaint on `manager.signals.themeChanged`.
|
|
|
|
## Rules (enforced)
|
|
|
|
- **No hard-coded hex and no `setStyleSheet` outside `cim_suite/core/ui/theme/`** —
|
|
`tests/core/test_design_discipline.py` fails the build otherwise.
|
|
- **Fonts come from `type_styles`**, never ad-hoc `QFont(...)` — microcaps tracking
|
|
and the Plex families only exist there (Qt ignores `text-transform`/tracking in
|
|
QSS, so microcaps text is `.upper()` + the style's font, in code).
|
|
- **Status is never color alone** (§1.2): the kit's status tags pair the dot shape
|
|
with a text label; alarm rows pair the tint with the edge bar.
|
|
- **One primary action per toolbar** (§5.1): the connect/disconnect verb gets
|
|
`objectName="PrimaryAction"`; everything else is secondary/quiet.
|
|
- **Single-click inline editing** (§5.6) via `TableTab`/the kit delegate;
|
|
`Qt.ItemIsEditable` is the single source of editability. When repopulating a table
|
|
from the model, run under the tab's `_loading` guard so programmatic fills don't
|
|
fire phantom edits (write-storm protection — regression-tested).
|
|
- Geometry/spacing from `Space`/`Radius`/`Metrics` tokens, not magic numbers.
|
|
|
|
## Component hooks
|
|
|
|
| Intent | How |
|
|
|---|---|
|
|
| Highlighted toolbar action | tool button `objectName="PrimaryAction"` |
|
|
| Brand marks in the toolbar | `QLabel` `objectName="BrandTitle"` / `"BrandTitleAccent"` |
|
|
| Filled accent button | `button.setProperty("variant", "primary")` |
|
|
| Footer readouts | `chrome.StatusFooter` — declarative `add_dot`/`add_field` |
|
|
| Confirm/info dialogs | `chrome.message_box` submodule — `message_box.question/information/warning` (chrome-framed, breadcrumb titles) |
|
|
| Grid summary footer | `TableTab.enable_summary(kit.EDIT_HINT)` + `summary.set_summary([...])` |
|
|
|
|
## For new modules — adoption checklist
|
|
|
|
The worked examples are the three shipped modules; `cim_suite/modules/da07/ui/` is
|
|
the most recent full adoption and `cim_suite/modules/iomodbus/ui/` shows the sidebar +
|
|
activity log. Copy this checklist into the module's adoption plan:
|
|
|
|
- [ ] Toolbar per spec §5.2: `← Suite` (emit `suiteRequested`; the shell wires it),
|
|
brand labels + `kit.ConnectionChip` (the module feeds COM/SIM source labels),
|
|
rare/risky commands behind a confirm-guarded `▾` menu
|
|
(`menu.setToolTipsVisible(True)` — QMenu hides tooltips by default),
|
|
primary Connect/Disconnect far right
|
|
- [ ] Grid screens subclass `TableTab`; column kinds set on the delegate
|
|
(`NUMERIC`/`IDENTIFIER`/`STATUS`/`TOGGLE`), units rows via `set_column_units`,
|
|
header help via `set_column_help` (tooltips — never ⓘ markers)
|
|
- [ ] Status columns use §1.2 status tags (`STATUS_ROLE`), full-row alarm treatment
|
|
via `ALARM_ROW_ROLE` for alarm-class states only
|
|
- [ ] Summary strip with `kit.EDIT_HINT` on every editable grid
|
|
- [ ] Station/settings screens use `kit.SettingsList` (§5.5) with curated groups;
|
|
read-only governed by the wire/protocol, never invented; toggles/choices only
|
|
where the value's meaning is verified (VB6-fidelity rule)
|
|
- [ ] Write feedback (§5.6): `delegate.mark_pending` on edit; `resolve` on the
|
|
protocol's confirmation (echo or re-read)
|
|
- [ ] No `setStyleSheet`, no hex, no ad-hoc fonts in module code (the guard test
|
|
enforces it); custom paints read `theme.current()` at paint time
|
|
- [ ] Spreadsheet export wired per `docs/EXPORT.md`
|
|
- [ ] Status shown by color *and* text; exactly one primary action per surface
|
|
|
|
## Fonts & licensing
|
|
|
|
IBM Plex Sans and IBM Plex Mono are bundled under the SIL Open Font License
|
|
(`cim_suite/core/ui/theme/fonts/OFL-IBMPlex.txt`) and registered at startup via
|
|
`QFontDatabase`; if the assets are missing, the theme degrades to system fonts
|
|
rather than failing.
|