Feat v0.6.11 css dedup (#46)
All checks were successful
CI/CD / detect-changes (push) Successful in 3s
CI/CD / test-frontend (push) Successful in 5s
CI/CD / test-go-pg (push) Successful in 2m44s
CI/CD / test-sqlite (push) Successful in 2m54s
CI/CD / build-and-deploy (push) Successful in 1m17s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #46.
This commit is contained in:
2026-04-01 11:18:28 +00:00
committed by xcaliber
parent ca3f845c34
commit 786bc92768
44 changed files with 311 additions and 317 deletions

View File

@@ -2,6 +2,56 @@
All notable changes to Armature are documented here. 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 `<select>`)
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 ## v0.6.10 — Viewport Foundation
Single layout model. Every surface renders inside one containment chain: Single layout model. Every surface renders inside one containment chain:

View File

@@ -51,9 +51,9 @@ Shipped. Single layout model: `body → shell → surface`. CSS `zoom` replaces
`transform: scale()`. `100dvh` fallbacks everywhere. Extension surfaces use `transform: scale()`. `100dvh` fallbacks everywhere. Extension surfaces use
`100%` instead of `100vh`. Dead shell layout code deprecated. See CHANGELOG.md. `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 | | Step | Description |
|------|-------------| |------|-------------|
@@ -118,7 +118,7 @@ v0.6.9 Session Lifetime Config ✅ SHIPPED
v0.6.10 Viewport Foundation ✅ 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 v0.6.12 Extension CSS Isolation ← Scoping requires the kernel CSS to be stable first

View File

@@ -1,6 +1,6 @@
# Armature — Roadmap # Armature — Roadmap
## Current: v0.6.10Viewport Foundation ## Current: v0.6.11CSS Deduplication
Self-hosted extensible platform. Auth, identity, packages, Starlark sandbox, Self-hosted extensible platform. Auth, identity, packages, Starlark sandbox,
storage, realtime, and ops are kernel primitives. Everything else is an extension. storage, realtime, and ops are kernel primitives. Everything else is an extension.

View File

@@ -1 +1 @@
0.6.10 0.6.11

View File

@@ -343,8 +343,8 @@
(ch.description ? '<div style="font-size:12px;color:var(--text-2);margin-bottom:8px;"><strong>Description:</strong> ' + (typeof esc === 'function' ? esc(ch.description) : ch.description) + '</div>' : '') + (ch.description ? '<div style="font-size:12px;color:var(--text-2);margin-bottom:8px;"><strong>Description:</strong> ' + (typeof esc === 'function' ? esc(ch.description) : ch.description) + '</div>' : '') +
(ch.created_at ? '<div style="font-size:12px;color:var(--text-3);margin-bottom:16px;">Created: ' + new Date(ch.created_at).toLocaleString() + '</div>' : '') + (ch.created_at ? '<div style="font-size:12px;color:var(--text-3);margin-bottom:16px;">Created: ' + new Date(ch.created_at).toLocaleString() + '</div>' : '') +
'<div style="display:flex;justify-content:flex-end;gap:8px;">' + '<div style="display:flex;justify-content:flex-end;gap:8px;">' +
'<button class="btn-small" id="dashDetailClose">Close</button>' + '<button class="sw-btn sw-btn--secondary sw-btn--sm" id="dashDetailClose">Close</button>' +
'<button class="btn-small btn-primary" id="dashDetailOpen">Open Chat</button>' + '<button class="sw-btn sw-btn--primary sw-btn--sm" id="dashDetailOpen">Open Chat</button>' +
'</div>'; '</div>';
modal.appendChild(box); modal.appendChild(box);

View File

@@ -509,7 +509,7 @@
border-bottom: 1px solid var(--border, #2a2a2e); border-bottom: 1px solid var(--border, #2a2a2e);
} }
.surface-editor .notes-toolbar .btn-small { .surface-editor .notes-toolbar .sw-btn--sm {
font-size: 11px; font-size: 11px;
padding: 3px 6px; padding: 3px 6px;
} }

View File

@@ -447,11 +447,4 @@
color: var(--text-3); color: var(--text-3);
} }
.btn-danger { /* git-board danger button override — uses kernel .sw-btn--danger */
background: var(--danger, #e53e3e);
color: #fff;
border: none;
}
.btn-danger:hover {
opacity: 0.9;
}

View File

@@ -168,7 +168,7 @@
onSelect=${function (o, r) { setOwner(o); setRepo(r); }} /> onSelect=${function (o, r) { setOwner(o); setRepo(r); }} />
</div> </div>
<div class="gb-header__right"> <div class="gb-header__right">
<button class="btn-small" onClick=${loadBoard} disabled=${loading}> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${loadBoard} disabled=${loading}>
${loading ? '↻' : 'Refresh'} ${loading ? '↻' : 'Refresh'}
</button> </button>
</div> </div>
@@ -196,7 +196,7 @@
<p>Ask your admin to add a <strong>Gitea</strong> connection in <p>Ask your admin to add a <strong>Gitea</strong> connection in
<strong>Admin → Connections</strong>, or add a personal one in <strong>Admin → Connections</strong>, or add a personal one in
<strong>Settings → Connections</strong>.</p> <strong>Settings → Connections</strong>.</p>
<button class="btn-small btn-primary" <button class="sw-btn sw-btn--primary sw-btn--sm"
onClick=${function () { window.location.href = base + '/settings'; }}> onClick=${function () { window.location.href = base + '/settings'; }}>
Open Settings Open Settings
</button> </button>
@@ -449,11 +449,11 @@
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) postComment(); if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) postComment();
}} /> }} />
<div class="gb-modal__actions"> <div class="gb-modal__actions">
<button class="btn-small btn-primary" disabled=${posting || !comment.trim()} <button class="sw-btn sw-btn--primary sw-btn--sm" disabled=${posting || !comment.trim()}
onClick=${postComment}> onClick=${postComment}>
${posting ? 'Posting…' : 'Comment'} ${posting ? 'Posting…' : 'Comment'}
</button> </button>
<button class="btn-small ${issue.state === 'open' ? 'btn-danger' : 'btn-secondary'}" <button class="sw-btn sw-btn--secondary sw-btn--sm ${issue.state === 'open' ? 'sw-btn sw-btn--danger sw-btn--md' : 'sw-btn sw-btn--secondary sw-btn--md'}"
onClick=${toggleState}> onClick=${toggleState}>
${issue.state === 'open' ? 'Close Issue' : 'Reopen Issue'} ${issue.state === 'open' ? 'Close Issue' : 'Reopen Issue'}
</button> </button>

View File

@@ -34,8 +34,8 @@
'Registered via surface manifest', '') + 'Registered via surface manifest', '') +
'</div>' + '</div>' +
'<div class="hello-actions">' + '<div class="hello-actions">' +
'<button class="btn-primary" id="helloToast">Show Toast</button>' + '<button class="sw-btn sw-btn--primary sw-btn--md" id="helloToast">Show Toast</button>' +
'<button class="btn-secondary" id="helloApi">Test API</button>' + '<button class="sw-btn sw-btn--secondary sw-btn--md" id="helloApi">Test API</button>' +
'</div>' + '</div>' +
'<pre class="hello-manifest">' + esc(JSON.stringify(manifest, null, 2)) + '</pre>' + '<pre class="hello-manifest">' + esc(JSON.stringify(manifest, null, 2)) + '</pre>' +
'</div>'; '</div>';

View File

@@ -26,45 +26,9 @@
background: var(--bg-hover) !important; background: var(--bg-hover) !important;
} }
/* Buttons — override small padding for our use */ /* Buttons — uses kernel sw-btn system, minor overrides for weight */
#extension-mount .btn-primary, #extension-mount .sw-btn {
#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);
font-weight: 600; 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 */ /* Status dot animation for running state */

View File

@@ -93,7 +93,7 @@
// Configure button // Configure button
var cfgBtn = $('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' }, style: { alignSelf: 'flex-end', whiteSpace: 'nowrap' },
onClick: function () { onClick: function () {
if (!T.providerSetup.apiKey) { if (!T.providerSetup.apiKey) {
@@ -110,7 +110,7 @@
if (T.providerSetup.configured) { if (T.providerSetup.configured) {
var clearBtn = $('button', { var clearBtn = $('button', {
className: 'btn-ghost', className: 'sw-btn sw-btn--ghost sw-btn--sm',
style: { alignSelf: 'flex-end', color: 'var(--danger)', whiteSpace: 'nowrap' }, style: { alignSelf: 'flex-end', color: 'var(--danger)', whiteSpace: 'nowrap' },
onClick: function () { onClick: function () {
T.providerSetup.apiKey = ''; T.providerSetup.apiKey = '';
@@ -189,19 +189,19 @@
T.renderControls = function () { T.renderControls = function () {
T.el.controls.innerHTML = ''; T.el.controls.innerHTML = '';
var btnSmoke = $('button', { var btnSmoke = $('button', {
className: 'btn-primary', className: 'sw-btn sw-btn--primary sw-btn--md',
onClick: function () { T.runSuite('smoke'); } onClick: function () { T.runSuite('smoke'); }
}, 'Smoke'); }, 'Smoke');
var btnCrud = $('button', { var btnCrud = $('button', {
className: 'btn-secondary', className: 'sw-btn sw-btn--secondary sw-btn--md',
onClick: function () { T.runSuite('crud'); } onClick: function () { T.runSuite('crud'); }
}, 'CRUD'); }, 'CRUD');
var btnAll = $('button', { var btnAll = $('button', {
className: 'btn-primary', className: 'sw-btn sw-btn--primary sw-btn--md',
onClick: function () { T.runSuite('all'); } onClick: function () { T.runSuite('all'); }
}, 'Run All'); }, 'Run All');
var btnClear = $('button', { 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(); } onClick: function () { T.results = []; T.renderProgress(); T.renderSummary(); T.renderDetail(); }
}, 'Clear'); }, 'Clear');
T.el.controls.appendChild(btnSmoke); T.el.controls.appendChild(btnSmoke);
@@ -210,14 +210,14 @@
// AuthZ button — only useful if admin (needs fixtures) // AuthZ button — only useful if admin (needs fixtures)
if (T.user.role === 'admin') { if (T.user.role === 'admin') {
var btnAuthz = $('button', { 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' }, style: { opacity: T.fixtures.ready ? '1' : '0.5' },
onClick: function () { T.runSuite('authz'); } onClick: function () { T.runSuite('authz'); }
}, 'AuthZ'); }, 'AuthZ');
T.el.controls.appendChild(btnAuthz); T.el.controls.appendChild(btnAuthz);
var btnSecurity = $('button', { 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: { style: {
opacity: T.fixtures.ready ? '1' : '0.5', opacity: T.fixtures.ready ? '1' : '0.5',
borderColor: T.fixtures.ready ? 'var(--danger)' : undefined, borderColor: T.fixtures.ready ? 'var(--danger)' : undefined,
@@ -230,7 +230,7 @@
// Provider tier button — needs provider setup // Provider tier button — needs provider setup
var btnProv = $('button', { 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' }, style: { opacity: T.providerSetup.configured ? '1' : '0.5' },
onClick: function () { T.runSuite('provider'); } onClick: function () { T.runSuite('provider'); }
}, 'Providers'); }, 'Providers');
@@ -238,7 +238,7 @@
// SDK tier button — tests armature-sdk.js contract // SDK tier button — tests armature-sdk.js contract
var btnSdk = $('button', { 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' }, style: { opacity: (typeof window.sw !== 'undefined') ? '1' : '0.5' },
onClick: function () { T.runSuite('sdk'); } onClick: function () { T.runSuite('sdk'); }
}, 'SDK'); }, 'SDK');
@@ -246,7 +246,7 @@
// Packaging tier button — extension permission lifecycle (v0.29.0) // Packaging tier button — extension permission lifecycle (v0.29.0)
var btnPkg = $('button', { var btnPkg = $('button', {
className: 'btn-secondary', className: 'sw-btn sw-btn--secondary sw-btn--md',
onClick: function () { T.runSuite('packaging'); } onClick: function () { T.runSuite('packaging'); }
}, 'Packaging'); }, 'Packaging');
T.el.controls.appendChild(btnPkg); T.el.controls.appendChild(btnPkg);
@@ -259,11 +259,11 @@
// Export buttons // Export buttons
var btnCopy = $('button', { var btnCopy = $('button', {
className: 'btn-ghost', className: 'sw-btn sw-btn--ghost sw-btn--sm',
onClick: function () { T.exportReport('clipboard'); } onClick: function () { T.exportReport('clipboard'); }
}, 'Copy Report'); }, 'Copy Report');
var btnDownload = $('button', { var btnDownload = $('button', {
className: 'btn-ghost', className: 'sw-btn sw-btn--ghost sw-btn--sm',
onClick: function () { T.exportReport('download'); } onClick: function () { T.exportReport('download'); }
}, 'Download'); }, 'Download');
T.el.controls.appendChild(btnCopy); T.el.controls.appendChild(btnCopy);
@@ -274,12 +274,12 @@
T.el.controls.appendChild($('div', { style: { width: '1px', height: '28px', background: 'var(--border)', flexShrink: '0' } })); T.el.controls.appendChild($('div', { style: { width: '1px', height: '28px', background: 'var(--border)', flexShrink: '0' } }));
if (!T.fixtures.ready) { if (!T.fixtures.ready) {
T.el.controls.appendChild($('button', { 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(); } onClick: async function () { await T.provisionFixtures(); T.renderControls(); }
}, 'Provision Test Users')); }, 'Provision Test Users'));
} else { } else {
T.el.controls.appendChild($('button', { T.el.controls.appendChild($('button', {
className: 'btn-ghost', className: 'sw-btn sw-btn--ghost sw-btn--sm',
style: { color: 'var(--danger)' }, style: { color: 'var(--danger)' },
onClick: async function () { await T.teardownFixtures(); T.renderControls(); } onClick: async function () { await T.teardownFixtures(); T.renderControls(); }
}, 'Tear Down Fixtures')); }, 'Tear Down Fixtures'));

View File

@@ -14,12 +14,12 @@
} }
.sdkr-version { .sdkr-version {
font-size: 12px; font-size: 12px;
color: var(--text3, #888); color: var(--text-3, #888);
font-family: 'JetBrains Mono', monospace; font-family: 'JetBrains Mono', monospace;
} }
.sdkr-desc { .sdkr-desc {
font-size: 13px; font-size: 13px;
color: var(--text2, #aaa); color: var(--text-2, #aaa);
margin: 0 0 16px 0; margin: 0 0 16px 0;
line-height: 1.5; line-height: 1.5;
} }
@@ -30,7 +30,7 @@
padding: 10px 14px; padding: 10px 14px;
border: 1px solid var(--border, #333); border: 1px solid var(--border, #333);
border-radius: 6px; border-radius: 6px;
background: var(--bg1, #1a1a1a); background: var(--bg-raised, #1a1a1a);
} }
.sdkr-fixture-row { .sdkr-fixture-row {
display: flex; display: flex;
@@ -41,14 +41,14 @@
.sdkr-fixture-label { .sdkr-fixture-label {
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
color: var(--text2, #aaa); color: var(--text-2, #aaa);
text-transform: uppercase; text-transform: uppercase;
white-space: nowrap; white-space: nowrap;
} }
.sdkr-select { .sdkr-select {
padding: 4px 8px; padding: 4px 8px;
font-size: 13px; font-size: 13px;
background: var(--bg2, #222); background: var(--bg-raised, #222);
color: var(--text, #e0e0e0); color: var(--text, #e0e0e0);
border: 1px solid var(--border, #444); border: 1px solid var(--border, #444);
border-radius: 4px; border-radius: 4px;
@@ -57,7 +57,7 @@
.sdkr-input { .sdkr-input {
padding: 4px 8px; padding: 4px 8px;
font-size: 13px; font-size: 13px;
background: var(--bg2, #222); background: var(--bg-raised, #222);
color: var(--text, #e0e0e0); color: var(--text, #e0e0e0);
border: 1px solid var(--border, #444); border: 1px solid var(--border, #444);
border-radius: 4px; border-radius: 4px;
@@ -67,17 +67,17 @@
font-family: 'JetBrains Mono', monospace; font-family: 'JetBrains Mono', monospace;
} }
.sdkr-input::placeholder { .sdkr-input::placeholder {
color: var(--text3, #666); color: var(--text-3, #666);
font-family: inherit; font-family: inherit;
} }
.sdkr-fixture-status { .sdkr-fixture-status {
font-size: 12px; font-size: 12px;
color: var(--text3, #888); color: var(--text-3, #888);
font-family: 'JetBrains Mono', monospace; font-family: 'JetBrains Mono', monospace;
} }
.sdkr-fixture-note { .sdkr-fixture-note {
font-size: 11px; font-size: 11px;
color: var(--text3, #666); color: var(--text-3, #666);
margin-top: 6px; margin-top: 6px;
line-height: 1.4; line-height: 1.4;
} }
@@ -98,7 +98,7 @@
.sdkr-filter-label { .sdkr-filter-label {
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
color: var(--text2, #aaa); color: var(--text-2, #aaa);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
} }
@@ -119,13 +119,13 @@
padding: 6px 14px; padding: 6px 14px;
border-radius: 6px; border-radius: 6px;
border: 1px solid var(--border, #444); border: 1px solid var(--border, #444);
background: var(--bg2, #2a2a2a); background: var(--bg-raised, #2a2a2a);
color: var(--text, #e0e0e0); color: var(--text, #e0e0e0);
font-size: 13px; font-size: 13px;
cursor: pointer; cursor: pointer;
transition: background 0.15s; 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:disabled { opacity: 0.5; cursor: not-allowed; }
.sdkr-btn-primary { .sdkr-btn-primary {
background: var(--accent, #3b82f6); background: var(--accent, #3b82f6);
@@ -146,7 +146,7 @@
gap: 8px; gap: 8px;
margin-bottom: 12px; margin-bottom: 12px;
padding: 10px 12px; padding: 10px 12px;
background: var(--bg2, #1e1e1e); background: var(--bg-raised, #1e1e1e);
border-radius: 8px; border-radius: 8px;
border: 1px solid var(--border, #333); border: 1px solid var(--border, #333);
} }
@@ -173,8 +173,8 @@
.sdkr-table th { .sdkr-table th {
text-align: left; text-align: left;
padding: 8px 10px; padding: 8px 10px;
background: var(--bg2, #1e1e1e); background: var(--bg-raised, #1e1e1e);
color: var(--text2, #aaa); color: var(--text-2, #aaa);
font-weight: 600; font-weight: 600;
font-size: 11px; font-size: 11px;
text-transform: uppercase; text-transform: uppercase;
@@ -205,12 +205,12 @@
.sdkr-ms { .sdkr-ms {
font-family: 'JetBrains Mono', monospace; font-family: 'JetBrains Mono', monospace;
font-size: 11px; font-size: 11px;
color: var(--text3, #888); color: var(--text-3, #888);
text-align: right; text-align: right;
} }
.sdkr-detail { .sdkr-detail {
font-size: 11px; font-size: 11px;
color: var(--text3, #888); color: var(--text-3, #888);
max-width: 400px; max-width: 400px;
word-break: break-word; word-break: break-word;
} }

View File

@@ -213,17 +213,7 @@
cursor: pointer; cursor: pointer;
font-weight: 500; font-weight: 500;
} }
.demo-card .btn-primary { /* demo-card buttons — uses kernel .sw-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;
}
/* Public link row */ /* Public link row */
.public-link-row { .public-link-row {

View File

@@ -216,7 +216,7 @@
} }
const tryBtn = document.createElement('button'); 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.textContent = 'Try It';
tryBtn.disabled = !isReady; tryBtn.disabled = !isReady;
if (!isReady) { if (!isReady) {

View File

@@ -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 "]"

View File

@@ -16,7 +16,7 @@
{{if .Multiple}}multiple{{end}} {{if .Multiple}}multiple{{end}}
{{if .Accept}}accept="{{.Accept}}"{{end}} {{if .Accept}}accept="{{.Accept}}"{{end}}
style="display:none"> style="display:none">
<button type="button" class="btn-small btn-primary" <button type="button" class="sw-btn sw-btn--primary sw-btn--sm"
onclick="document.getElementById('{{.FieldName}}Input').click()"> onclick="document.getElementById('{{.FieldName}}Input').click()">
Choose files… Choose files…
</button> </button>

View File

@@ -38,9 +38,6 @@
display: flex; justify-content: flex-end; gap: 0.5rem; 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-upload-row { display: flex; align-items: center; gap: 14px; margin-bottom: 1rem; }
.avatar-preview { .avatar-preview {
width: 56px; height: 56px; border-radius: 50%; background: var(--bg-raised); 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 { 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 { 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 button:hover { border-color: var(--border-light); color: var(--text); }
.admin-user-actions .btn-danger { color: var(--danger); } .admin-user-actions .sw-btn--danger { color: var(--danger); }
.admin-user-actions .btn-danger:hover { border-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; } .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; } .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; }

View File

@@ -1,27 +1,11 @@
/* ── primitives.css ────────────────────────── /* ── 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 ─────────────────────────────── */ /* ── Buttons ─────────────────────────────── */
button { font-family: var(--font); cursor: pointer; } 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 ────────────────────────────────── */ /* ── 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); } .checkbox-label { display: flex; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; margin: 0.5rem 0; color: var(--text-2); }
/* ── Toast ────────────────────────────────── */ /* Toast system — see sw-primitives.css (.sw-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); } }
/* ── Mode Selector (v0.21.3) ───────────── */ /* ── 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); } .repl-json-toggle:hover { color: var(--accent); }
/* ── Popup Menu (shared base) ───────────── */ /* Popup menu — see sw-primitives.css (.sw-menu) */
.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; }
/* ── Tools Toggle ────────────────────────── */ /* ── 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.active { background: var(--bg-raised); color: var(--text); }
.toggle-btn:hover { color: var(--text-2); } .toggle-btn:hover { color: var(--text-2); }
.btn-ghost { background: transparent; color: var(--text-2); border: 1px solid var(--border); } /* Button variants — see sw-primitives.css (.sw-btn--*) */
.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; }
/* ── Badge ───────────────────────────────── */ /* ── Badge ───────────────────────────────── */
.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; border: none; height: 1px; background: var(--border, #2a2a2e); margin: 4px 0;
} }
/* Dropdown — styled native select */ /* Dropdown — see sw-primitives.css (.sw-dropdown) */
.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); }
/* Toolbar — horizontal button row */ /* Toolbar — horizontal button row */
.sw-toolbar { .sw-toolbar {
@@ -455,22 +392,7 @@ select option { background: var(--bg-surface); color: var(--text); }
padding: 4px 0; padding: 4px 0;
} }
/* Tabs — tabbed content container */ /* Tabs — see sw-primitives.css (.sw-tabs) */
.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; }
/* ── Responsive: Mobile ───────────────────── */ /* ── Responsive: Mobile ───────────────────── */

View File

@@ -23,6 +23,8 @@
.sw-btn--secondary:hover:not(:disabled) { background: var(--bg-hover); border-color: var(--border-light); } .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 { background: var(--danger); color: var(--text-on-color); }
.sw-btn--danger:hover:not(:disabled) { opacity: 0.9; } .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 { background: transparent; color: var(--text-2); }
.sw-btn--ghost:hover:not(:disabled) { background: var(--bg-hover); color: var(--text); } .sw-btn--ghost:hover:not(:disabled) { background: var(--bg-hover); color: var(--text); }

View File

@@ -15,6 +15,7 @@
--bg-surface: #18181b; --bg-surface: #18181b;
--bg-raised: #222227; --bg-raised: #222227;
--bg-hover: #2a2a30; --bg-hover: #2a2a30;
--bg-active: #2f2f38;
--bg-elevated: #42424e; --bg-elevated: #42424e;
--border: #2e2e35; --border: #2e2e35;
--border-elevated: #555560; --border-elevated: #555560;
@@ -81,6 +82,7 @@
--bg-surface: #ffffff; --bg-surface: #ffffff;
--bg-raised: #eff0f3; --bg-raised: #eff0f3;
--bg-hover: #e5e6eb; --bg-hover: #e5e6eb;
--bg-active: #dddee5;
--bg-elevated: #ffffff; --bg-elevated: #ffffff;
--border: #d5d6dc; --border: #d5d6dc;
--border-elevated: #c0c0c8; --border-elevated: #c0c0c8;

View File

@@ -27,8 +27,8 @@
} }
.wf-stage-name { flex: 1; font-size: 13px; font-weight: 500; } .wf-stage-name { flex: 1; font-size: 13px; font-weight: 500; }
.wf-stage-row .badge { font-size: 10px; } .wf-stage-row .badge { font-size: 10px; }
.wf-stage-row .btn-small { opacity: 0; transition: opacity 0.15s; } .wf-stage-row .sw-btn--sm { opacity: 0; transition: opacity 0.15s; }
.wf-stage-row:hover .btn-small { opacity: 1; } .wf-stage-row:hover .sw-btn--sm { opacity: 1; }
/* ── Queue sidebar ───────────────────── */ /* ── Queue sidebar ───────────────────── */
@@ -55,16 +55,16 @@
.sb-queue-item.active { background: var(--accent-dim); color: var(--accent); } .sb-queue-item.active { background: var(--accent-dim); color: var(--accent); }
.sb-queue-icon { font-size: 14px; } .sb-queue-icon { font-size: 14px; }
.sb-queue-label { flex: 1; } .sb-queue-label { flex: 1; }
.sb-queue-item .btn-small { .sb-queue-item .sw-btn--sm {
opacity: 0; padding: 2px 6px; font-size: 11px; opacity: 0; padding: 2px 6px; font-size: 11px;
transition: opacity 0.15s; 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 */ /* 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-item { justify-content: center; padding: 5px 0; margin: 0 6px; gap: 0; }
.sidebar.collapsed .sb-queue-label { display: none; } .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; } .sidebar.collapsed .sb-queue-badge { display: none !important; }
/* Unpin button */ /* Unpin button */
@@ -150,7 +150,7 @@
.wf-bar-status.stale { background: var(--warning-dim); color: var(--warning-light); } .wf-bar-status.stale { background: var(--warning-dim); color: var(--warning-light); }
.wf-bar-spacer { flex: 1; } .wf-bar-spacer { flex: 1; }
.wf-bar-actions { display: flex; gap: 6px; } .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 */ /* DnD stage reorder feedback */
.wf-stage-row.dragging { opacity: 0.4; } .wf-stage-row.dragging { opacity: 0.4; }

View File

@@ -102,13 +102,13 @@ function DebugModal({ engine }) {
</div> </div>
<div class="modal-footer debug-modal-footer"> <div class="modal-footer debug-modal-footer">
<div class="debug-footer-left"> <div class="debug-footer-left">
<button class="btn-danger btn-small" onClick=${() => { setActiveTab('console'); engine.runDiagnostics(); }}>Diagnostics</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => { setActiveTab('console'); engine.runDiagnostics(); }}>Diagnostics</button>
<button class="btn-danger btn-small" onClick=${onPurge}>Purge Cache</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${onPurge}>Purge Cache</button>
</div> </div>
<div class="debug-footer-right"> <div class="debug-footer-right">
<button class="btn-secondary btn-small" onClick=${onClear}>Clear</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${onClear}>Clear</button>
<button class="btn-secondary btn-small" onClick=${() => engine.copyLog()}>Copy</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => engine.copyLog()}>Copy</button>
<button class="btn-secondary btn-small" onClick=${() => engine.downloadLog()}>Export</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => engine.downloadLog()}>Export</button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -77,9 +77,9 @@ export default function AuditSection() {
</div> </div>
<div style="margin-top:12px;display:flex;align-items:center;gap:8px;"> <div style="margin-top:12px;display:flex;align-items:center;gap:8px;">
<button class="btn-small" disabled=${page <= 1} onClick=${() => setPage(p => p - 1)}>\u2190 Prev</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" disabled=${page <= 1} onClick=${() => setPage(p => p - 1)}>\u2190 Prev</button>
<span class="text-muted" style="font-size:12px;">Page ${page} of ${totalPages} (${total} total)</span> <span class="text-muted" style="font-size:12px;">Page ${page} of ${totalPages} (${total} total)</span>
<button class="btn-small" disabled=${page >= totalPages} onClick=${() => setPage(p => p + 1)}>Next \u2192</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" disabled=${page >= totalPages} onClick=${() => setPage(p => p + 1)}>Next \u2192</button>
</div> </div>
` `
} }

View File

@@ -118,13 +118,13 @@ export default function BackupSection() {
return html` return html`
<div> <div>
<div style="display:flex;gap:8px;margin-bottom:20px;flex-wrap:wrap;"> <div style="display:flex;gap:8px;margin-bottom:20px;flex-wrap:wrap;">
<button class="btn btn-primary" onclick=${() => createBackup(false)} disabled=${creating || restoring}> <button class="sw-btn sw-btn--primary sw-btn--md" onclick=${() => createBackup(false)} disabled=${creating || restoring}>
${creating ? 'Creating\u2026' : 'Download Backup'} ${creating ? 'Creating\u2026' : 'Download Backup'}
</button> </button>
<button class="btn" onclick=${() => createBackup(true)} disabled=${creating || restoring}> <button class="btn" onclick=${() => createBackup(true)} disabled=${creating || restoring}>
${creating ? 'Creating\u2026' : 'Save to Server'} ${creating ? 'Creating\u2026' : 'Save to Server'}
</button> </button>
<button class="btn btn-danger" onclick=${restoreBackup} disabled=${creating || restoring}> <button class="sw-btn sw-btn--danger sw-btn--md" onclick=${restoreBackup} disabled=${creating || restoring}>
${restoring ? 'Restoring\u2026' : 'Restore from File'} ${restoring ? 'Restoring\u2026' : 'Restore from File'}
</button> </button>
</div> </div>
@@ -149,8 +149,8 @@ export default function BackupSection() {
<td>${formatBytes(b.size)}</td> <td>${formatBytes(b.size)}</td>
<td>${formatDate(b.created_at)}</td> <td>${formatDate(b.created_at)}</td>
<td> <td>
<button class="btn btn-sm" onclick=${() => downloadBackup(b.name)}>Download</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onclick=${() => downloadBackup(b.name)}>Download</button>
<button class="btn btn-sm btn-danger" onclick=${() => deleteBackup(b.name)} style="margin-left:4px;">Delete</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onclick=${() => deleteBackup(b.name)} style="margin-left:4px;">Delete</button>
</td> </td>
</tr> </tr>
`)} `)}

View File

@@ -38,7 +38,7 @@ export default function BroadcastSection() {
<option value="critical">Critical</option> <option value="critical">Critical</option>
</select> </select>
</div> </div>
<button class="btn-md btn-primary" onClick=${send} disabled=${sending}>${sending ? 'Sending\u2026' : 'Send Broadcast'}</button> <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${send} disabled=${sending}>${sending ? 'Sending\u2026' : 'Send Broadcast'}</button>
</div> </div>
`; `;
} }

View File

@@ -114,7 +114,7 @@ export default function ConnectionsSection() {
return html` return html`
<div style="margin-bottom:12px;display:flex;align-items:center;gap:8px;"> <div style="margin-bottom:12px;display:flex;align-items:center;gap:8px;">
<button class="btn-md btn-primary" onClick=${openAdd} <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${openAdd}
disabled=${!connTypes.length}>+ Add Connection</button> disabled=${!connTypes.length}>+ Add Connection</button>
${!connTypes.length && html` ${!connTypes.length && html`
<span style="color:var(--text-3);font-size:12px;"> <span style="color:var(--text-3);font-size:12px;">
@@ -165,11 +165,11 @@ export default function ConnectionsSection() {
</div> </div>
`)} `)}
<div style="display:flex;gap:8px;margin-top:12px;"> <div style="display:flex;gap:8px;margin-top:12px;">
<button class="btn-md btn-primary" disabled=${saving} <button class="sw-btn sw-btn--primary sw-btn--md" disabled=${saving}
onClick=${save}> onClick=${save}>
${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'} ${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'}
</button> </button>
<button class="btn-md btn-secondary" onClick=${cancel}>Cancel</button> <button class="sw-btn sw-btn--secondary sw-btn--md" onClick=${cancel}>Cancel</button>
</div> </div>
</div> </div>
`} `}
@@ -196,7 +196,7 @@ export default function ConnectionsSection() {
<button class="icon-btn" title="Edit" onClick=${() => openEdit(c)}> <button class="icon-btn" title="Edit" onClick=${() => openEdit(c)}>
\u{270F}\u{FE0F} \u{270F}\u{FE0F}
</button> </button>
<button class="icon-btn icon-btn-danger" title="Delete" <button class="icon-btn icon-sw-btn sw-btn--danger sw-btn--md" title="Delete"
onClick=${() => del(c)}> onClick=${() => del(c)}>
\u{1F5D1} \u{1F5D1}
</button> </button>

View File

@@ -112,11 +112,11 @@ export default function GroupsSection() {
if (detail) return html` if (detail) return html`
<div> <div>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
<button class="btn-small" onClick=${() => setDetail(null)}>\u2190 Back</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setDetail(null)}>\u2190 Back</button>
<h4 style="margin:0;">${detail.name}</h4> <h4 style="margin:0;">${detail.name}</h4>
<span class="text-muted" style="font-size:12px;">${detail.scope}</span> <span class="text-muted" style="font-size:12px;">${detail.scope}</span>
<div style="flex:1"></div> <div style="flex:1"></div>
<button class="btn-small btn-danger" onClick=${deleteGroup}>Delete</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${deleteGroup}>Delete</button>
</div> </div>
<div class="settings-section" style="margin-bottom:16px;"> <div class="settings-section" style="margin-bottom:16px;">
@@ -134,7 +134,7 @@ export default function GroupsSection() {
</div> </div>
<div style="margin-bottom:16px;"> <div style="margin-bottom:16px;">
<button class="btn-small btn-primary" onClick=${savePermissions}>Save Permissions</button> <button class="sw-btn sw-btn--primary sw-btn--sm" onClick=${savePermissions}>Save Permissions</button>
</div> </div>
<hr style="border:none;border-top:1px solid var(--border);margin:16px 0;" /> <hr style="border:none;border-top:1px solid var(--border);margin:16px 0;" />
@@ -144,7 +144,7 @@ export default function GroupsSection() {
${members.map(m => html` ${members.map(m => html`
<div class="admin-surface-row" key=${m.id || m.user_id}> <div class="admin-surface-row" key=${m.id || m.user_id}>
<span style="flex:1;">${m.username || m.user_id}</span> <span style="flex:1;">${m.username || m.user_id}</span>
<button class="btn-small btn-danger" onClick=${() => removeMember(m.user_id || m.id)}>Remove</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => removeMember(m.user_id || m.id)}>Remove</button>
</div> </div>
`)} `)}
</div> </div>
@@ -160,12 +160,12 @@ export default function GroupsSection() {
</div> </div>
</div> </div>
<div class="form-row" style="margin-top:8px;gap:8px;"> <div class="form-row" style="margin-top:8px;gap:8px;">
<button type="submit" class="btn-small btn-primary">Add</button> <button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Add</button>
<button type="button" class="btn-small" onClick=${() => setShowAddMember(false)}>Cancel</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowAddMember(false)}>Cancel</button>
</div> </div>
</form> </form>
` `
: html`<button class="btn-small" style="margin-top:8px;" onClick=${() => setShowAddMember(true)}>+ Add Member</button>` : html`<button class="sw-btn sw-btn--secondary sw-btn--sm" style="margin-top:8px;" onClick=${() => setShowAddMember(true)}>+ Add Member</button>`
} }
</div> </div>
`; `;
@@ -174,7 +174,7 @@ export default function GroupsSection() {
<div> <div>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
<div style="flex:1"></div> <div style="flex:1"></div>
<button class="btn-md btn-primary" onClick=${() => setShowCreate(true)}>+ Add</button> <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${() => setShowCreate(true)}>+ Add</button>
</div> </div>
${showCreate && html` ${showCreate && html`
@@ -187,8 +187,8 @@ export default function GroupsSection() {
</div> </div>
</div> </div>
<div class="form-row" style="margin-top:8px;gap:8px;"> <div class="form-row" style="margin-top:8px;gap:8px;">
<button type="submit" class="btn-small btn-primary">Create</button> <button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Create</button>
<button type="button" class="btn-small" onClick=${() => setShowCreate(false)}>Cancel</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowCreate(false)}>Cancel</button>
</div> </div>
</form> </form>
`} `}

View File

@@ -252,10 +252,10 @@ export default function PackagesSection() {
options=${TYPE_OPTIONS.map(t => ({ label: t.charAt(0).toUpperCase() + t.slice(1), value: t }))} options=${TYPE_OPTIONS.map(t => ({ label: t.charAt(0).toUpperCase() + t.slice(1), value: t }))}
/> />
<span style="flex:1;" /> <span style="flex:1;" />
<button class="btn-small" onClick=${uploadPackage} disabled=${installing}> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${uploadPackage} disabled=${installing}>
${installing ? 'Installing\u2026' : 'Install Package'} ${installing ? 'Installing\u2026' : 'Install Package'}
</button> </button>
<button class="btn-small" onClick=${toggleRegistry}> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${toggleRegistry}>
${registryOpen ? 'Close Registry' : 'Browse Registry'} ${registryOpen ? 'Close Registry' : 'Browse Registry'}
</button> </button>
</div> </div>
@@ -276,28 +276,28 @@ export default function PackagesSection() {
${pkg.is_system && html`<span class="badge" style="margin-left:4px;">system</span>`} ${pkg.is_system && html`<span class="badge" style="margin-left:4px;">system</span>`}
</div> </div>
<div class="admin-actions-cell"> <div class="admin-actions-cell">
<button class="btn-small" <button class="sw-btn sw-btn--secondary sw-btn--sm"
onClick=${() => toggleEnabled(pkg)} onClick=${() => toggleEnabled(pkg)}
disabled=${CORE_IDS.has(pkg.id) || pkg.status === 'dormant' || pkg.status === 'pending_review'} 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' : ''}> title=${pkg.status === 'dormant' ? 'Requires chat' : pkg.status === 'pending_review' ? 'Grant permissions to activate' : ''}>
${pkg.enabled ? 'Disable' : 'Enable'} ${pkg.enabled ? 'Disable' : 'Enable'}
</button> </button>
${hasManifestSettings(pkg) && html` ${hasManifestSettings(pkg) && html`
<button class="btn-small" onClick=${() => toggleSettings(pkg.id)}> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => toggleSettings(pkg.id)}>
${expandedId === pkg.id ? 'Close' : 'Settings'} ${expandedId === pkg.id ? 'Close' : 'Settings'}
</button> </button>
`} `}
${hasDeclaredPerms(pkg) && html` ${hasDeclaredPerms(pkg) && html`
<button class="btn-small" onClick=${() => togglePerms(pkg.id)}> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => togglePerms(pkg.id)}>
${permsId === pkg.id ? 'Close' : 'Permissions'} ${permsId === pkg.id ? 'Close' : 'Permissions'}
</button> </button>
`} `}
${pkg.source !== 'core' && html` ${pkg.source !== 'core' && html`
<button class="btn-small" onClick=${() => updatePkg(pkg)}>Update</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => updatePkg(pkg)}>Update</button>
`} `}
<button class="btn-small" onClick=${() => exportPkg(pkg.id)}>Export</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => exportPkg(pkg.id)}>Export</button>
${pkg.source !== 'core' && !pkg.is_system && html` ${pkg.source !== 'core' && !pkg.is_system && html`
<button class="btn-small btn-danger" onClick=${() => deletePkg(pkg)}>Delete</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => deletePkg(pkg)}>Delete</button>
`} `}
</div> </div>
</div> </div>
@@ -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;" /> 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;" />
</div> </div>
`)} `)}
<button class="btn-small" style="margin-top:4px;" onClick=${() => saveSettings(pkg.id)}>Save</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" style="margin-top:4px;" onClick=${() => saveSettings(pkg.id)}>Save</button>
` `
} }
</div> </div>
@@ -333,7 +333,7 @@ export default function PackagesSection() {
${perms.map(p => html` ${perms.map(p => html`
<div key=${p.permission} style="display:flex;gap:8px;align-items:center;margin-bottom:4px;"> <div key=${p.permission} style="display:flex;gap:8px;align-items:center;margin-bottom:4px;">
<code style="min-width:140px;font-size:12px;">${p.permission}</code> <code style="min-width:140px;font-size:12px;">${p.permission}</code>
<button class="btn-small ${p.granted ? 'btn-danger' : 'btn-primary'}" <button class="sw-btn sw-btn--secondary sw-btn--sm ${p.granted ? 'sw-btn sw-btn--danger sw-btn--md' : 'sw-btn sw-btn--primary sw-btn--md'}"
style="min-width:64px;font-size:11px;" style="min-width:64px;font-size:11px;"
onClick=${() => p.granted ? revokePerm(pkg.id, p.permission) : grantPerm(pkg.id, p.permission)}> onClick=${() => p.granted ? revokePerm(pkg.id, p.permission) : grantPerm(pkg.id, p.permission)}>
${p.granted ? 'Revoke' : 'Grant'} ${p.granted ? 'Revoke' : 'Grant'}
@@ -345,7 +345,7 @@ export default function PackagesSection() {
`)} `)}
</div> </div>
${perms.some(p => !p.granted) && html` ${perms.some(p => !p.granted) && html`
<button class="btn-small btn-primary" onClick=${() => grantAllPerms(pkg.id)}>Grant All</button> <button class="sw-btn sw-btn--primary sw-btn--sm" onClick=${() => grantAllPerms(pkg.id)}>Grant All</button>
`} `}
` `
} }
@@ -376,7 +376,7 @@ export default function PackagesSection() {
</div> </div>
<div class="admin-actions-cell"> <div class="admin-actions-cell">
${typeBadge(rp.type)} ${typeBadge(rp.type)}
<button class="btn-small btn-primary" <button class="sw-btn sw-btn--primary sw-btn--sm"
onClick=${() => installFromRegistry(rp.download_url)}>Install</button> onClick=${() => installFromRegistry(rp.download_url)}>Install</button>
</div> </div>
</div> </div>

View File

@@ -231,11 +231,11 @@ export default function SettingsSection() {
</div> </div>
<div class="settings-section"><h3>Email</h3> <div class="settings-section"><h3>Email</h3>
<button class="btn-small" onClick=${testEmail}>Send Test Email</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${testEmail}>Send Test Email</button>
</div> </div>
<div style="display:flex;gap:8px;align-items:center;margin-top:16px;"> <div style="display:flex;gap:8px;align-items:center;margin-top:16px;">
<button class="btn-md btn-primary" onClick=${save} disabled=${saving}>${saving ? 'Saving\u2026' : 'Save Settings'}</button> <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${save} disabled=${saving}>${saving ? 'Saving\u2026' : 'Save Settings'}</button>
</div> </div>
</div> </div>
`; `;

View File

@@ -61,7 +61,7 @@ export default function StorageSection() {
<div class="settings-section" style="margin-bottom:16px;"> <div class="settings-section" style="margin-bottom:16px;">
<h4>Orphaned Blobs</h4> <h4>Orphaned Blobs</h4>
<p class="text-muted" style="font-size:12px;">${orphans.count || 0} orphans found</p> <p class="text-muted" style="font-size:12px;">${orphans.count || 0} orphans found</p>
${(orphans.count || 0) > 0 && html`<button class="btn-small btn-primary" onClick=${cleanup}>Clean Up</button>`} ${(orphans.count || 0) > 0 && html`<button class="sw-btn sw-btn--primary sw-btn--sm" onClick=${cleanup}>Clean Up</button>`}
</div> </div>
`} `}

View File

@@ -94,10 +94,10 @@ export default function TeamsSection() {
if (detail) return html` if (detail) return html`
<div> <div>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
<button class="btn-small" onClick=${() => setDetail(null)}>\u2190 Back</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setDetail(null)}>\u2190 Back</button>
<h4 style="margin:0;">${detail.name}</h4> <h4 style="margin:0;">${detail.name}</h4>
<div style="flex:1"></div> <div style="flex:1"></div>
<button class="btn-small btn-danger" onClick=${() => deleteTeam(detail.id)}>Delete Team</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => deleteTeam(detail.id)}>Delete Team</button>
</div> </div>
${detail.description && html`<p class="text-muted" style="font-size:12px;margin-bottom:12px;">${detail.description}</p>`} ${detail.description && html`<p class="text-muted" style="font-size:12px;margin-bottom:12px;">${detail.description}</p>`}
@@ -112,7 +112,7 @@ export default function TeamsSection() {
<option value="member">member</option> <option value="member">member</option>
<option value="admin">admin</option> <option value="admin">admin</option>
</select> </select>
<button class="btn-small btn-danger" onClick=${() => removeMember(m.id)}>Remove</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => removeMember(m.id)}>Remove</button>
</div> </div>
`)} `)}
</div> </div>
@@ -132,12 +132,12 @@ export default function TeamsSection() {
</div> </div>
</div> </div>
<div class="form-row" style="margin-top:8px;gap:8px;"> <div class="form-row" style="margin-top:8px;gap:8px;">
<button type="submit" class="btn-small btn-primary">Add</button> <button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Add</button>
<button type="button" class="btn-small" onClick=${() => setShowAddMember(false)}>Cancel</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowAddMember(false)}>Cancel</button>
</div> </div>
</form> </form>
` `
: html`<button class="btn-small" style="margin-top:8px;" onClick=${() => setShowAddMember(true)}>+ Add Member</button>` : html`<button class="sw-btn sw-btn--secondary sw-btn--sm" style="margin-top:8px;" onClick=${() => setShowAddMember(true)}>+ Add Member</button>`
} }
</div> </div>
`; `;
@@ -147,7 +147,7 @@ export default function TeamsSection() {
<div> <div>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
<div style="flex:1"></div> <div style="flex:1"></div>
<button class="btn-md btn-primary" onClick=${() => setShowCreate(true)}>+ Add</button> <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${() => setShowCreate(true)}>+ Add</button>
</div> </div>
${showCreate && html` ${showCreate && html`
@@ -157,8 +157,8 @@ export default function TeamsSection() {
<div class="form-group"><label>Description</label><input name="desc" placeholder="Optional" /></div> <div class="form-group"><label>Description</label><input name="desc" placeholder="Optional" /></div>
</div> </div>
<div class="form-row" style="margin-top:8px;gap:8px;"> <div class="form-row" style="margin-top:8px;gap:8px;">
<button type="submit" class="btn-small btn-primary">Create</button> <button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Create</button>
<button type="button" class="btn-small" onClick=${() => setShowCreate(false)}>Cancel</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowCreate(false)}>Cancel</button>
</div> </div>
</form> </form>
`} `}

View File

@@ -103,7 +103,7 @@ export default function UsersSection() {
<input placeholder="Search users\u2026" value=${search} onInput=${e => setSearch(e.target.value)} autocomplete="off" /> <input placeholder="Search users\u2026" value=${search} onInput=${e => setSearch(e.target.value)} autocomplete="off" />
</div> </div>
<div style="flex:1"></div> <div style="flex:1"></div>
<button class="btn-md btn-primary" onClick=${() => setShowCreate(true)}>+ Add</button> <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${() => setShowCreate(true)}>+ Add</button>
</div> </div>
${showCreate && html` ${showCreate && html`
@@ -117,8 +117,8 @@ export default function UsersSection() {
</div> </div>
</div> </div>
<div class="form-row" style="margin-top:8px;gap:8px;"> <div class="form-row" style="margin-top:8px;gap:8px;">
<button type="submit" class="btn-small btn-primary">Create</button> <button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Create</button>
<button type="button" class="btn-small" onClick=${() => setShowCreate(false)}>Cancel</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowCreate(false)}>Cancel</button>
</div> </div>
</form> </form>
`} `}
@@ -134,14 +134,14 @@ export default function UsersSection() {
</div> </div>
<div class="admin-actions-cell"> <div class="admin-actions-cell">
${u.status === 'pending' && html` ${u.status === 'pending' && html`
<button class="btn-small btn-primary" onClick=${() => approveUser(u.id)}>Approve</button> <button class="sw-btn sw-btn--primary sw-btn--sm" onClick=${() => approveUser(u.id)}>Approve</button>
`} `}
${u.is_active ${u.is_active
? html`<button class="btn-small" onClick=${() => toggleActive(u.id, false)}>Disable</button>` ? html`<button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => toggleActive(u.id, false)}>Disable</button>`
: html`<button class="btn-small" onClick=${() => toggleActive(u.id, true)}>Enable</button>` : html`<button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => toggleActive(u.id, true)}>Enable</button>`
} }
<button class="btn-small" onClick=${() => resetPassword(u.id, u.username)}>Reset PW</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => resetPassword(u.id, u.username)}>Reset PW</button>
<button class="btn-small btn-danger" onClick=${() => deleteUser(u.id, u.username)}>Delete</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => deleteUser(u.id, u.username)}>Delete</button>
</div> </div>
</div> </div>
`)} `)}

View File

@@ -80,10 +80,10 @@ export default function WorkflowsSection() {
if (editing) return html` if (editing) return html`
<form onSubmit=${updateWorkflow}> <form onSubmit=${updateWorkflow}>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
<button type="button" class="btn-small" onClick=${() => setEditing(null)}>\u2190 Back</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setEditing(null)}>\u2190 Back</button>
<h4 style="margin:0;">Edit: ${editing.name}</h4> <h4 style="margin:0;">Edit: ${editing.name}</h4>
<div style="flex:1"></div> <div style="flex:1"></div>
<button type="button" class="btn-small btn-danger" onClick=${() => deleteWorkflow(editing.id)}>Delete</button> <button type="button" class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => deleteWorkflow(editing.id)}>Delete</button>
</div> </div>
<div class="form-row"> <div class="form-row">
<div class="form-group"><label>Name</label><input name="name" value=${editing.name || ''} /></div> <div class="form-group"><label>Name</label><input name="name" value=${editing.name || ''} /></div>
@@ -113,7 +113,7 @@ export default function WorkflowsSection() {
</div> </div>
<div class="form-row" style="margin-top:16px;gap:8px;"> <div class="form-row" style="margin-top:16px;gap:8px;">
<button type="submit" class="btn-small btn-primary">Save</button> <button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Save</button>
</div> </div>
</form> </form>
`; `;
@@ -122,7 +122,7 @@ export default function WorkflowsSection() {
<div> <div>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
<div style="flex:1"></div> <div style="flex:1"></div>
<button class="btn-md btn-primary" onClick=${() => setShowCreate(true)}>+ Add</button> <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${() => setShowCreate(true)}>+ Add</button>
</div> </div>
${showCreate && html` ${showCreate && html`
@@ -138,8 +138,8 @@ export default function WorkflowsSection() {
</div> </div>
<div class="form-group"><label>Description</label><input name="description" placeholder="Optional" /></div> <div class="form-group"><label>Description</label><input name="description" placeholder="Optional" /></div>
<div class="form-row" style="margin-top:8px;gap:8px;"> <div class="form-row" style="margin-top:8px;gap:8px;">
<button type="submit" class="btn-small btn-primary">Create</button> <button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Create</button>
<button type="button" class="btn-small" onClick=${() => setShowCreate(false)}>Cancel</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowCreate(false)}>Cancel</button>
</div> </div>
</form> </form>
`} `}

View File

@@ -73,6 +73,6 @@ export function AppearanceSection() {
</div> </div>
</div> </div>
</div> </div>
<button class="btn-md btn-primary" onClick=${save}>Save</button> <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${save}>Save</button>
`; `;
} }

View File

@@ -114,7 +114,7 @@ export function ConnectionsSection() {
return html` return html`
<div style="margin-bottom:12px;"> <div style="margin-bottom:12px;">
<button class="btn-md btn-primary" onClick=${openAdd} <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${openAdd}
disabled=${!connTypes.length}>+ Add Connection</button> disabled=${!connTypes.length}>+ Add Connection</button>
${!connTypes.length && html` ${!connTypes.length && html`
<span style="margin-left:8px;color:var(--text-3);font-size:12px;"> <span style="margin-left:8px;color:var(--text-3);font-size:12px;">
@@ -169,11 +169,11 @@ export function ConnectionsSection() {
</div> </div>
`)} `)}
<div style="display:flex;gap:8px;margin-top:12px;"> <div style="display:flex;gap:8px;margin-top:12px;">
<button class="btn-md btn-primary" disabled=${saving} <button class="sw-btn sw-btn--primary sw-btn--md" disabled=${saving}
onClick=${save}> onClick=${save}>
${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'} ${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'}
</button> </button>
<button class="btn-md btn-secondary" onClick=${cancel}>Cancel</button> <button class="sw-btn sw-btn--secondary sw-btn--md" onClick=${cancel}>Cancel</button>
</div> </div>
</div> </div>
`} `}
@@ -207,7 +207,7 @@ export function ConnectionsSection() {
<button class="icon-btn" title="Edit" onClick=${() => openEdit(c)}> <button class="icon-btn" title="Edit" onClick=${() => openEdit(c)}>
\u{270F}\u{FE0F} \u{270F}\u{FE0F}
</button> </button>
<button class="icon-btn icon-btn-danger" title="Delete" <button class="icon-btn icon-sw-btn sw-btn--danger sw-btn--md" title="Delete"
onClick=${() => del(c)}> onClick=${() => del(c)}>
\u{1F5D1} \u{1F5D1}
</button> </button>

View File

@@ -54,7 +54,7 @@ export function GeneralSection() {
If your chosen surface is uninstalled, the global default is used. If your chosen surface is uninstalled, the global default is used.
</span> </span>
</div> </div>
<button class="btn-md btn-primary" style="margin-top:12px;" <button class="sw-btn sw-btn--primary sw-btn--md" style="margin-top:12px;"
disabled=${saving} onClick=${save}> disabled=${saving} onClick=${save}>
${saving ? 'Saving\u2026' : 'Save'} ${saving ? 'Saving\u2026' : 'Save'}
</button> </button>

View File

@@ -81,7 +81,7 @@ export default function NotificationsSection() {
onChange=${() => togglePref(type, enabled)} /> onChange=${() => togglePref(type, enabled)} />
<span class="toggle-track"></span> <span class="toggle-track"></span>
</label> </label>
<button class="btn-small btn-ghost" <button class="sw-btn sw-btn--ghost sw-btn--sm"
disabled=${isSaving} disabled=${isSaving}
onClick=${() => resetPref(type)} onClick=${() => resetPref(type)}
title="Reset to default"> title="Reset to default">

View File

@@ -124,12 +124,12 @@ export function ProfileSection() {
} }
</div> </div>
<div style="display:flex;gap:8px;"> <div style="display:flex;gap:8px;">
<button class="btn-sm btn-secondary" <button class="sw-btn sw-btn--secondary sw-btn--sm"
onClick=${() => fileRef.current?.click()}> onClick=${() => fileRef.current?.click()}>
Upload Upload
</button> </button>
${avatarUrl && html` ${avatarUrl && html`
<button class="btn-sm btn-secondary" onClick=${removeAvatar}> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${removeAvatar}>
Remove Remove
</button> </button>
`} `}
@@ -148,7 +148,7 @@ export function ProfileSection() {
<label>Email</label> <label>Email</label>
<input type="email" disabled value=${email} /> <input type="email" disabled value=${email} />
</div> </div>
<button class="btn-md btn-primary" disabled=${savingProfile} <button class="sw-btn sw-btn--primary sw-btn--md" disabled=${savingProfile}
onClick=${saveProfile}> onClick=${saveProfile}>
${savingProfile ? 'Saving\u2026' : 'Save Profile'} ${savingProfile ? 'Saving\u2026' : 'Save Profile'}
</button> </button>
@@ -171,7 +171,7 @@ export function ProfileSection() {
<input type="password" autocomplete="new-password" <input type="password" autocomplete="new-password"
value=${confirmPw} onInput=${e => setConfirmPw(e.target.value)} /> value=${confirmPw} onInput=${e => setConfirmPw(e.target.value)} />
</div> </div>
<button class="btn-md btn-primary" disabled=${savingPw} <button class="sw-btn sw-btn--primary sw-btn--md" disabled=${savingPw}
onClick=${changePassword}> onClick=${changePassword}>
${savingPw ? 'Updating\u2026' : 'Update Password'} ${savingPw ? 'Updating\u2026' : 'Update Password'}
</button> </button>

View File

@@ -77,9 +77,9 @@ export default function ActivitySection({ teamId }) {
</div> </div>
<div style="margin-top:12px;display:flex;align-items:center;gap:8px;"> <div style="margin-top:12px;display:flex;align-items:center;gap:8px;">
<button class="btn-small" disabled=${page <= 1} onClick=${() => setPage(p => p - 1)}>\u2190 Prev</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" disabled=${page <= 1} onClick=${() => setPage(p => p - 1)}>\u2190 Prev</button>
<span class="text-muted" style="font-size:12px;">Page ${page} of ${totalPages} (${total} total)</span> <span class="text-muted" style="font-size:12px;">Page ${page} of ${totalPages} (${total} total)</span>
<button class="btn-small" disabled=${page >= totalPages} onClick=${() => setPage(p => p + 1)}>Next \u2192</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" disabled=${page >= totalPages} onClick=${() => setPage(p => p + 1)}>Next \u2192</button>
</div> </div>
` `
} }

View File

@@ -114,7 +114,7 @@ export default function ConnectionsSection({ teamId }) {
return html` return html`
<div style="margin-bottom:12px;"> <div style="margin-bottom:12px;">
<button class="btn-md btn-primary" onClick=${openAdd} <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${openAdd}
disabled=${!connTypes.length}>+ Add Connection</button> disabled=${!connTypes.length}>+ Add Connection</button>
</div> </div>
@@ -157,11 +157,11 @@ export default function ConnectionsSection({ teamId }) {
</div> </div>
`)} `)}
<div style="display:flex;gap:8px;margin-top:12px;"> <div style="display:flex;gap:8px;margin-top:12px;">
<button class="btn-md btn-primary" disabled=${saving} <button class="sw-btn sw-btn--primary sw-btn--md" disabled=${saving}
onClick=${save}> onClick=${save}>
${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'} ${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'}
</button> </button>
<button class="btn-md btn-secondary" onClick=${cancel}>Cancel</button> <button class="sw-btn sw-btn--secondary sw-btn--md" onClick=${cancel}>Cancel</button>
</div> </div>
</div> </div>
`} `}
@@ -187,7 +187,7 @@ export default function ConnectionsSection({ teamId }) {
<button class="icon-btn" title="Edit" onClick=${() => openEdit(c)}> <button class="icon-btn" title="Edit" onClick=${() => openEdit(c)}>
\u{270F}\u{FE0F} \u{270F}\u{FE0F}
</button> </button>
<button class="icon-btn icon-btn-danger" title="Delete" <button class="icon-btn icon-sw-btn sw-btn--danger sw-btn--md" title="Delete"
onClick=${() => del(c)}> onClick=${() => del(c)}>
\u{1F5D1} \u{1F5D1}
</button> </button>

View File

@@ -185,7 +185,7 @@ function TeamAdminSurface() {
<span class="settings-topbar-title"> <span class="settings-topbar-title">
${teamName} ${teamName}
${adminTeams.length > 1 && html` ${adminTeams.length > 1 && html`
<button class="btn-small btn-ghost" style="margin-left:8px;font-size:11px;" <button class="sw-btn sw-btn--ghost sw-btn--sm" style="margin-left:8px;font-size:11px;"
onClick=${() => setSelectedTeam(null)}>Switch Team</button> onClick=${() => setSelectedTeam(null)}>Switch Team</button>
`} `}
</span> </span>

View File

@@ -79,7 +79,7 @@ export default function MembersSection({ teamId }) {
<div> <div>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
<div style="flex:1"></div> <div style="flex:1"></div>
<button class="btn-md btn-primary" onClick=${openAdd}>+ Add Member</button> <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${openAdd}>+ Add Member</button>
</div> </div>
${showAdd && html` ${showAdd && html`
@@ -98,8 +98,8 @@ export default function MembersSection({ teamId }) {
</div> </div>
</div> </div>
<div class="form-row" style="margin-top:8px;gap:8px;"> <div class="form-row" style="margin-top:8px;gap:8px;">
<button type="submit" class="btn-small btn-primary">Add</button> <button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Add</button>
<button type="button" class="btn-small" onClick=${() => setShowAdd(false)}>Cancel</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowAdd(false)}>Cancel</button>
</div> </div>
</form> </form>
`} `}
@@ -113,7 +113,7 @@ export default function MembersSection({ teamId }) {
onChange=${e => updateRole(m.id || m.user_id, e.target.value)}> onChange=${e => updateRole(m.id || m.user_id, e.target.value)}>
${roles.map(r => html`<option key=${r} value=${r}>${r}</option>`)} ${roles.map(r => html`<option key=${r} value=${r}>${r}</option>`)}
</select> </select>
<button class="btn-small btn-danger" onClick=${() => removeMember(m.id || m.user_id)}>Remove</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => removeMember(m.id || m.user_id)}>Remove</button>
</div> </div>
`)} `)}
</div> </div>
@@ -121,7 +121,7 @@ export default function MembersSection({ teamId }) {
<div style="margin-top:24px;border-top:1px solid var(--border-1);padding-top:12px;"> <div style="margin-top:24px;border-top:1px solid var(--border-1);padding-top:12px;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
<h4 style="margin:0;flex:1;">Team Roles</h4> <h4 style="margin:0;flex:1;">Team Roles</h4>
<button class="btn-small" onClick=${() => setShowRoles(!showRoles)}> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowRoles(!showRoles)}>
${showRoles ? 'Hide' : 'Manage'} ${showRoles ? 'Hide' : 'Manage'}
</button> </button>
</div> </div>
@@ -132,7 +132,7 @@ export default function MembersSection({ teamId }) {
<div style="display:flex;gap:8px;align-items:center;"> <div style="display:flex;gap:8px;align-items:center;">
<input value=${newRole} onInput=${e => setNewRole(e.target.value)} <input value=${newRole} onInput=${e => setNewRole(e.target.value)}
placeholder="New role name" style="width:150px;" /> placeholder="New role name" style="width:150px;" />
<button class="btn-small btn-primary" onClick=${async () => { <button class="sw-btn sw-btn--primary sw-btn--sm" onClick=${async () => {
if (!newRole.trim()) return; if (!newRole.trim()) return;
const updated = [...new Set([...roles, newRole.trim()])]; const updated = [...new Set([...roles, newRole.trim()])];
try { try {
@@ -147,7 +147,7 @@ export default function MembersSection({ teamId }) {
${roles.filter(r => r !== 'admin' && r !== 'member').map(r => html` ${roles.filter(r => r !== 'admin' && r !== 'member').map(r => html`
<div class="admin-surface-row" key=${r}> <div class="admin-surface-row" key=${r}>
<span style="flex:1;">${r}</span> <span style="flex:1;">${r}</span>
<button class="btn-small btn-danger" onClick=${async () => { <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${async () => {
const updated = roles.filter(x => x !== r); const updated = roles.filter(x => x !== r);
try { try {
await sw.api.put('/api/v1/teams/' + teamId + '/roles', { roles: updated }); await sw.api.put('/api/v1/teams/' + teamId + '/roles', { roles: updated });

View File

@@ -65,7 +65,7 @@ export default function SettingsSection({ teamId }) {
`} `}
<div style="display:flex;gap:8px;align-items:center;margin-top:16px;"> <div style="display:flex;gap:8px;align-items:center;margin-top:16px;">
<button class="btn-md btn-primary" onClick=${save} disabled=${saving}>${saving ? 'Saving\u2026' : 'Save Settings'}</button> <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${save} disabled=${saving}>${saving ? 'Saving\u2026' : 'Save Settings'}</button>
</div> </div>
</div> </div>
`; `;

View File

@@ -46,7 +46,7 @@ export default function WorkflowsSection({ teamId }) {
<div class="admin-tabs" style="display:flex;gap:4px;margin-bottom:16px;"> <div class="admin-tabs" style="display:flex;gap:4px;margin-bottom:16px;">
${TABS.map(t => html` ${TABS.map(t => html`
<button key=${t} <button key=${t}
class="btn-small ${tab === t ? 'btn-primary' : ''}" class="sw-btn sw-btn--secondary sw-btn--sm ${tab === t ? 'sw-btn sw-btn--primary sw-btn--md' : ''}"
onClick=${() => setTab(t)}>${t}</button> onClick=${() => setTab(t)}>${t}</button>
`)} `)}
</div> </div>
@@ -123,8 +123,8 @@ function WorkflowsTab({ teamId }) {
<div> <div>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
<div style="flex:1"></div> <div style="flex:1"></div>
<button class="btn-md" onClick=${openAdopt}>Adopt Global</button> <button class="sw-btn sw-btn--secondary sw-btn--md" onClick=${openAdopt}>Adopt Global</button>
<button class="btn-md btn-primary" onClick=${() => setShowCreate(true)}>+ Add</button> <button class="sw-btn sw-btn--primary sw-btn--md" onClick=${() => setShowCreate(true)}>+ Add</button>
</div> </div>
${showAdopt && html` ${showAdopt && html`
@@ -132,7 +132,7 @@ function WorkflowsTab({ teamId }) {
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
<strong>Adopt a Global Workflow</strong> <strong>Adopt a Global Workflow</strong>
<div style="flex:1"></div> <div style="flex:1"></div>
<button class="btn-small" onClick=${() => setShowAdopt(false)}>Cancel</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowAdopt(false)}>Cancel</button>
</div> </div>
${available.length === 0 && html`<div class="empty-hint">No global workflows available</div>`} ${available.length === 0 && html`<div class="empty-hint">No global workflows available</div>`}
<div class="admin-list"> <div class="admin-list">
@@ -143,7 +143,7 @@ function WorkflowsTab({ teamId }) {
<span class="text-muted" style="margin-left:8px;font-size:11px;">/${w.slug}</span> <span class="text-muted" style="margin-left:8px;font-size:11px;">/${w.slug}</span>
</div> </div>
<span class="badge">${w.entry_mode}</span> <span class="badge">${w.entry_mode}</span>
<button class="btn-small btn-primary" onClick=${() => adoptWorkflow(w.id)}>Adopt</button> <button class="sw-btn sw-btn--primary sw-btn--sm" onClick=${() => adoptWorkflow(w.id)}>Adopt</button>
</div> </div>
`)} `)}
</div> </div>
@@ -163,8 +163,8 @@ function WorkflowsTab({ teamId }) {
</div> </div>
<div class="form-group"><label>Description</label><input name="description" placeholder="Optional" /></div> <div class="form-group"><label>Description</label><input name="description" placeholder="Optional" /></div>
<div class="form-row" style="margin-top:8px;gap:8px;"> <div class="form-row" style="margin-top:8px;gap:8px;">
<button type="submit" class="btn-small btn-primary">Create</button> <button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Create</button>
<button type="button" class="btn-small" onClick=${() => setShowCreate(false)}>Cancel</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowCreate(false)}>Cancel</button>
</div> </div>
</form> </form>
`} `}
@@ -266,11 +266,11 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
return html` return html`
<form onSubmit=${updateWorkflow}> <form onSubmit=${updateWorkflow}>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
<button type="button" class="btn-small" onClick=${onBack}>\u2190 Back</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${onBack}>\u2190 Back</button>
<h4 style="margin:0;">Edit: ${workflow.name}</h4> <h4 style="margin:0;">Edit: ${workflow.name}</h4>
<div style="flex:1"></div> <div style="flex:1"></div>
<button type="button" class="btn-small" onClick=${publishWorkflow}>Publish</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${publishWorkflow}>Publish</button>
<button type="button" class="btn-small btn-danger" onClick=${deleteWorkflow}>Delete</button> <button type="button" class="sw-btn sw-btn--danger sw-btn--sm" onClick=${deleteWorkflow}>Delete</button>
</div> </div>
<div class="form-row"> <div class="form-row">
<div class="form-group"><label>Name</label><input name="name" value=${workflow.name || ''} /></div> <div class="form-group"><label>Name</label><input name="name" value=${workflow.name || ''} /></div>
@@ -299,7 +299,7 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
<input type="text" readOnly value=${window.location.origin + (window.__BASE__ || '') + '/api/v1/public/workflows/' + workflow.id + '/start'} <input type="text" readOnly value=${window.location.origin + (window.__BASE__ || '') + '/api/v1/public/workflows/' + workflow.id + '/start'}
style="flex:1;font-size:11px;padding:4px 8px;background:var(--input-bg);border:1px solid var(--border);border-radius:4px;color:var(--text);font-family:var(--mono);" style="flex:1;font-size:11px;padding:4px 8px;background:var(--input-bg);border:1px solid var(--border);border-radius:4px;color:var(--text);font-family:var(--mono);"
onClick=${e => e.target.select()} /> onClick=${e => e.target.select()} />
<button type="button" class="btn-small" onClick=${(e) => { <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${(e) => {
const url = window.location.origin + (window.__BASE__ || '') + '/api/v1/public/workflows/' + workflow.id + '/start'; const url = window.location.origin + (window.__BASE__ || '') + '/api/v1/public/workflows/' + workflow.id + '/start';
navigator.clipboard.writeText(url).then(() => { navigator.clipboard.writeText(url).then(() => {
e.target.textContent = 'Copied!'; e.target.textContent = 'Copied!';
@@ -311,7 +311,7 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
`} `}
<div class="form-row" style="margin-top:16px;gap:8px;"> <div class="form-row" style="margin-top:16px;gap:8px;">
<button type="submit" class="btn-small btn-primary">Save</button> <button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Save</button>
</div> </div>
</form> </form>
@@ -320,7 +320,7 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
<h5 style="margin:0;">Stages (${stages.length})</h5> <h5 style="margin:0;">Stages (${stages.length})</h5>
<div style="flex:1"></div> <div style="flex:1"></div>
<button class="btn-small" onClick=${() => setEditingStage('new')}>+ Add Stage</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setEditingStage('new')}>+ Add Stage</button>
</div> </div>
<div class="admin-list"> <div class="admin-list">
@@ -331,8 +331,8 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
<span class="badge">${s.stage_mode || '\u2014'}</span> <span class="badge">${s.stage_mode || '\u2014'}</span>
<span class="badge">${s.audience || 'team'}</span> <span class="badge">${s.audience || 'team'}</span>
${s.assignment_team_id && html`<span class="badge">team assign</span>`} ${s.assignment_team_id && html`<span class="badge">team assign</span>`}
<button class="btn-small" onClick=${() => setEditingStage(s.id)}>Edit</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setEditingStage(s.id)}>Edit</button>
<button class="btn-small btn-danger" onClick=${() => deleteStage(s.id)}>\u00d7</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => deleteStage(s.id)}>\u00d7</button>
</div> </div>
`)} `)}
${stages.length === 0 && html`<div class="empty-hint">No stages defined</div>`} ${stages.length === 0 && html`<div class="empty-hint">No stages defined</div>`}
@@ -502,10 +502,10 @@ function StageForm({ stage, teams, onSave, onCancel }) {
<span class="toggle-track"></span><span>Auto-advance when complete</span> <span class="toggle-track"></span><span>Auto-advance when complete</span>
</label> </label>
<div class="form-row" style="margin-top:12px;gap:8px;"> <div class="form-row" style="margin-top:12px;gap:8px;">
<button type="button" class="btn-small btn-primary" onClick=${submit}> <button type="button" class="sw-btn sw-btn--primary sw-btn--sm" onClick=${submit}>
${stage ? 'Update' : 'Add Stage'} ${stage ? 'Update' : 'Add Stage'}
</button> </button>
<button type="button" class="btn-small" onClick=${onCancel}>Cancel</button> <button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${onCancel}>Cancel</button>
</div> </div>
</div> </div>
`; `;
@@ -578,10 +578,10 @@ function AssignmentsTab({ teamId }) {
${a.sla_breached && html`<span class="badge badge-danger">SLA</span>`} ${a.sla_breached && html`<span class="badge badge-danger">SLA</span>`}
</div> </div>
<div style="display:flex;gap:4px;"> <div style="display:flex;gap:4px;">
<button class="btn-small" onClick=${() => unclaim(a.id)}>Release</button> <button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => unclaim(a.id)}>Release</button>
<button class="btn-small btn-primary" <button class="sw-btn sw-btn--primary sw-btn--sm"
onClick=${() => { location.href = sw.base + '/w/' + a.channel_id; }}>Open</button> onClick=${() => { location.href = sw.base + '/w/' + a.channel_id; }}>Open</button>
<button class="btn-small btn-success" onClick=${() => complete(a.id)}>Complete</button> <button class="sw-btn sw-btn--success sw-btn--sm" onClick=${() => complete(a.id)}>Complete</button>
</div> </div>
</div> </div>
`)} `)}
@@ -597,7 +597,7 @@ function AssignmentsTab({ teamId }) {
<span class="badge">${a.stage_name || `Stage ${a.stage}`}</span> <span class="badge">${a.stage_name || `Stage ${a.stage}`}</span>
<span class="text-muted" style="font-size:11px;">${_timeAgo(a.created_at)}</span> <span class="text-muted" style="font-size:11px;">${_timeAgo(a.created_at)}</span>
</div> </div>
<button class="btn-small btn-primary" onClick=${() => claim(a.id)}>Claim</button> <button class="sw-btn sw-btn--primary sw-btn--sm" onClick=${() => claim(a.id)}>Claim</button>
</div> </div>
`)} `)}
${unassigned.length === 0 && html`<div class="empty-hint">No available assignments</div>`} ${unassigned.length === 0 && html`<div class="empty-hint">No available assignments</div>`}
@@ -648,11 +648,11 @@ function MonitorTab({ teamId }) {
<span class="text-muted" style="font-size:11px;">Age: ${_formatDuration(inst.age_seconds)}</span> <span class="text-muted" style="font-size:11px;">Age: ${_formatDuration(inst.age_seconds)}</span>
</div> </div>
<div style="display:flex;gap:4px;"> <div style="display:flex;gap:4px;">
<button class="btn-small" <button class="sw-btn sw-btn--secondary sw-btn--sm"
onClick=${() => setExpandedSignoff(expandedSignoff === inst.id ? null : inst.id)}>Signoffs</button> onClick=${() => setExpandedSignoff(expandedSignoff === inst.id ? null : inst.id)}>Signoffs</button>
<button class="btn-small" <button class="sw-btn sw-btn--secondary sw-btn--sm"
onClick=${() => { location.href = sw.base + '/w/' + inst.channel_id; }}>Open</button> onClick=${() => { location.href = sw.base + '/w/' + inst.channel_id; }}>Open</button>
<button class="btn-small btn-danger" onClick=${() => cancelInstance(inst.channel_id)}>Cancel</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => cancelInstance(inst.channel_id)}>Cancel</button>
</div> </div>
${expandedSignoff === inst.id && html`<${SignoffPanel} instanceId=${inst.id} teamId=${teamId} />`} ${expandedSignoff === inst.id && html`<${SignoffPanel} instanceId=${inst.id} teamId=${teamId} />`}
</div> </div>
@@ -703,8 +703,8 @@ function SignoffPanel({ instanceId, teamId }) {
<div style="display:flex;gap:4px;align-items:center;"> <div style="display:flex;gap:4px;align-items:center;">
<input value=${comment} onInput=${e => setComment(e.target.value)} <input value=${comment} onInput=${e => setComment(e.target.value)}
placeholder="Comment (optional)" style="flex:1;font-size:12px;" /> placeholder="Comment (optional)" style="flex:1;font-size:12px;" />
<button class="btn-small btn-success" onClick=${() => submit('approve')}>Approve</button> <button class="sw-btn sw-btn--success sw-btn--sm" onClick=${() => submit('approve')}>Approve</button>
<button class="btn-small btn-danger" onClick=${() => submit('reject')}>Reject</button> <button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => submit('reject')}>Reject</button>
</div> </div>
${signoffs.length > 0 && html` ${signoffs.length > 0 && html`
<div style="margin-top:6px;"> <div style="margin-top:6px;">