From 221ae94f4f56be5c03ff809db6be994508a777f3 Mon Sep 17 00:00:00 2001 From: Jeffrey Smith Date: Wed, 1 Apr 2026 11:58:39 +0000 Subject: [PATCH] Feat v0.6.12 css isolation (#47) Co-authored-by: Jeffrey Smith Co-committed-by: Jeffrey Smith --- CHANGELOG.md | 31 ++ ROADMAP-UI.md | 15 +- ROADMAP.md | 2 +- VERSION | 2 +- docs/EXTENSION-CSS.md | 149 +++++++++ packages/chat/css/main.css | 162 +++++----- packages/chat/js/main.js | 144 ++++----- packages/dashboard/css/main.css | 50 +-- packages/dashboard/js/main.js | 76 ++--- packages/editor/css/main.css | 150 ++++----- packages/editor/js/main.js | 42 +-- packages/git-board/css/main.css | 120 ++++---- packages/git-board/js/main.js | 120 ++++---- packages/hello-dashboard/css/main.css | 26 +- packages/hello-dashboard/js/main.js | 20 +- packages/icd-test-runner/css/main.css | 18 +- packages/icd-test-runner/js/main.js | 1 + packages/icd-test-runner/js/tier-sdk.js | 4 +- packages/notes/css/main.css | 288 +++++++++--------- packages/notes/js/main.js | 172 +++++------ packages/schedules/css/main.css | 100 +++--- packages/schedules/js/main.js | 76 ++--- packages/sdk-test-runner/css/main.css | 78 ++--- packages/sdk-test-runner/js/ui.js | 56 ++-- packages/tasks/css/main.css | 104 +++---- packages/tasks/js/main.js | 80 ++--- packages/team-activity-log/css/main.css | 62 ++-- packages/team-activity-log/js/main.js | 48 +-- packages/workflow-demo/css/main.css | 76 ++--- packages/workflow-demo/js/main.js | 54 ++-- scripts/lint-package-css.sh | 129 ++++++++ .../pages/templates/surfaces/extension.html | 2 +- src/js/sw/sdk/markdown.js | 2 +- 33 files changed, 1385 insertions(+), 1074 deletions(-) create mode 100644 docs/EXTENSION-CSS.md create mode 100755 scripts/lint-package-css.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a4cd67..d52dfdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ All notable changes to Armature are documented here. +## v0.6.12 — Extension CSS Isolation + +Prefix enforcement prevents extension CSS from leaking into the kernel or +sibling extensions. All 12 in-tree packages migrated to `.ext-{slug}-*` +naming convention. + +### Added + +- **`data-ext` attribute** on extension mount container — enables scoped + selectors like `[data-ext="chat"] .ext-chat-app`. +- **CSS linter** (`scripts/lint-package-css.sh`) — validates that the first + class selector in every extension CSS rule starts with `.ext-{slug}`. + Exempts `:root`, `@keyframes`, `@font-face`, `@media`, kernel `.sw-*` + classes, and CodeMirror `.cm-*` classes. +- **Kernel CSS contract** (`docs/EXTENSION-CSS.md`) — documents stable + public classes and CSS variables that extensions may reference. Everything + else is internal kernel CSS. + +### Changed + +- **12 packages migrated** — all class selectors renamed to `.ext-{slug}-*`: + chat, dashboard, editor, git-board, hello-dashboard, icd-test-runner, + notes, schedules, sdk-test-runner, tasks, team-activity-log, workflow-demo. + CSS and JS files updated in lockstep. +- **`icd-test-runner`** — ID selectors (`#extension-mount`) converted to + class-based selectors with proper prefix. +- **`editor` cross-references** — compound selectors referencing notes + classes updated to new `.ext-notes-*` names. +- **`chat` kernel overrides** — `.sw-dialog:has(...)` override scoped under + `[data-ext="chat"]` instead of global. + ## v0.6.11 — CSS Deduplication One class per concept. The old `primitives.css` button, toast, popup-menu, diff --git a/ROADMAP-UI.md b/ROADMAP-UI.md index 771fb37..2254d6a 100644 --- a/ROADMAP-UI.md +++ b/ROADMAP-UI.md @@ -63,16 +63,17 @@ Shipped. Old primitive system retired. One class per concept. See CHANGELOG.md. | Mark deprecated classes | Any remaining old classes get a `/* DEPRECATED v0.6.11 — use .sw-btn--* */` comment and a 1-version grace period for package authors. | | Package CSS audit | Scan all `packages/*/css/main.css` for references to deprecated kernel classes. Fix in-tree packages. | -### v0.6.12 — Extension CSS Isolation +### v0.6.12 — Extension CSS Isolation ✅ -Prevent extension CSS from leaking into the kernel or sibling extensions. +Shipped. Prefix enforcement via linter. All 12 in-tree packages migrated. +See CHANGELOG.md. | Step | Description | |------|-------------| -| Scoping strategy | Two options: (A) `@scope (.extension-mount[data-ext="slug"])` — CSS `@scope` is supported in Chrome 118+, Firefox 128+, Safari 17.4+. (B) Prefix enforcement — package CSS linter rejects selectors that don't start with `.ext-{slug}` or `[data-ext="{slug}"]`. **Recommendation**: (B) prefix enforcement. `@scope` support is still patchy; prefix enforcement works everywhere and is trivially lintable. | -| Linter | `scripts/lint-package-css.sh` — shell script using `grep`/`awk`. Runs in CI. Accepts `.ext-{slug}` or `[data-ext="{slug}"]` prefixed selectors only. Exempts `:root`, `@keyframes`, `@font-face`, and `@media` wrappers. | -| Kernel CSS contract | Document which kernel classes are stable public API for extensions to reference (`.sw-btn--*`, `.sw-input`, `.sw-field`, `.sw-dialog`, `.sw-toast`, `.sw-menu`, `.sw-tabs`, CSS variables). Everything else is internal. Publish in `docs/EXTENSION-CSS.md`. | -| Migrate in-tree packages | Add `data-ext` attribute to extension mount container. Prefix all in-tree package CSS. Verify no visual regressions. | +| Scoping strategy | Prefix enforcement (option B). `@scope` support still patchy; prefix works everywhere, trivially lintable. | +| Linter | `scripts/lint-package-css.sh` — validates first class selector in every rule starts with `.ext-{slug}`. Exempts `:root`, `@keyframes`, `@font-face`, `@media`, `.sw-*` kernel classes, `.cm-*` CodeMirror classes. | +| Kernel CSS contract | `docs/EXTENSION-CSS.md` — stable public classes (`.sw-btn--*`, `.sw-input`, `.sw-field`, `.sw-dialog`, `.sw-toast`, `.sw-menu`, `.sw-tabs`, etc.) and all CSS variables. Everything else internal. | +| Migrate in-tree packages | `data-ext="{{.Surface}}"` on extension mount. All 12 packages prefixed to `.ext-{slug}-*`. CSS + JS updated in lockstep. | ### v0.6.13 — Responsive & Spacing @@ -120,7 +121,7 @@ v0.6.10 Viewport Foundation ✅ SHIPPED ↓ v0.6.11 CSS Deduplication ✅ SHIPPED ↓ -v0.6.12 Extension CSS Isolation ← Scoping requires the kernel CSS to be stable first +v0.6.12 Extension CSS Isolation ✅ SHIPPED ↓ v0.6.13 Responsive & Spacing ← Spacing tokens need stable class names to attach to ↓ diff --git a/ROADMAP.md b/ROADMAP.md index 857403e..ed71405 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,6 +1,6 @@ # Armature — Roadmap -## Current: v0.6.11 — CSS Deduplication +## Current: v0.6.12 — Extension CSS Isolation Self-hosted extensible platform. Auth, identity, packages, Starlark sandbox, storage, realtime, and ops are kernel primitives. Everything else is an extension. diff --git a/VERSION b/VERSION index 45a346d..592e815 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.11 +0.6.12 diff --git a/docs/EXTENSION-CSS.md b/docs/EXTENSION-CSS.md new file mode 100644 index 0000000..47396ba --- /dev/null +++ b/docs/EXTENSION-CSS.md @@ -0,0 +1,149 @@ +# Extension CSS Contract + +> **Version**: v0.6.12 — Extension CSS Isolation + +This document defines the CSS contract between the Armature kernel and extension +packages. Extensions **must** follow these rules; the kernel guarantees the listed +classes and variables are stable public API. + +--- + +## Naming Rule + +All class selectors in extension CSS (`packages/{slug}/css/main.css`) must start +with `.ext-{slug}-`. The `{slug}` is the package directory name. + +```css +/* Good */ +.ext-my-app-sidebar { ... } +.ext-my-app-card { ... } + +/* Bad — will be rejected by the linter */ +.sidebar { ... } +.my-sidebar { ... } +``` + +**Compound selectors**: Descendant classes scoped under your `.ext-{slug}` root +are allowed to reference kernel classes or state modifiers: + +```css +/* Allowed — kernel class scoped under extension namespace */ +.ext-my-app .sw-btn { margin-top: 8px; } + +/* Allowed — state modifier on an extension element */ +.ext-my-app-item.active { ... } +``` + +Run `bash scripts/lint-package-css.sh` to validate. The linter checks that the +**first** class selector in every rule starts with `.ext-{slug}`. + +--- + +## Stable Kernel Classes + +Extensions may reference these classes in compound selectors. They are part of +the public API and will not change without a major version bump. + +### Components (from `sw-primitives.css`) + +| Class pattern | Component | +|---------------|-----------| +| `.sw-btn`, `.sw-btn--{variant}`, `.sw-btn--{size}` | Buttons | +| `.sw-input` | Text inputs | +| `.sw-field`, `.sw-field__label`, `.sw-field__hint` | Form fields | +| `.sw-dialog`, `.sw-dialog__header`, `.sw-dialog__body`, `.sw-dialog__footer` | Dialogs | +| `.sw-toast`, `.sw-toast-container` | Toast notifications | +| `.sw-menu`, `.sw-menu-item` | Context menus | +| `.sw-tabs`, `.sw-tab-btn` | Tab strips | +| `.sw-dropdown` | Custom dropdowns | +| `.sw-spinner` | Loading spinners | +| `.sw-avatar` | User avatars | +| `.sw-drawer` | Slide-out drawers | +| `.sw-banner` | Banner bars | +| `.sw-tooltip` | Tooltips | + +### Extension Mount + +The extension surface container has a `data-ext` attribute set to the package +slug. Use this for scoping if needed: + +```css +[data-ext="my-app"] .ext-my-app-sidebar { ... } +``` + +--- + +## Stable CSS Variables + +All variables from `variables.css` are public API. Extensions should use these +instead of hardcoded colors to respect the user's theme. + +### Colors + +| Variable | Purpose | +|----------|---------| +| `--bg` | Page background | +| `--bg-secondary` | Secondary/darker background | +| `--bg-elevated` | Elevated surface background | +| `--bg-raised` | Raised card background | +| `--bg-surface` | Surface-level background | +| `--bg-hover` | Hover state background | +| `--bg-active` | Active/pressed state background | +| `--bg-code` | Code block background | +| `--text` | Primary text color | +| `--text-2` | Secondary text color | +| `--text-3` | Tertiary/muted text color | +| `--text-on-color` | Text on colored backgrounds | +| `--accent` | Primary accent color | +| `--accent-dim` | Dimmed accent for backgrounds | +| `--accent-hover` | Accent hover state | +| `--accent-light` | Light accent variant | +| `--border` | Default border color | +| `--border-light` | Light border variant | +| `--border-elevated` | Border for elevated surfaces | +| `--danger` | Error/destructive color | +| `--danger-dim` | Dimmed danger background | +| `--danger-light` | Light danger variant | +| `--success` | Success/positive color | +| `--success-dim` | Dimmed success background | +| `--success-light` | Light success variant | +| `--warning` | Warning/caution color | +| `--warning-dim` | Dimmed warning background | +| `--warning-light` | Light warning variant | +| `--purple` | Purple accent | +| `--purple-dim` | Dimmed purple background | + +### Layout & Typography + +| Variable | Purpose | +|----------|---------| +| `--font` | Primary font family | +| `--mono` | Monospace font family | +| `--radius` | Default border-radius (8px) | +| `--radius-lg` | Large border-radius (12px) | +| `--transition` | Default transition timing | +| `--shadow-lg` | Large elevation shadow | +| `--overlay` | Modal overlay color | +| `--glass` | Glassmorphism backdrop | +| `--input-bg` | Form input background | +| `--sidebar-w` | Sidebar width | + +--- + +## What Is Internal + +Everything not listed above is **internal kernel CSS** and may change between +minor versions. Extensions must not depend on: + +- Kernel layout classes (`.admin-*`, `.surface-*`, `.sidebar`, etc.) +- Kernel CSS file load order +- Specific HTML structure of the shell or topbar +- Undocumented CSS variables + +--- + +## Enforcement + +The linter script `scripts/lint-package-css.sh` runs against all +`packages/*/css/main.css` files. It exits non-zero if any rule's first class +selector does not start with `.ext-{slug}`. diff --git a/packages/chat/css/main.css b/packages/chat/css/main.css index 53a1c31..e3fa64b 100644 --- a/packages/chat/css/main.css +++ b/packages/chat/css/main.css @@ -9,7 +9,7 @@ /* ── Layout ─────────────────────────────── */ -.chat-app { +.ext-chat-app { display: flex; flex-direction: column; height: 100%; @@ -18,20 +18,20 @@ color: var(--text); } -.chat-loading { +.ext-chat-loading { display: flex; align-items: center; justify-content: center; height: 100%; } -.chat-body { +.ext-chat-body { display: flex; flex: 1; min-height: 0; } -.chat-main { +.ext-chat-main { display: flex; flex-direction: column; flex: 1; @@ -40,7 +40,7 @@ /* ── Topbar extras ──────────────────────── */ -.chat-topbar__thread-title { +.ext-chat-topbar__thread-title { font-weight: 600; font-size: 14px; margin-right: 8px; @@ -49,7 +49,7 @@ /* ── Sidebar ────────────────────────────── */ -.chat-sidebar { +.ext-chat-sidebar { width: 280px; min-width: 280px; border-right: 1px solid var(--border); @@ -58,7 +58,7 @@ background: var(--bg-secondary); } -.chat-sidebar__header { +.ext-chat-sidebar__header { display: flex; align-items: center; justify-content: space-between; @@ -66,46 +66,46 @@ border-bottom: 1px solid var(--border); } -.chat-sidebar__title { +.ext-chat-sidebar__title { font-weight: 600; font-size: 14px; } -.chat-sidebar__list { +.ext-chat-sidebar__list { flex: 1; overflow-y: auto; } -.chat-sidebar__empty { +.ext-chat-sidebar__empty { padding: 24px 16px; text-align: center; color: var(--text-3); font-size: 13px; } -.chat-sidebar__item { +.ext-chat-sidebar__item { padding: 10px 16px; cursor: pointer; border-bottom: 1px solid var(--border-light); transition: background 0.1s; } -.chat-sidebar__item:hover { +.ext-chat-sidebar__item:hover { background: var(--bg-hover); } -.chat-sidebar__item--active { +.ext-chat-sidebar__item--active { background: var(--accent-dim); } -.chat-sidebar__item-top { +.ext-chat-sidebar__item-top { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 2px; } -.chat-sidebar__item-title { +.ext-chat-sidebar__item-title { font-weight: 600; font-size: 13px; white-space: nowrap; @@ -115,19 +115,19 @@ margin-right: 8px; } -.chat-sidebar__item-time { +.ext-chat-sidebar__item-time { font-size: 11px; color: var(--text-3); white-space: nowrap; } -.chat-sidebar__item-bottom { +.ext-chat-sidebar__item-bottom { display: flex; align-items: center; gap: 6px; } -.chat-sidebar__item-preview { +.ext-chat-sidebar__item-preview { font-size: 12px; color: var(--text-2); white-space: nowrap; @@ -136,7 +136,7 @@ flex: 1; } -.chat-sidebar__badge { +.ext-chat-sidebar__badge { background: var(--accent); color: var(--text-on-color); font-size: 11px; @@ -153,13 +153,13 @@ /* ── Sidebar Search ────────────────────── */ -.chat-sidebar__search { +.ext-chat-sidebar__search { position: relative; padding: 8px 16px; border-bottom: 1px solid var(--border-light); } -.chat-sidebar__search-input { +.ext-chat-sidebar__search-input { width: 100%; border: 1px solid var(--border); border-radius: 6px; @@ -171,12 +171,12 @@ box-sizing: border-box; } -.chat-sidebar__search-input:focus { +.ext-chat-sidebar__search-input:focus { outline: none; border-color: var(--accent); } -.chat-sidebar__search-clear { +.ext-chat-sidebar__search-clear { position: absolute; right: 22px; top: 50%; @@ -190,16 +190,16 @@ line-height: 1; } -.chat-sidebar__search-clear:hover { +.ext-chat-sidebar__search-clear:hover { color: var(--text); } -.chat-sidebar__search-results { +.ext-chat-sidebar__search-results { flex: 1; overflow-y: auto; } -.chat-sidebar__search-section { +.ext-chat-sidebar__search-section { padding: 8px 16px 4px; font-size: 11px; font-weight: 600; @@ -208,13 +208,13 @@ color: var(--text-3); } -.chat-sidebar__search-loading { +.ext-chat-sidebar__search-loading { display: flex; justify-content: center; padding: 16px; } -.chat-sidebar__item--search-msg .chat-sidebar__item-preview { +.ext-chat-sidebar__item--search-msg .ext-chat-sidebar__item-preview { font-size: 13px; white-space: normal; display: -webkit-box; @@ -225,20 +225,20 @@ /* ── Message Thread ─────────────────────── */ -.chat-thread { +.ext-chat-thread { flex: 1; display: flex; flex-direction: column; min-height: 0; } -.chat-thread--empty { +.ext-chat-thread--empty { align-items: center; justify-content: center; color: var(--text-3); } -.chat-thread__messages { +.ext-chat-thread__messages { flex: 1; overflow-y: auto; padding: 16px; @@ -247,19 +247,19 @@ gap: 4px; } -.chat-thread__loading { +.ext-chat-thread__loading { display: flex; justify-content: center; padding: 24px; } -.chat-thread__loading-more { +.ext-chat-thread__loading-more { display: flex; justify-content: center; padding: 8px; } -.chat-thread__load-more { +.ext-chat-thread__load-more { align-self: center; background: none; border: 1px solid var(--border); @@ -271,11 +271,11 @@ margin-bottom: 8px; } -.chat-thread__load-more:hover { +.ext-chat-thread__load-more:hover { background: var(--bg-hover); } -.chat-thread__typing { +.ext-chat-thread__typing { padding: 4px 16px 8px; font-size: 12px; color: var(--text-3); @@ -284,7 +284,7 @@ /* ── Message Bubble ─────────────────────── */ -.chat-msg { +.ext-chat-msg { display: flex; align-items: flex-start; gap: 8px; @@ -292,44 +292,44 @@ position: relative; } -.chat-msg--own { +.ext-chat-msg--own { flex-direction: row-reverse; } -.chat-msg--system { +.ext-chat-msg--system { justify-content: center; padding: 2px 0; } -.chat-msg--system span { +.ext-chat-msg--system span { font-size: 12px; color: var(--text-3); font-style: italic; } -.chat-msg--deleted { +.ext-chat-msg--deleted { justify-content: center; padding: 2px 0; } -.chat-msg--deleted em { +.ext-chat-msg--deleted em { font-size: 12px; color: var(--text-3); } -.chat-msg__body { +.ext-chat-msg__body { max-width: 65%; background: var(--bg-raised); border-radius: 12px; padding: 8px 12px; } -.chat-msg--own .chat-msg__body { +.ext-chat-msg--own .ext-chat-msg__body { background: var(--accent); color: var(--text-on-color); } -.chat-msg__name { +.ext-chat-msg__name { font-size: 11px; font-weight: 600; color: var(--text-2); @@ -337,42 +337,42 @@ margin-bottom: 2px; } -.chat-msg__content { +.ext-chat-msg__content { font-size: 14px; line-height: 1.4; white-space: pre-wrap; word-break: break-word; } -.chat-msg__meta { +.ext-chat-msg__meta { display: flex; gap: 6px; align-items: center; margin-top: 2px; } -.chat-msg__time { +.ext-chat-msg__time { font-size: 10px; color: var(--text-3); } -.chat-msg--own .chat-msg__time { +.ext-chat-msg--own .ext-chat-msg__time { color: rgba(255, 255, 255, 0.7); } -.chat-msg__edited { +.ext-chat-msg__edited { font-size: 10px; color: var(--text-3); font-style: italic; } -.chat-msg--own .chat-msg__edited { +.ext-chat-msg--own .ext-chat-msg__edited { color: rgba(255, 255, 255, 0.7); } /* ── Message Actions ────────────────────── */ -.chat-msg__actions { +.ext-chat-msg__actions { display: flex; gap: 2px; position: absolute; @@ -385,12 +385,12 @@ padding: 2px; } -.chat-msg--own .chat-msg__actions { +.ext-chat-msg--own .ext-chat-msg__actions { right: auto; left: 0; } -.chat-msg__action { +.ext-chat-msg__action { background: none; border: none; padding: 4px 6px; @@ -401,24 +401,24 @@ color: var(--text-2); } -.chat-msg__action:hover { +.ext-chat-msg__action:hover { background: var(--bg-hover); } -.chat-msg__action--danger:hover { +.ext-chat-msg__action--danger:hover { background: var(--danger-bg); color: var(--danger); } /* ── Message Edit ───────────────────────── */ -.chat-msg__edit { +.ext-chat-msg__edit { display: flex; flex-direction: column; gap: 6px; } -.chat-msg__edit-input { +.ext-chat-msg__edit-input { width: 100%; min-width: 200px; border: 1px solid var(--border); @@ -431,7 +431,7 @@ color: var(--text); } -.chat-msg__edit-actions { +.ext-chat-msg__edit-actions { display: flex; gap: 6px; justify-content: flex-end; @@ -439,7 +439,7 @@ /* ── Compose Bar ────────────────────────── */ -.chat-compose { +.ext-chat-compose { display: flex; align-items: flex-end; gap: 8px; @@ -448,7 +448,7 @@ background: var(--bg); } -.chat-compose__input { +.ext-chat-compose__input { flex: 1; border: 1px solid var(--border); border-radius: 8px; @@ -462,7 +462,7 @@ color: var(--text); } -.chat-compose__input:focus { +.ext-chat-compose__input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-dim); @@ -470,7 +470,7 @@ /* ── Participant Sidebar ────────────────── */ -.chat-participants { +.ext-chat-participants { width: 240px; min-width: 240px; border-left: 1px solid var(--border); @@ -479,7 +479,7 @@ background: var(--bg-secondary); } -.chat-participants__header { +.ext-chat-participants__header { display: flex; align-items: center; justify-content: space-between; @@ -489,20 +489,20 @@ font-size: 13px; } -.chat-participants__list { +.ext-chat-participants__list { flex: 1; overflow-y: auto; padding: 8px 0; } -.chat-participants__item { +.ext-chat-participants__item { display: flex; align-items: center; gap: 8px; padding: 6px 16px; } -.chat-participants__name { +.ext-chat-participants__name { flex: 1; font-size: 13px; white-space: nowrap; @@ -510,14 +510,14 @@ text-overflow: ellipsis; } -.chat-participants__badge { +.ext-chat-participants__badge { font-size: 10px; color: var(--accent); font-weight: 600; margin-left: 4px; } -.chat-participants__status { +.ext-chat-participants__status { width: 8px; height: 8px; border-radius: 50%; @@ -525,11 +525,11 @@ flex-shrink: 0; } -.chat-participants__status--online { +.ext-chat-participants__status--online { background: var(--success); } -.chat-participants__remove { +.ext-chat-participants__remove { background: none; border: none; color: var(--text-3); @@ -539,25 +539,25 @@ line-height: 1; } -.chat-participants__remove:hover { +.ext-chat-participants__remove:hover { color: var(--danger); } /* ── New Conversation Dialog ────────────── */ -.chat-new { +.ext-chat-new { display: flex; flex-direction: column; gap: 12px; min-width: 320px; } -.chat-new__type { +.ext-chat-new__type { display: flex; gap: 16px; } -.chat-new__type label { +.ext-chat-new__type label { display: flex; align-items: center; gap: 6px; @@ -565,7 +565,7 @@ cursor: pointer; } -.chat-new__title { +.ext-chat-new__title { border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; @@ -575,13 +575,13 @@ color: var(--text); } -.chat-new__selected { +.ext-chat-new__selected { display: flex; flex-wrap: wrap; gap: 6px; } -.chat-new__chip { +.ext-chat-new__chip { display: inline-flex; align-items: center; gap: 4px; @@ -593,7 +593,7 @@ border-radius: 12px; } -.chat-new__chip button { +.ext-chat-new__chip button { background: none; border: none; color: inherit; @@ -607,10 +607,10 @@ /* Allow the autocomplete dropdown to overflow the dialog body. Applies to both New Conversation and Add Participant dialogs. */ -.sw-dialog__body:has(.sw-user-picker) { +[data-ext="chat"] .sw-dialog__body:has(.sw-user-picker) { overflow: visible; } -.sw-dialog:has(.sw-user-picker) { +[data-ext="chat"] .sw-dialog:has(.sw-user-picker) { overflow: visible; } diff --git a/packages/chat/js/main.js b/packages/chat/js/main.js index 5098a0c..1b33689 100644 --- a/packages/chat/js/main.js +++ b/packages/chat/js/main.js @@ -123,60 +123,60 @@ var sMsgs = showSearch ? (searchResults.messages || []) : []; return html` -
-
- Conversations +
+
+ Conversations <${Button} size="sm" onClick=${onNew}>New
- ${showSearch ? html` -
- ${searching && html`
<${Spinner} size="sm" />
`} +
+ ${searching && html`
<${Spinner} size="sm" />
`} ${!searching && sConvs.length === 0 && sMsgs.length === 0 && html` -
No results
`} +
No results
`} ${sConvs.length > 0 && html` -
Conversations
+
Conversations
${sConvs.map(c => html` -
selectFromSearch(c.id)}> -
- ${c.title || 'Untitled'} - ${timeAgo(c.updated_at || c.created_at)} +
selectFromSearch(c.id)}> +
+ ${c.title || 'Untitled'} + ${timeAgo(c.updated_at || c.created_at)}
`)}`} ${sMsgs.length > 0 && html` -
Messages
+
Messages
${sMsgs.map(m => html` -
selectFromSearch(m.conversation_id)}> -
- ${truncate(m.content, 80)} +
selectFromSearch(m.conversation_id)}> +
+ ${truncate(m.content, 80)}
-
- ${timeAgo(m.created_at)} +
+ ${timeAgo(m.created_at)}
`)}`}
` : html` -
+
${conversations.length === 0 && html` -
No conversations yet
`} +
No conversations yet
`} ${conversations.map(c => html`
onSelect(c.id)}> -
- ${c.title || 'Untitled'} - ${timeAgo(c.updated_at || c.created_at)} +
+ ${c.title || 'Untitled'} + ${timeAgo(c.updated_at || c.created_at)}
-
- +
+ ${c.last_message ? truncate(c.last_message.content_type === 'system' ? '\u2022 ' + c.last_message.content @@ -184,7 +184,7 @@ : 'No messages yet'} ${(unread[c.id] || 0) > 0 && html` - ${unread[c.id]}`} + ${unread[c.id]}`}
`)}
@@ -204,14 +204,14 @@ if (msg._deleted) { return html` -
+
This message was deleted
`; } if (msg.content_type === 'system') { return html` -
+
${msg.content}
`; } @@ -239,37 +239,37 @@ } return html` -
setHover(true)} onMouseLeave=${() => setHover(false)}> ${!isOwn && html` <${Avatar} name=${msg._display_name || msg.participant_id} size="sm" />`} -
- ${!isOwn && html`${msg._display_name || msg.participant_id}`} +
+ ${!isOwn && html`${msg._display_name || msg.participant_id}`} ${editing ? html` -
-