diff --git a/CHANGELOG.md b/CHANGELOG.md
index bd144aa..3a4cd67 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,56 @@
All notable changes to Armature are documented here.
+## v0.6.11 — CSS Deduplication
+
+One class per concept. The old `primitives.css` button, toast, popup-menu,
+dropdown, and tabs systems are retired. `sw-primitives.css` is the single
+source of truth for all Preact component styles.
+
+### Changed
+
+- **Buttons**: All 29 files migrated from `.btn-primary` / `.btn-small` /
+ `.btn-danger` / `.btn-ghost` / `.btn-md` / `.btn-sm` to the BEM-style
+ `.sw-btn .sw-btn--{variant} .sw-btn--{size}` system.
+- **Toasts**: Old `.toast-container` / `.toast` CSS deleted. SDK's
+ `sw.toast()` API already used `.sw-toast-*` classes — no JS changes.
+- **Popup menus**: Old `.popup-menu` / `.popup-menu-item` CSS deleted
+ (unused — `.sw-menu` is the active system).
+- **Dropdown collision resolved**: Old `.sw-dropdown` (styled ``)
+ deleted from `primitives.css`. The `sw-primitives.css` custom dropdown
+ component (`.sw-dropdown` with BEM sub-elements) is authoritative.
+- **Tabs collision resolved**: Old `.sw-tabs` / `.sw-tab-btn` deleted from
+ `primitives.css`. The `sw-primitives.css` scrollable tabs component
+ (`.sw-tabs__tab`) is authoritative.
+- **`.settings-section` collision resolved**: Removed duplicate definition
+ from `modals.css`. The `surfaces.css` card-style definition is
+ authoritative; `.settings-content .settings-section` override resets
+ card styling for flat settings layouts.
+
+### Added
+
+- `.sw-btn--success` variant in `sw-primitives.css` (green action button).
+- `--bg-active` CSS variable in both dark/light themes (`variables.css`).
+- `scripts/audit-css-collisions.sh` — finds duplicate class selectors
+ across kernel CSS files and outputs a JSON collision report.
+
+### Fixed
+
+- `packages/sdk-test-runner/css/main.css`: Wrong variable names
+ (`--text3` → `--text-3`, `--text2` → `--text-2`, `--bg1`/`--bg2` →
+ `--bg-raised`).
+- `packages/icd-test-runner/css/main.css`: Replaced inline button fallback
+ styles with kernel `.sw-btn` system.
+
+### Removed
+
+- Old button classes: `.btn-primary`, `.btn-small`, `.btn-danger`,
+ `.btn-full`, `.btn-ghost`, `.btn-subtle`, `.btn-sm`, `.btn-md`.
+- Old toast classes: `.toast-container`, `.toast`, `.toast.error/warning/success`.
+- Old popup menu classes: `.popup-menu`, `.popup-menu-item`, `.popup-menu-*`.
+- Old dropdown and tabs definitions from `primitives.css` that collided
+ with `sw-primitives.css`.
+
## v0.6.10 — Viewport Foundation
Single layout model. Every surface renders inside one containment chain:
diff --git a/ROADMAP-UI.md b/ROADMAP-UI.md
index e973b5a..771fb37 100644
--- a/ROADMAP-UI.md
+++ b/ROADMAP-UI.md
@@ -51,9 +51,9 @@ Shipped. Single layout model: `body → shell → surface`. CSS `zoom` replaces
`transform: scale()`. `100dvh` fallbacks everywhere. Extension surfaces use
`100%` instead of `100vh`. Dead shell layout code deprecated. See CHANGELOG.md.
-### v0.6.11 — CSS Deduplication
+### v0.6.11 — CSS Deduplication ✅
-Kill the old primitive system. One class per concept.
+Shipped. Old primitive system retired. One class per concept. See CHANGELOG.md.
| Step | Description |
|------|-------------|
@@ -118,7 +118,7 @@ v0.6.9 Session Lifetime Config ✅ SHIPPED
↓
v0.6.10 Viewport Foundation ✅ SHIPPED
↓
-v0.6.11 CSS Deduplication ← Can't audit styling until there's one source of truth
+v0.6.11 CSS Deduplication ✅ SHIPPED
↓
v0.6.12 Extension CSS Isolation ← Scoping requires the kernel CSS to be stable first
↓
diff --git a/ROADMAP.md b/ROADMAP.md
index 409aa3e..857403e 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,6 +1,6 @@
# Armature — Roadmap
-## Current: v0.6.10 — Viewport Foundation
+## Current: v0.6.11 — CSS Deduplication
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 04e84f8..45a346d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.6.10
+0.6.11
diff --git a/packages/dashboard/js/main.js b/packages/dashboard/js/main.js
index 2d8f835..120ff0d 100644
--- a/packages/dashboard/js/main.js
+++ b/packages/dashboard/js/main.js
@@ -343,8 +343,8 @@
(ch.description ? 'Description: ' + (typeof esc === 'function' ? esc(ch.description) : ch.description) + '
' : '') +
(ch.created_at ? 'Created: ' + new Date(ch.created_at).toLocaleString() + '
' : '') +
'' +
- 'Close ' +
- 'Open Chat ' +
+ 'Close ' +
+ 'Open Chat ' +
'
';
modal.appendChild(box);
diff --git a/packages/editor/css/main.css b/packages/editor/css/main.css
index f88b9d1..0e7b840 100644
--- a/packages/editor/css/main.css
+++ b/packages/editor/css/main.css
@@ -509,7 +509,7 @@
border-bottom: 1px solid var(--border, #2a2a2e);
}
-.surface-editor .notes-toolbar .btn-small {
+.surface-editor .notes-toolbar .sw-btn--sm {
font-size: 11px;
padding: 3px 6px;
}
diff --git a/packages/git-board/css/main.css b/packages/git-board/css/main.css
index b152773..980b403 100644
--- a/packages/git-board/css/main.css
+++ b/packages/git-board/css/main.css
@@ -447,11 +447,4 @@
color: var(--text-3);
}
-.btn-danger {
- background: var(--danger, #e53e3e);
- color: #fff;
- border: none;
-}
-.btn-danger:hover {
- opacity: 0.9;
-}
+/* git-board danger button override — uses kernel .sw-btn--danger */
diff --git a/packages/git-board/js/main.js b/packages/git-board/js/main.js
index ef4e6fa..2bf52a8 100644
--- a/packages/git-board/js/main.js
+++ b/packages/git-board/js/main.js
@@ -168,7 +168,7 @@
onSelect=${function (o, r) { setOwner(o); setRepo(r); }} />
@@ -196,7 +196,7 @@
Ask your admin to add a Gitea connection in
Admin → Connections , or add a personal one in
Settings → Connections .
-
Open Settings
@@ -449,11 +449,11 @@
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) postComment();
}} />
-
${posting ? 'Posting…' : 'Comment'}
-
${issue.state === 'open' ? 'Close Issue' : 'Reopen Issue'}
diff --git a/packages/hello-dashboard/js/main.js b/packages/hello-dashboard/js/main.js
index 0f88eef..314ff6a 100644
--- a/packages/hello-dashboard/js/main.js
+++ b/packages/hello-dashboard/js/main.js
@@ -34,8 +34,8 @@
'Registered via surface manifest', '') +
'
' +
'' +
- 'Show Toast ' +
- 'Test API ' +
+ 'Show Toast ' +
+ 'Test API ' +
'
' +
'' + esc(JSON.stringify(manifest, null, 2)) + ' ' +
'';
diff --git a/packages/icd-test-runner/css/main.css b/packages/icd-test-runner/css/main.css
index 1a5ada1..ff6b1df 100644
--- a/packages/icd-test-runner/css/main.css
+++ b/packages/icd-test-runner/css/main.css
@@ -26,45 +26,9 @@
background: var(--bg-hover) !important;
}
-/* Buttons — override small padding for our use */
-#extension-mount .btn-primary,
-#extension-mount .btn-secondary,
-#extension-mount .btn-ghost {
- font-size: 13px;
- padding: 7px 16px;
- border-radius: var(--radius, 8px);
- cursor: pointer;
- font-family: var(--font, 'DM Sans', sans-serif);
+/* Buttons — uses kernel sw-btn system, minor overrides for weight */
+#extension-mount .sw-btn {
font-weight: 600;
- transition: background var(--transition, 180ms ease), opacity var(--transition, 180ms ease);
-}
-
-/* Fallback button styles if platform classes not loaded */
-#extension-mount button.btn-primary {
- background: var(--accent, #6c9fff);
- color: #fff;
- border: none;
-}
-#extension-mount button.btn-primary:hover {
- opacity: 0.88;
-}
-
-#extension-mount button.btn-secondary {
- background: var(--accent-dim, rgba(108,159,255,0.12));
- color: var(--accent, #6c9fff);
- border: none;
-}
-#extension-mount button.btn-secondary:hover {
- background: var(--accent-dim, rgba(108,159,255,0.2));
-}
-
-#extension-mount button.btn-ghost {
- background: transparent;
- color: var(--text-2, #9898a8);
- border: 1px solid var(--border, #2e2e35);
-}
-#extension-mount button.btn-ghost:hover {
- background: var(--bg-hover, #2a2a30);
}
/* Status dot animation for running state */
diff --git a/packages/icd-test-runner/js/ui.js b/packages/icd-test-runner/js/ui.js
index 757fd24..2c0ec67 100644
--- a/packages/icd-test-runner/js/ui.js
+++ b/packages/icd-test-runner/js/ui.js
@@ -93,7 +93,7 @@
// Configure button
var cfgBtn = $('button', {
- className: T.providerSetup.configured ? 'btn-ghost' : 'btn-primary',
+ className: T.providerSetup.configured ? 'sw-btn sw-btn--ghost sw-btn--sm' : 'sw-btn sw-btn--primary sw-btn--md',
style: { alignSelf: 'flex-end', whiteSpace: 'nowrap' },
onClick: function () {
if (!T.providerSetup.apiKey) {
@@ -110,7 +110,7 @@
if (T.providerSetup.configured) {
var clearBtn = $('button', {
- className: 'btn-ghost',
+ className: 'sw-btn sw-btn--ghost sw-btn--sm',
style: { alignSelf: 'flex-end', color: 'var(--danger)', whiteSpace: 'nowrap' },
onClick: function () {
T.providerSetup.apiKey = '';
@@ -189,19 +189,19 @@
T.renderControls = function () {
T.el.controls.innerHTML = '';
var btnSmoke = $('button', {
- className: 'btn-primary',
+ className: 'sw-btn sw-btn--primary sw-btn--md',
onClick: function () { T.runSuite('smoke'); }
}, 'Smoke');
var btnCrud = $('button', {
- className: 'btn-secondary',
+ className: 'sw-btn sw-btn--secondary sw-btn--md',
onClick: function () { T.runSuite('crud'); }
}, 'CRUD');
var btnAll = $('button', {
- className: 'btn-primary',
+ className: 'sw-btn sw-btn--primary sw-btn--md',
onClick: function () { T.runSuite('all'); }
}, 'Run All');
var btnClear = $('button', {
- className: 'btn-ghost',
+ className: 'sw-btn sw-btn--ghost sw-btn--sm',
onClick: function () { T.results = []; T.renderProgress(); T.renderSummary(); T.renderDetail(); }
}, 'Clear');
T.el.controls.appendChild(btnSmoke);
@@ -210,14 +210,14 @@
// AuthZ button — only useful if admin (needs fixtures)
if (T.user.role === 'admin') {
var btnAuthz = $('button', {
- className: T.fixtures.ready ? 'btn-secondary' : 'btn-ghost',
+ className: T.fixtures.ready ? 'sw-btn sw-btn--secondary sw-btn--md' : 'sw-btn sw-btn--ghost sw-btn--sm',
style: { opacity: T.fixtures.ready ? '1' : '0.5' },
onClick: function () { T.runSuite('authz'); }
}, 'AuthZ');
T.el.controls.appendChild(btnAuthz);
var btnSecurity = $('button', {
- className: T.fixtures.ready ? 'btn-secondary' : 'btn-ghost',
+ className: T.fixtures.ready ? 'sw-btn sw-btn--secondary sw-btn--md' : 'sw-btn sw-btn--ghost sw-btn--sm',
style: {
opacity: T.fixtures.ready ? '1' : '0.5',
borderColor: T.fixtures.ready ? 'var(--danger)' : undefined,
@@ -230,7 +230,7 @@
// Provider tier button — needs provider setup
var btnProv = $('button', {
- className: T.providerSetup.configured ? 'btn-secondary' : 'btn-ghost',
+ className: T.providerSetup.configured ? 'sw-btn sw-btn--secondary sw-btn--md' : 'sw-btn sw-btn--ghost sw-btn--sm',
style: { opacity: T.providerSetup.configured ? '1' : '0.5' },
onClick: function () { T.runSuite('provider'); }
}, 'Providers');
@@ -238,7 +238,7 @@
// SDK tier button — tests armature-sdk.js contract
var btnSdk = $('button', {
- className: (typeof window.sw !== 'undefined') ? 'btn-secondary' : 'btn-ghost',
+ className: (typeof window.sw !== 'undefined') ? 'sw-btn sw-btn--secondary sw-btn--md' : 'sw-btn sw-btn--ghost sw-btn--sm',
style: { opacity: (typeof window.sw !== 'undefined') ? '1' : '0.5' },
onClick: function () { T.runSuite('sdk'); }
}, 'SDK');
@@ -246,7 +246,7 @@
// Packaging tier button — extension permission lifecycle (v0.29.0)
var btnPkg = $('button', {
- className: 'btn-secondary',
+ className: 'sw-btn sw-btn--secondary sw-btn--md',
onClick: function () { T.runSuite('packaging'); }
}, 'Packaging');
T.el.controls.appendChild(btnPkg);
@@ -259,11 +259,11 @@
// Export buttons
var btnCopy = $('button', {
- className: 'btn-ghost',
+ className: 'sw-btn sw-btn--ghost sw-btn--sm',
onClick: function () { T.exportReport('clipboard'); }
}, 'Copy Report');
var btnDownload = $('button', {
- className: 'btn-ghost',
+ className: 'sw-btn sw-btn--ghost sw-btn--sm',
onClick: function () { T.exportReport('download'); }
}, 'Download');
T.el.controls.appendChild(btnCopy);
@@ -274,12 +274,12 @@
T.el.controls.appendChild($('div', { style: { width: '1px', height: '28px', background: 'var(--border)', flexShrink: '0' } }));
if (!T.fixtures.ready) {
T.el.controls.appendChild($('button', {
- className: 'btn-secondary',
+ className: 'sw-btn sw-btn--secondary sw-btn--md',
onClick: async function () { await T.provisionFixtures(); T.renderControls(); }
}, 'Provision Test Users'));
} else {
T.el.controls.appendChild($('button', {
- className: 'btn-ghost',
+ className: 'sw-btn sw-btn--ghost sw-btn--sm',
style: { color: 'var(--danger)' },
onClick: async function () { await T.teardownFixtures(); T.renderControls(); }
}, 'Tear Down Fixtures'));
diff --git a/packages/sdk-test-runner/css/main.css b/packages/sdk-test-runner/css/main.css
index 6c59fe2..f106a6b 100644
--- a/packages/sdk-test-runner/css/main.css
+++ b/packages/sdk-test-runner/css/main.css
@@ -14,12 +14,12 @@
}
.sdkr-version {
font-size: 12px;
- color: var(--text3, #888);
+ color: var(--text-3, #888);
font-family: 'JetBrains Mono', monospace;
}
.sdkr-desc {
font-size: 13px;
- color: var(--text2, #aaa);
+ color: var(--text-2, #aaa);
margin: 0 0 16px 0;
line-height: 1.5;
}
@@ -30,7 +30,7 @@
padding: 10px 14px;
border: 1px solid var(--border, #333);
border-radius: 6px;
- background: var(--bg1, #1a1a1a);
+ background: var(--bg-raised, #1a1a1a);
}
.sdkr-fixture-row {
display: flex;
@@ -41,14 +41,14 @@
.sdkr-fixture-label {
font-size: 12px;
font-weight: 600;
- color: var(--text2, #aaa);
+ color: var(--text-2, #aaa);
text-transform: uppercase;
white-space: nowrap;
}
.sdkr-select {
padding: 4px 8px;
font-size: 13px;
- background: var(--bg2, #222);
+ background: var(--bg-raised, #222);
color: var(--text, #e0e0e0);
border: 1px solid var(--border, #444);
border-radius: 4px;
@@ -57,7 +57,7 @@
.sdkr-input {
padding: 4px 8px;
font-size: 13px;
- background: var(--bg2, #222);
+ background: var(--bg-raised, #222);
color: var(--text, #e0e0e0);
border: 1px solid var(--border, #444);
border-radius: 4px;
@@ -67,17 +67,17 @@
font-family: 'JetBrains Mono', monospace;
}
.sdkr-input::placeholder {
- color: var(--text3, #666);
+ color: var(--text-3, #666);
font-family: inherit;
}
.sdkr-fixture-status {
font-size: 12px;
- color: var(--text3, #888);
+ color: var(--text-3, #888);
font-family: 'JetBrains Mono', monospace;
}
.sdkr-fixture-note {
font-size: 11px;
- color: var(--text3, #666);
+ color: var(--text-3, #666);
margin-top: 6px;
line-height: 1.4;
}
@@ -98,7 +98,7 @@
.sdkr-filter-label {
font-size: 12px;
font-weight: 600;
- color: var(--text2, #aaa);
+ color: var(--text-2, #aaa);
text-transform: uppercase;
letter-spacing: 0.05em;
}
@@ -119,13 +119,13 @@
padding: 6px 14px;
border-radius: 6px;
border: 1px solid var(--border, #444);
- background: var(--bg2, #2a2a2a);
+ background: var(--bg-raised, #2a2a2a);
color: var(--text, #e0e0e0);
font-size: 13px;
cursor: pointer;
transition: background 0.15s;
}
-.sdkr-btn:hover { background: var(--bg3, #383838); }
+.sdkr-btn:hover { background: var(--bg-hover, #383838); }
.sdkr-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.sdkr-btn-primary {
background: var(--accent, #3b82f6);
@@ -146,7 +146,7 @@
gap: 8px;
margin-bottom: 12px;
padding: 10px 12px;
- background: var(--bg2, #1e1e1e);
+ background: var(--bg-raised, #1e1e1e);
border-radius: 8px;
border: 1px solid var(--border, #333);
}
@@ -173,8 +173,8 @@
.sdkr-table th {
text-align: left;
padding: 8px 10px;
- background: var(--bg2, #1e1e1e);
- color: var(--text2, #aaa);
+ background: var(--bg-raised, #1e1e1e);
+ color: var(--text-2, #aaa);
font-weight: 600;
font-size: 11px;
text-transform: uppercase;
@@ -205,12 +205,12 @@
.sdkr-ms {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
- color: var(--text3, #888);
+ color: var(--text-3, #888);
text-align: right;
}
.sdkr-detail {
font-size: 11px;
- color: var(--text3, #888);
+ color: var(--text-3, #888);
max-width: 400px;
word-break: break-word;
}
diff --git a/packages/workflow-demo/css/main.css b/packages/workflow-demo/css/main.css
index d70c467..90a9cb7 100644
--- a/packages/workflow-demo/css/main.css
+++ b/packages/workflow-demo/css/main.css
@@ -213,17 +213,7 @@
cursor: pointer;
font-weight: 500;
}
-.demo-card .btn-primary {
- background: var(--accent);
- color: var(--text-on-color);
-}
-.demo-card .btn-primary:hover {
- background: var(--accent-hover);
-}
-.demo-card .btn-primary:disabled {
- opacity: 0.5;
- cursor: default;
-}
+/* demo-card buttons — uses kernel .sw-btn--primary */
/* Public link row */
.public-link-row {
diff --git a/packages/workflow-demo/js/main.js b/packages/workflow-demo/js/main.js
index 2a020f6..21cbcd8 100644
--- a/packages/workflow-demo/js/main.js
+++ b/packages/workflow-demo/js/main.js
@@ -216,7 +216,7 @@
}
const tryBtn = document.createElement('button');
- tryBtn.className = 'btn btn-primary';
+ tryBtn.className = 'sw-btn sw-btn--primary sw-btn--md';
tryBtn.textContent = 'Try It';
tryBtn.disabled = !isReady;
if (!isReady) {
diff --git a/scripts/audit-css-collisions.sh b/scripts/audit-css-collisions.sh
new file mode 100644
index 0000000..b0dea79
--- /dev/null
+++ b/scripts/audit-css-collisions.sh
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+# audit-css-collisions.sh — Find duplicate class selectors across kernel CSS files
+# Outputs a JSON collision report to stdout.
+# Usage: bash scripts/audit-css-collisions.sh [css-dir]
+
+set -euo pipefail
+CSS_DIR="${1:-src/css}"
+
+# Extract class selectors from CSS files and track their file + line
+declare -A SEEN # selector -> "file:line file:line ..."
+
+for f in "$CSS_DIR"/*.css; do
+ [ -f "$f" ] || continue
+ lineno=0
+ while IFS= read -r line; do
+ lineno=$((lineno + 1))
+ # Extract class selectors (e.g. .foo-bar, .foo__bar--baz)
+ # Skip lines that are comments, inside @keyframes, or @media queries
+ [[ "$line" =~ ^[[:space:]]*/\* ]] && continue
+ [[ "$line" =~ ^[[:space:]]*@keyframes ]] && continue
+
+ # Match class selectors at the start of a rule
+ while [[ "$line" =~ \.([a-zA-Z_][a-zA-Z0-9_-]*) ]]; do
+ cls=".${BASH_REMATCH[1]}"
+ entry="$(basename "$f"):$lineno"
+ if [ -n "${SEEN[$cls]:-}" ]; then
+ SEEN[$cls]="${SEEN[$cls]} $entry"
+ else
+ SEEN[$cls]="$entry"
+ fi
+ # Remove the matched portion to find more classes on the same line
+ line="${line#*${BASH_REMATCH[0]}}"
+ done
+ done < "$f"
+done
+
+# Build JSON: only emit selectors defined in more than one file
+echo "["
+first=true
+for cls in "${!SEEN[@]}"; do
+ locations="${SEEN[$cls]}"
+ # Get unique files
+ files=()
+ for loc in $locations; do
+ file="${loc%%:*}"
+ found=false
+ for f in "${files[@]:-}"; do
+ [ "$f" = "$file" ] && found=true && break
+ done
+ $found || files+=("$file")
+ done
+
+ # Only report if defined in 2+ different files
+ [ ${#files[@]} -lt 2 ] && continue
+
+ if $first; then first=false; else echo ","; fi
+
+ # Build locations array
+ locs_json=""
+ for loc in $locations; do
+ [ -n "$locs_json" ] && locs_json="$locs_json, "
+ locs_json="$locs_json\"$loc\""
+ done
+
+ files_json=""
+ for f in "${files[@]}"; do
+ [ -n "$files_json" ] && files_json="$files_json, "
+ files_json="$files_json\"$f\""
+ done
+
+ printf ' {"selector": "%s", "files": [%s], "locations": [%s]}' "$cls" "$files_json" "$locs_json"
+done
+echo ""
+echo "]"
diff --git a/server/pages/templates/components/file-upload.html b/server/pages/templates/components/file-upload.html
index 61ab8af..c6b2ce1 100644
--- a/server/pages/templates/components/file-upload.html
+++ b/server/pages/templates/components/file-upload.html
@@ -16,7 +16,7 @@
{{if .Multiple}}multiple{{end}}
{{if .Accept}}accept="{{.Accept}}"{{end}}
style="display:none">
-
Choose files…
diff --git a/src/css/modals.css b/src/css/modals.css
index 381d3bb..8550979 100644
--- a/src/css/modals.css
+++ b/src/css/modals.css
@@ -38,9 +38,6 @@
display: flex; justify-content: flex-end; gap: 0.5rem;
}
-.settings-section { margin-bottom: 1.5rem; padding-bottom: 1rem; border-bottom: 1px solid var(--border); }
-.settings-section:last-child { border-bottom: none; margin-bottom: 0; }
-.settings-section h3 { font-size: 12px; font-weight: 600; color: var(--text-3); margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.06em; }
.avatar-upload-row { display: flex; align-items: center; gap: 14px; margin-bottom: 1rem; }
.avatar-preview {
width: 56px; height: 56px; border-radius: 50%; background: var(--bg-raised);
@@ -196,8 +193,8 @@
.admin-user-actions { display: flex; gap: 6px; flex-shrink: 0; }
.admin-user-actions button { background: none; border: 1px solid var(--border); color: var(--text-2); border-radius: 4px; padding: 4px 10px; font-size: 12px; cursor: pointer; min-width: 68px; text-align: center; }
.admin-user-actions button:hover { border-color: var(--border-light); color: var(--text); }
-.admin-user-actions .btn-danger { color: var(--danger); }
-.admin-user-actions .btn-danger:hover { border-color: var(--danger); }
+.admin-user-actions .sw-btn--danger { color: var(--danger); }
+.admin-user-actions .sw-btn--danger:hover { border-color: var(--danger); }
.inline-select { background: var(--bg-surface); border: 1px solid var(--border); color: var(--text-2); border-radius: 4px; padding: 3px 8px; font-size: 12px; cursor: pointer; }
.admin-model-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--border); font-size: 14px; min-height: 44px; }
diff --git a/src/css/primitives.css b/src/css/primitives.css
index 9bea4d7..c8840f4 100644
--- a/src/css/primitives.css
+++ b/src/css/primitives.css
@@ -1,27 +1,11 @@
/* ── primitives.css ──────────────────────────
- Buttons, forms, toasts, popups, toggles, badges
+ Forms, toggles, badges, mode selector, REPL, SDK primitives.
+ Buttons/toasts/popup-menus migrated to sw-primitives.css (v0.6.11).
──────────────────────────────────────────── */
/* ── Buttons ─────────────────────────────── */
button { font-family: var(--font); cursor: pointer; }
-.btn-primary {
- background: var(--accent); color: var(--text-on-color); border: none;
- padding: 8px 16px; border-radius: var(--radius);
- font-weight: 500; font-size: 13px;
- transition: background var(--transition);
-}
-.btn-primary:hover { background: var(--accent-hover); }
-.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
-.btn-full { width: 100%; }
-.btn-small {
- background: var(--bg-raised); border: 1px solid var(--border);
- color: var(--text-2); padding: 6px 12px; border-radius: var(--radius);
- font-size: 12px; transition: all var(--transition);
-}
-.btn-small:hover { background: var(--bg-hover); color: var(--text); }
-.btn-small.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--text-on-color); }
-.btn-danger { background: var(--danger); color: var(--text-on-color); border: none; border-radius: var(--radius); }
/* ── Forms ────────────────────────────────── */
@@ -61,20 +45,7 @@ select option { background: var(--bg-surface); color: var(--text); }
.checkbox-label { display: flex; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; margin: 0.5rem 0; color: var(--text-2); }
-/* ── Toast ────────────────────────────────── */
-
-.toast-container { position: fixed; bottom: 1rem; left: 50%; transform: translateX(-50%); z-index: 10000; display: flex; flex-direction: column; gap: 0.5rem; }
-.toast {
- background: var(--bg-surface); border: 1px solid var(--border);
- border-radius: var(--radius); padding: 8px 16px; font-size: 13px;
- display: flex; align-items: center; gap: 8px;
- animation: toast-in 0.25s ease; box-shadow: 0 4px 12px rgba(0,0,0,0.3);
-}
-.toast.error { border-color: var(--danger); }
-.toast.warning { border-color: var(--warning); }
-.toast.success { border-color: var(--success); }
-.toast button { background: none; border: none; color: var(--text-3); cursor: pointer; }
-@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } }
+/* Toast system — see sw-primitives.css (.sw-toast) */
/* ── Mode Selector (v0.21.3) ───────────── */
@@ -170,29 +141,7 @@ select option { background: var(--bg-surface); color: var(--text); }
.repl-json-toggle:hover { color: var(--accent); }
-/* ── Popup Menu (shared base) ───────────── */
-
-.popup-menu {
- display: none; position: absolute; z-index: 200;
- background: var(--bg-raised); border: 1px solid var(--border-light);
- border-radius: var(--radius); padding: 4px;
- box-shadow: 0 4px 16px rgba(0,0,0,0.4);
- min-width: 160px;
-}
-.popup-menu.open { display: block; }
-.popup-menu-item {
- display: flex; align-items: center; gap: 10px;
- padding: 8px 12px; border-radius: var(--radius);
- background: none; border: none; color: var(--text-2);
- cursor: pointer; font-size: 13px; width: 100%;
- transition: background var(--transition), color var(--transition);
-}
-.popup-menu-item:hover { background: var(--bg-hover); color: var(--text); }
-.popup-menu-item.disabled { opacity: 0.4; pointer-events: none; }
-.popup-menu-danger:hover { color: var(--danger); }
-.popup-menu-icon { flex-shrink: 0; display: flex; align-items: center; }
-.popup-menu-hint { margin-left: auto; font-size: 11px; color: var(--text-3); }
-.popup-menu-divider { height: 1px; background: var(--border); margin: 4px 8px; }
+/* Popup menu — see sw-primitives.css (.sw-menu) */
/* ── Tools Toggle ────────────────────────── */
@@ -354,11 +303,7 @@ select option { background: var(--bg-surface); color: var(--text); }
.toggle-btn.active { background: var(--bg-raised); color: var(--text); }
.toggle-btn:hover { color: var(--text-2); }
-.btn-ghost { background: transparent; color: var(--text-2); border: 1px solid var(--border); }
-.btn-ghost:hover { background: var(--bg-hover); }
-.btn-subtle { background: var(--accent-dim); color: var(--accent); border: none; }
-.btn-sm { padding: 5px 11px; font-size: 12px; border-radius: 6px; cursor: pointer; font-weight: 500; font-family: inherit; }
-.btn-md { padding: 7px 15px; font-size: 13px; border-radius: var(--radius); cursor: pointer; font-weight: 500; font-family: inherit; }
+/* Button variants — see sw-primitives.css (.sw-btn--*) */
/* ── Badge ───────────────────────────────── */
.badge {
@@ -439,15 +384,7 @@ select option { background: var(--bg-surface); color: var(--text); }
border: none; height: 1px; background: var(--border, #2a2a2e); margin: 4px 0;
}
-/* Dropdown — styled native select */
-.sw-dropdown {
- background: var(--bg-raised, #222); color: var(--text, #eee);
- border: 1px solid var(--border, #2e2e35); border-radius: var(--radius, 6px);
- padding: 6px 10px; font-size: 13px; font-family: inherit;
- cursor: pointer; min-width: 100px;
-}
-.sw-dropdown:hover { border-color: var(--border-light, #444); }
-.sw-dropdown:focus { outline: none; border-color: var(--accent, #b38a4e); }
+/* Dropdown — see sw-primitives.css (.sw-dropdown) */
/* Toolbar — horizontal button row */
.sw-toolbar {
@@ -455,22 +392,7 @@ select option { background: var(--bg-surface); color: var(--text); }
padding: 4px 0;
}
-/* Tabs — tabbed content container */
-.sw-tabs { display: flex; flex-direction: column; height: 100%; }
-.sw-tabs-bar {
- display: flex; gap: 0; border-bottom: 1px solid var(--border, #2e2e35);
- flex-shrink: 0;
-}
-.sw-tab-btn {
- padding: 8px 16px; background: none; border: none; border-bottom: 2px solid transparent;
- color: var(--text-2, #999); cursor: pointer; font-size: 12px; font-weight: 600;
- font-family: inherit; text-transform: uppercase; letter-spacing: 0.5px;
- transition: color 0.15s, border-color 0.15s;
-}
-.sw-tab-btn:hover { color: var(--text, #eee); }
-.sw-tab-btn--active { color: var(--accent, #b38a4e); border-bottom-color: var(--accent, #b38a4e); }
-.sw-tabs-content { flex: 1; min-height: 0; overflow: hidden; }
-.sw-tab-panel { height: 100%; overflow-y: auto; }
+/* Tabs — see sw-primitives.css (.sw-tabs) */
/* ── Responsive: Mobile ───────────────────── */
diff --git a/src/css/sw-primitives.css b/src/css/sw-primitives.css
index 38aa227..50b94ee 100644
--- a/src/css/sw-primitives.css
+++ b/src/css/sw-primitives.css
@@ -23,6 +23,8 @@
.sw-btn--secondary:hover:not(:disabled) { background: var(--bg-hover); border-color: var(--border-light); }
.sw-btn--danger { background: var(--danger); color: var(--text-on-color); }
.sw-btn--danger:hover:not(:disabled) { opacity: 0.9; }
+.sw-btn--success { background: var(--success); color: var(--text-on-color); }
+.sw-btn--success:hover:not(:disabled) { opacity: 0.9; }
.sw-btn--ghost { background: transparent; color: var(--text-2); }
.sw-btn--ghost:hover:not(:disabled) { background: var(--bg-hover); color: var(--text); }
diff --git a/src/css/variables.css b/src/css/variables.css
index ce54130..d6e6049 100644
--- a/src/css/variables.css
+++ b/src/css/variables.css
@@ -15,6 +15,7 @@
--bg-surface: #18181b;
--bg-raised: #222227;
--bg-hover: #2a2a30;
+ --bg-active: #2f2f38;
--bg-elevated: #42424e;
--border: #2e2e35;
--border-elevated: #555560;
@@ -81,6 +82,7 @@
--bg-surface: #ffffff;
--bg-raised: #eff0f3;
--bg-hover: #e5e6eb;
+ --bg-active: #dddee5;
--bg-elevated: #ffffff;
--border: #d5d6dc;
--border-elevated: #c0c0c8;
diff --git a/src/css/workflow.css b/src/css/workflow.css
index d440c7f..a3b5d0f 100644
--- a/src/css/workflow.css
+++ b/src/css/workflow.css
@@ -27,8 +27,8 @@
}
.wf-stage-name { flex: 1; font-size: 13px; font-weight: 500; }
.wf-stage-row .badge { font-size: 10px; }
-.wf-stage-row .btn-small { opacity: 0; transition: opacity 0.15s; }
-.wf-stage-row:hover .btn-small { opacity: 1; }
+.wf-stage-row .sw-btn--sm { opacity: 0; transition: opacity 0.15s; }
+.wf-stage-row:hover .sw-btn--sm { opacity: 1; }
/* ── Queue sidebar ───────────────────── */
@@ -55,16 +55,16 @@
.sb-queue-item.active { background: var(--accent-dim); color: var(--accent); }
.sb-queue-icon { font-size: 14px; }
.sb-queue-label { flex: 1; }
-.sb-queue-item .btn-small {
+.sb-queue-item .sw-btn--sm {
opacity: 0; padding: 2px 6px; font-size: 11px;
transition: opacity 0.15s;
}
-.sb-queue-item:hover .btn-small { opacity: 1; }
+.sb-queue-item:hover .sw-btn--sm { opacity: 1; }
/* Collapsed sidebar — show icon only */
.sidebar.collapsed .sb-queue-item { justify-content: center; padding: 5px 0; margin: 0 6px; gap: 0; }
.sidebar.collapsed .sb-queue-label { display: none; }
-.sidebar.collapsed .sb-queue-item .btn-small { display: none; }
+.sidebar.collapsed .sb-queue-item .sw-btn--sm { display: none; }
.sidebar.collapsed .sb-queue-badge { display: none !important; }
/* Unpin button */
@@ -150,7 +150,7 @@
.wf-bar-status.stale { background: var(--warning-dim); color: var(--warning-light); }
.wf-bar-spacer { flex: 1; }
.wf-bar-actions { display: flex; gap: 6px; }
-.wf-bar-actions .btn-small { font-size: 11px; padding: 3px 10px; }
+.wf-bar-actions .sw-btn--sm { font-size: 11px; padding: 3px 10px; }
/* DnD stage reorder feedback */
.wf-stage-row.dragging { opacity: 0.4; }
diff --git a/src/js/sw/components/debug/index.js b/src/js/sw/components/debug/index.js
index 77dbdba..07fb616 100644
--- a/src/js/sw/components/debug/index.js
+++ b/src/js/sw/components/debug/index.js
@@ -102,13 +102,13 @@ function DebugModal({ engine }) {
diff --git a/src/js/sw/surfaces/admin/audit.js b/src/js/sw/surfaces/admin/audit.js
index 7196921..37bac2d 100644
--- a/src/js/sw/surfaces/admin/audit.js
+++ b/src/js/sw/surfaces/admin/audit.js
@@ -77,9 +77,9 @@ export default function AuditSection() {
- setPage(p => p - 1)}>\u2190 Prev
+ setPage(p => p - 1)}>\u2190 Prev
Page ${page} of ${totalPages} (${total} total)
- = totalPages} onClick=${() => setPage(p => p + 1)}>Next \u2192
+ = totalPages} onClick=${() => setPage(p => p + 1)}>Next \u2192
`
}
diff --git a/src/js/sw/surfaces/admin/backup.js b/src/js/sw/surfaces/admin/backup.js
index 715d747..d572e29 100644
--- a/src/js/sw/surfaces/admin/backup.js
+++ b/src/js/sw/surfaces/admin/backup.js
@@ -118,13 +118,13 @@ export default function BackupSection() {
return html`
- createBackup(false)} disabled=${creating || restoring}>
+ createBackup(false)} disabled=${creating || restoring}>
${creating ? 'Creating\u2026' : 'Download Backup'}
createBackup(true)} disabled=${creating || restoring}>
${creating ? 'Creating\u2026' : 'Save to Server'}
-
+
${restoring ? 'Restoring\u2026' : 'Restore from File'}
@@ -149,8 +149,8 @@ export default function BackupSection() {
${formatBytes(b.size)}
${formatDate(b.created_at)}
- downloadBackup(b.name)}>Download
- deleteBackup(b.name)} style="margin-left:4px;">Delete
+ downloadBackup(b.name)}>Download
+ deleteBackup(b.name)} style="margin-left:4px;">Delete
`)}
diff --git a/src/js/sw/surfaces/admin/broadcast.js b/src/js/sw/surfaces/admin/broadcast.js
index b161fbc..bed977d 100644
--- a/src/js/sw/surfaces/admin/broadcast.js
+++ b/src/js/sw/surfaces/admin/broadcast.js
@@ -38,7 +38,7 @@ export default function BroadcastSection() {
Critical
- ${sending ? 'Sending\u2026' : 'Send Broadcast'}
+ ${sending ? 'Sending\u2026' : 'Send Broadcast'}
`;
}
diff --git a/src/js/sw/surfaces/admin/connections.js b/src/js/sw/surfaces/admin/connections.js
index 1371b0f..e4c48cb 100644
--- a/src/js/sw/surfaces/admin/connections.js
+++ b/src/js/sw/surfaces/admin/connections.js
@@ -114,7 +114,7 @@ export default function ConnectionsSection() {
return html`
- + Add Connection
${!connTypes.length && html`
@@ -165,11 +165,11 @@ export default function ConnectionsSection() {
`)}
-
${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'}
- Cancel
+ Cancel
`}
@@ -196,7 +196,7 @@ export default function ConnectionsSection() {
openEdit(c)}>
\u{270F}\u{FE0F}
- del(c)}>
\u{1F5D1}
diff --git a/src/js/sw/surfaces/admin/groups.js b/src/js/sw/surfaces/admin/groups.js
index 3567a9d..ac88a6c 100644
--- a/src/js/sw/surfaces/admin/groups.js
+++ b/src/js/sw/surfaces/admin/groups.js
@@ -112,11 +112,11 @@ export default function GroupsSection() {
if (detail) return html`
-
setDetail(null)}>\u2190 Back
+
setDetail(null)}>\u2190 Back
${detail.name}
${detail.scope}
-
Delete
+
Delete
@@ -134,7 +134,7 @@ export default function GroupsSection() {
- Save Permissions
+ Save Permissions
@@ -144,7 +144,7 @@ export default function GroupsSection() {
${members.map(m => html`
${m.username || m.user_id}
- removeMember(m.user_id || m.id)}>Remove
+ removeMember(m.user_id || m.id)}>Remove
`)}
@@ -160,12 +160,12 @@ export default function GroupsSection() {
- Add
- setShowAddMember(false)}>Cancel
+ Add
+ setShowAddMember(false)}>Cancel
`
- : html` setShowAddMember(true)}>+ Add Member `
+ : html` setShowAddMember(true)}>+ Add Member `
}
`;
@@ -174,7 +174,7 @@ export default function GroupsSection() {
-
setShowCreate(true)}>+ Add
+
setShowCreate(true)}>+ Add
${showCreate && html`
@@ -187,8 +187,8 @@ export default function GroupsSection() {
- Create
- setShowCreate(false)}>Cancel
+ Create
+ setShowCreate(false)}>Cancel
`}
diff --git a/src/js/sw/surfaces/admin/packages.js b/src/js/sw/surfaces/admin/packages.js
index 8b3e2c7..e6aaf45 100644
--- a/src/js/sw/surfaces/admin/packages.js
+++ b/src/js/sw/surfaces/admin/packages.js
@@ -252,10 +252,10 @@ export default function PackagesSection() {
options=${TYPE_OPTIONS.map(t => ({ label: t.charAt(0).toUpperCase() + t.slice(1), value: t }))}
/>
-
+
${installing ? 'Installing\u2026' : 'Install Package'}
-
+
${registryOpen ? 'Close Registry' : 'Browse Registry'}
@@ -276,28 +276,28 @@ export default function PackagesSection() {
${pkg.is_system && html`system `}
- toggleEnabled(pkg)}
disabled=${CORE_IDS.has(pkg.id) || pkg.status === 'dormant' || pkg.status === 'pending_review'}
title=${pkg.status === 'dormant' ? 'Requires chat' : pkg.status === 'pending_review' ? 'Grant permissions to activate' : ''}>
${pkg.enabled ? 'Disable' : 'Enable'}
${hasManifestSettings(pkg) && html`
- toggleSettings(pkg.id)}>
+ toggleSettings(pkg.id)}>
${expandedId === pkg.id ? 'Close' : 'Settings'}
`}
${hasDeclaredPerms(pkg) && html`
- togglePerms(pkg.id)}>
+ togglePerms(pkg.id)}>
${permsId === pkg.id ? 'Close' : 'Permissions'}
`}
${pkg.source !== 'core' && html`
- updatePkg(pkg)}>Update
+ updatePkg(pkg)}>Update
`}
- exportPkg(pkg.id)}>Export
+ exportPkg(pkg.id)}>Export
${pkg.source !== 'core' && !pkg.is_system && html`
- deletePkg(pkg)}>Delete
+ deletePkg(pkg)}>Delete
`}
@@ -317,7 +317,7 @@ export default function PackagesSection() {
style="flex:1;padding:4px 8px;background:var(--bg-1,var(--bg));color:var(--text);border:1px solid var(--border);border-radius:4px;font-size:12px;" />
`)}
- saveSettings(pkg.id)}>Save
+ saveSettings(pkg.id)}>Save
`
}
@@ -333,7 +333,7 @@ export default function PackagesSection() {
${perms.map(p => html`
${p.permission}
- p.granted ? revokePerm(pkg.id, p.permission) : grantPerm(pkg.id, p.permission)}>
${p.granted ? 'Revoke' : 'Grant'}
@@ -345,7 +345,7 @@ export default function PackagesSection() {
`)}
${perms.some(p => !p.granted) && html`
- grantAllPerms(pkg.id)}>Grant All
+ grantAllPerms(pkg.id)}>Grant All
`}
`
}
@@ -376,7 +376,7 @@ export default function PackagesSection() {
${typeBadge(rp.type)}
- installFromRegistry(rp.download_url)}>Install
diff --git a/src/js/sw/surfaces/admin/settings.js b/src/js/sw/surfaces/admin/settings.js
index a7e96ae..86d8eaf 100644
--- a/src/js/sw/surfaces/admin/settings.js
+++ b/src/js/sw/surfaces/admin/settings.js
@@ -231,11 +231,11 @@ export default function SettingsSection() {
Email
- Send Test Email
+ Send Test Email
- ${saving ? 'Saving\u2026' : 'Save Settings'}
+ ${saving ? 'Saving\u2026' : 'Save Settings'}
`;
diff --git a/src/js/sw/surfaces/admin/storage.js b/src/js/sw/surfaces/admin/storage.js
index 4ed27ab..6d62e25 100644
--- a/src/js/sw/surfaces/admin/storage.js
+++ b/src/js/sw/surfaces/admin/storage.js
@@ -61,7 +61,7 @@ export default function StorageSection() {
Orphaned Blobs
${orphans.count || 0} orphans found
- ${(orphans.count || 0) > 0 && html`
Clean Up `}
+ ${(orphans.count || 0) > 0 && html`
Clean Up `}
`}
diff --git a/src/js/sw/surfaces/admin/teams.js b/src/js/sw/surfaces/admin/teams.js
index 7611924..aeb8611 100644
--- a/src/js/sw/surfaces/admin/teams.js
+++ b/src/js/sw/surfaces/admin/teams.js
@@ -94,10 +94,10 @@ export default function TeamsSection() {
if (detail) return html`
-
setDetail(null)}>\u2190 Back
+
setDetail(null)}>\u2190 Back
${detail.name}
-
deleteTeam(detail.id)}>Delete Team
+
deleteTeam(detail.id)}>Delete Team
${detail.description && html`
${detail.description}
`}
@@ -112,7 +112,7 @@ export default function TeamsSection() {
member
admin
-
removeMember(m.id)}>Remove
+
removeMember(m.id)}>Remove
`)}
@@ -132,12 +132,12 @@ export default function TeamsSection() {
- Add
- setShowAddMember(false)}>Cancel
+ Add
+ setShowAddMember(false)}>Cancel
`
- : html` setShowAddMember(true)}>+ Add Member `
+ : html` setShowAddMember(true)}>+ Add Member `
}
`;
@@ -147,7 +147,7 @@ export default function TeamsSection() {
-
setShowCreate(true)}>+ Add
+
setShowCreate(true)}>+ Add
${showCreate && html`
@@ -157,8 +157,8 @@ export default function TeamsSection() {
Description
- Create
- setShowCreate(false)}>Cancel
+ Create
+ setShowCreate(false)}>Cancel
`}
diff --git a/src/js/sw/surfaces/admin/users.js b/src/js/sw/surfaces/admin/users.js
index c0e488c..48e03b8 100644
--- a/src/js/sw/surfaces/admin/users.js
+++ b/src/js/sw/surfaces/admin/users.js
@@ -103,7 +103,7 @@ export default function UsersSection() {
setSearch(e.target.value)} autocomplete="off" />
- setShowCreate(true)}>+ Add
+ setShowCreate(true)}>+ Add
${showCreate && html`
@@ -117,8 +117,8 @@ export default function UsersSection() {
- Create
- setShowCreate(false)}>Cancel
+ Create
+ setShowCreate(false)}>Cancel
`}
@@ -134,14 +134,14 @@ export default function UsersSection() {
${u.status === 'pending' && html`
- approveUser(u.id)}>Approve
+ approveUser(u.id)}>Approve
`}
${u.is_active
- ? html` toggleActive(u.id, false)}>Disable `
- : html` toggleActive(u.id, true)}>Enable `
+ ? html` toggleActive(u.id, false)}>Disable `
+ : html` toggleActive(u.id, true)}>Enable `
}
- resetPassword(u.id, u.username)}>Reset PW
- deleteUser(u.id, u.username)}>Delete
+ resetPassword(u.id, u.username)}>Reset PW
+ deleteUser(u.id, u.username)}>Delete
`)}
diff --git a/src/js/sw/surfaces/admin/workflows.js b/src/js/sw/surfaces/admin/workflows.js
index 6c32453..f924750 100644
--- a/src/js/sw/surfaces/admin/workflows.js
+++ b/src/js/sw/surfaces/admin/workflows.js
@@ -80,10 +80,10 @@ export default function WorkflowsSection() {
if (editing) return html`
`;
@@ -122,7 +122,7 @@ export default function WorkflowsSection() {
-
setShowCreate(true)}>+ Add
+
setShowCreate(true)}>+ Add
${showCreate && html`
@@ -138,8 +138,8 @@ export default function WorkflowsSection() {
Description
- Create
- setShowCreate(false)}>Cancel
+ Create
+ setShowCreate(false)}>Cancel
`}
diff --git a/src/js/sw/surfaces/settings/appearance.js b/src/js/sw/surfaces/settings/appearance.js
index 545a9c4..e65bbc6 100644
--- a/src/js/sw/surfaces/settings/appearance.js
+++ b/src/js/sw/surfaces/settings/appearance.js
@@ -73,6 +73,6 @@ export function AppearanceSection() {
- Save
+ Save
`;
}
diff --git a/src/js/sw/surfaces/settings/connections.js b/src/js/sw/surfaces/settings/connections.js
index 4ab1c83..dae29fe 100644
--- a/src/js/sw/surfaces/settings/connections.js
+++ b/src/js/sw/surfaces/settings/connections.js
@@ -114,7 +114,7 @@ export function ConnectionsSection() {
return html`
- + Add Connection
${!connTypes.length && html`
@@ -169,11 +169,11 @@ export function ConnectionsSection() {
`)}
-
${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'}
- Cancel
+ Cancel
`}
@@ -207,7 +207,7 @@ export function ConnectionsSection() {
openEdit(c)}>
\u{270F}\u{FE0F}
- del(c)}>
\u{1F5D1}
diff --git a/src/js/sw/surfaces/settings/general.js b/src/js/sw/surfaces/settings/general.js
index 688ed6b..ae905f9 100644
--- a/src/js/sw/surfaces/settings/general.js
+++ b/src/js/sw/surfaces/settings/general.js
@@ -54,7 +54,7 @@ export function GeneralSection() {
If your chosen surface is uninstalled, the global default is used.
-
${saving ? 'Saving\u2026' : 'Save'}
diff --git a/src/js/sw/surfaces/settings/notifications.js b/src/js/sw/surfaces/settings/notifications.js
index 477bda8..8eb920d 100644
--- a/src/js/sw/surfaces/settings/notifications.js
+++ b/src/js/sw/surfaces/settings/notifications.js
@@ -81,7 +81,7 @@ export default function NotificationsSection() {
onChange=${() => togglePref(type, enabled)} />
- resetPref(type)}
title="Reset to default">
diff --git a/src/js/sw/surfaces/settings/profile.js b/src/js/sw/surfaces/settings/profile.js
index d44436d..f29f0ec 100644
--- a/src/js/sw/surfaces/settings/profile.js
+++ b/src/js/sw/surfaces/settings/profile.js
@@ -124,12 +124,12 @@ export function ProfileSection() {
}
- fileRef.current?.click()}>
Upload
${avatarUrl && html`
-
+
Remove
`}
@@ -148,7 +148,7 @@ export function ProfileSection() {
Email
-
${savingProfile ? 'Saving\u2026' : 'Save Profile'}
@@ -171,7 +171,7 @@ export function ProfileSection() {
setConfirmPw(e.target.value)} />
-
${savingPw ? 'Updating\u2026' : 'Update Password'}
diff --git a/src/js/sw/surfaces/team-admin/activity.js b/src/js/sw/surfaces/team-admin/activity.js
index 32f735a..dfe0d9e 100644
--- a/src/js/sw/surfaces/team-admin/activity.js
+++ b/src/js/sw/surfaces/team-admin/activity.js
@@ -77,9 +77,9 @@ export default function ActivitySection({ teamId }) {
- setPage(p => p - 1)}>\u2190 Prev
+ setPage(p => p - 1)}>\u2190 Prev
Page ${page} of ${totalPages} (${total} total)
- = totalPages} onClick=${() => setPage(p => p + 1)}>Next \u2192
+ = totalPages} onClick=${() => setPage(p => p + 1)}>Next \u2192
`
}
diff --git a/src/js/sw/surfaces/team-admin/connections.js b/src/js/sw/surfaces/team-admin/connections.js
index 83d6a24..5bcc0ab 100644
--- a/src/js/sw/surfaces/team-admin/connections.js
+++ b/src/js/sw/surfaces/team-admin/connections.js
@@ -114,7 +114,7 @@ export default function ConnectionsSection({ teamId }) {
return html`
- + Add Connection
@@ -157,11 +157,11 @@ export default function ConnectionsSection({ teamId }) {
`)}
-
${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'}
- Cancel
+ Cancel
`}
@@ -187,7 +187,7 @@ export default function ConnectionsSection({ teamId }) {
openEdit(c)}>
\u{270F}\u{FE0F}
- del(c)}>
\u{1F5D1}
diff --git a/src/js/sw/surfaces/team-admin/index.js b/src/js/sw/surfaces/team-admin/index.js
index eaf224a..c101fa8 100644
--- a/src/js/sw/surfaces/team-admin/index.js
+++ b/src/js/sw/surfaces/team-admin/index.js
@@ -185,7 +185,7 @@ function TeamAdminSurface() {
${teamName}
${adminTeams.length > 1 && html`
- setSelectedTeam(null)}>Switch Team
`}
diff --git a/src/js/sw/surfaces/team-admin/members.js b/src/js/sw/surfaces/team-admin/members.js
index ccadd87..e334d67 100644
--- a/src/js/sw/surfaces/team-admin/members.js
+++ b/src/js/sw/surfaces/team-admin/members.js
@@ -79,7 +79,7 @@ export default function MembersSection({ teamId }) {
-
+ Add Member
+
+ Add Member
${showAdd && html`
@@ -98,8 +98,8 @@ export default function MembersSection({ teamId }) {
- Add
- setShowAdd(false)}>Cancel
+ Add
+ setShowAdd(false)}>Cancel
`}
@@ -113,7 +113,7 @@ export default function MembersSection({ teamId }) {
onChange=${e => updateRole(m.id || m.user_id, e.target.value)}>
${roles.map(r => html`${r} `)}
- removeMember(m.id || m.user_id)}>Remove
+ removeMember(m.id || m.user_id)}>Remove
`)}
@@ -121,7 +121,7 @@ export default function MembersSection({ teamId }) {
Team Roles
- setShowRoles(!showRoles)}>
+ setShowRoles(!showRoles)}>
${showRoles ? 'Hide' : 'Manage'}
@@ -132,7 +132,7 @@ export default function MembersSection({ teamId }) {
setNewRole(e.target.value)}
placeholder="New role name" style="width:150px;" />
-
{
+ {
if (!newRole.trim()) return;
const updated = [...new Set([...roles, newRole.trim()])];
try {
@@ -147,7 +147,7 @@ export default function MembersSection({ teamId }) {
${roles.filter(r => r !== 'admin' && r !== 'member').map(r => html`
${r}
-
{
+ {
const updated = roles.filter(x => x !== r);
try {
await sw.api.put('/api/v1/teams/' + teamId + '/roles', { roles: updated });
diff --git a/src/js/sw/surfaces/team-admin/settings.js b/src/js/sw/surfaces/team-admin/settings.js
index be9a1a5..e1714bc 100644
--- a/src/js/sw/surfaces/team-admin/settings.js
+++ b/src/js/sw/surfaces/team-admin/settings.js
@@ -65,7 +65,7 @@ export default function SettingsSection({ teamId }) {
`}
- ${saving ? 'Saving\u2026' : 'Save Settings'}
+ ${saving ? 'Saving\u2026' : 'Save Settings'}
`;
diff --git a/src/js/sw/surfaces/team-admin/workflows.js b/src/js/sw/surfaces/team-admin/workflows.js
index 89e2023..5ef739f 100644
--- a/src/js/sw/surfaces/team-admin/workflows.js
+++ b/src/js/sw/surfaces/team-admin/workflows.js
@@ -46,7 +46,7 @@ export default function WorkflowsSection({ teamId }) {
${TABS.map(t => html`
setTab(t)}>${t}
`)}
@@ -123,8 +123,8 @@ function WorkflowsTab({ teamId }) {
-
Adopt Global
-
setShowCreate(true)}>+ Add
+
Adopt Global
+
setShowCreate(true)}>+ Add
${showAdopt && html`
@@ -132,7 +132,7 @@ function WorkflowsTab({ teamId }) {
Adopt a Global Workflow
-
setShowAdopt(false)}>Cancel
+
setShowAdopt(false)}>Cancel
${available.length === 0 && html`
No global workflows available
`}
@@ -143,7 +143,7 @@ function WorkflowsTab({ teamId }) {
/${w.slug}
${w.entry_mode}
-
adoptWorkflow(w.id)}>Adopt
+
adoptWorkflow(w.id)}>Adopt
`)}
@@ -163,8 +163,8 @@ function WorkflowsTab({ teamId }) {
Description
- Create
- setShowCreate(false)}>Cancel
+ Create
+ setShowCreate(false)}>Cancel
`}
@@ -266,11 +266,11 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
return html`