Feat v0.6.11 css dedup #46
50
CHANGELOG.md
50
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 `<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
|
||||
|
||||
Single layout model. Every surface renders inside one containment chain:
|
||||
|
||||
@@ -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
|
||||
↓
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.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;">' +
|
||||
'<button class="btn-small" id="dashDetailClose">Close</button>' +
|
||||
'<button class="btn-small btn-primary" id="dashDetailOpen">Open Chat</button>' +
|
||||
'<button class="sw-btn sw-btn--secondary sw-btn--sm" id="dashDetailClose">Close</button>' +
|
||||
'<button class="sw-btn sw-btn--primary sw-btn--sm" id="dashDetailOpen">Open Chat</button>' +
|
||||
'</div>';
|
||||
|
||||
modal.appendChild(box);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
onSelect=${function (o, r) { setOwner(o); setRepo(r); }} />
|
||||
</div>
|
||||
<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'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -196,7 +196,7 @@
|
||||
<p>Ask your admin to add a <strong>Gitea</strong> connection in
|
||||
<strong>Admin → Connections</strong>, or add a personal one in
|
||||
<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'; }}>
|
||||
Open Settings
|
||||
</button>
|
||||
@@ -449,11 +449,11 @@
|
||||
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) postComment();
|
||||
}} />
|
||||
<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}>
|
||||
${posting ? 'Posting…' : 'Comment'}
|
||||
</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}>
|
||||
${issue.state === 'open' ? 'Close Issue' : 'Reopen Issue'}
|
||||
</button>
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
'Registered via surface manifest', '') +
|
||||
'</div>' +
|
||||
'<div class="hello-actions">' +
|
||||
'<button class="btn-primary" id="helloToast">Show Toast</button>' +
|
||||
'<button class="btn-secondary" id="helloApi">Test API</button>' +
|
||||
'<button class="sw-btn sw-btn--primary sw-btn--md" id="helloToast">Show Toast</button>' +
|
||||
'<button class="sw-btn sw-btn--secondary sw-btn--md" id="helloApi">Test API</button>' +
|
||||
'</div>' +
|
||||
'<pre class="hello-manifest">' + esc(JSON.stringify(manifest, null, 2)) + '</pre>' +
|
||||
'</div>';
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
74
scripts/audit-css-collisions.sh
Normal file
74
scripts/audit-css-collisions.sh
Normal 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 "]"
|
||||
@@ -16,7 +16,7 @@
|
||||
{{if .Multiple}}multiple{{end}}
|
||||
{{if .Accept}}accept="{{.Accept}}"{{end}}
|
||||
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()">
|
||||
Choose files…
|
||||
</button>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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 ───────────────────── */
|
||||
|
||||
|
||||
@@ -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); }
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -102,13 +102,13 @@ function DebugModal({ engine }) {
|
||||
</div>
|
||||
<div class="modal-footer debug-modal-footer">
|
||||
<div class="debug-footer-left">
|
||||
<button class="btn-danger btn-small" 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=${() => { setActiveTab('console'); engine.runDiagnostics(); }}>Diagnostics</button>
|
||||
<button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${onPurge}>Purge Cache</button>
|
||||
</div>
|
||||
<div class="debug-footer-right">
|
||||
<button class="btn-secondary btn-small" onClick=${onClear}>Clear</button>
|
||||
<button class="btn-secondary btn-small" 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=${onClear}>Clear</button>
|
||||
<button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => engine.copyLog()}>Copy</button>
|
||||
<button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => engine.downloadLog()}>Export</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -77,9 +77,9 @@ export default function AuditSection() {
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
`
|
||||
}
|
||||
|
||||
@@ -118,13 +118,13 @@ export default function BackupSection() {
|
||||
return html`
|
||||
<div>
|
||||
<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'}
|
||||
</button>
|
||||
<button class="btn" onclick=${() => createBackup(true)} disabled=${creating || restoring}>
|
||||
${creating ? 'Creating\u2026' : 'Save to Server'}
|
||||
</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'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -149,8 +149,8 @@ export default function BackupSection() {
|
||||
<td>${formatBytes(b.size)}</td>
|
||||
<td>${formatDate(b.created_at)}</td>
|
||||
<td>
|
||||
<button class="btn 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--secondary sw-btn--sm" onclick=${() => downloadBackup(b.name)}>Download</button>
|
||||
<button class="sw-btn sw-btn--danger sw-btn--sm" onclick=${() => deleteBackup(b.name)} style="margin-left:4px;">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
`)}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function BroadcastSection() {
|
||||
<option value="critical">Critical</option>
|
||||
</select>
|
||||
</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>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ export default function ConnectionsSection() {
|
||||
|
||||
return html`
|
||||
<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>
|
||||
${!connTypes.length && html`
|
||||
<span style="color:var(--text-3);font-size:12px;">
|
||||
@@ -165,11 +165,11 @@ export default function ConnectionsSection() {
|
||||
</div>
|
||||
`)}
|
||||
<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}>
|
||||
${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'}
|
||||
</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>
|
||||
`}
|
||||
@@ -196,7 +196,7 @@ export default function ConnectionsSection() {
|
||||
<button class="icon-btn" title="Edit" onClick=${() => openEdit(c)}>
|
||||
\u{270F}\u{FE0F}
|
||||
</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)}>
|
||||
\u{1F5D1}
|
||||
</button>
|
||||
|
||||
@@ -112,11 +112,11 @@ export default function GroupsSection() {
|
||||
if (detail) return html`
|
||||
<div>
|
||||
<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>
|
||||
<span class="text-muted" style="font-size:12px;">${detail.scope}</span>
|
||||
<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 class="settings-section" style="margin-bottom:16px;">
|
||||
@@ -134,7 +134,7 @@ export default function GroupsSection() {
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
<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`
|
||||
<div class="admin-surface-row" key=${m.id || m.user_id}>
|
||||
<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>
|
||||
@@ -160,12 +160,12 @@ export default function GroupsSection() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-top:8px;gap:8px;">
|
||||
<button type="submit" class="btn-small btn-primary">Add</button>
|
||||
<button type="button" class="btn-small" onClick=${() => setShowAddMember(false)}>Cancel</button>
|
||||
<button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Add</button>
|
||||
<button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowAddMember(false)}>Cancel</button>
|
||||
</div>
|
||||
</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>
|
||||
`;
|
||||
@@ -174,7 +174,7 @@ export default function GroupsSection() {
|
||||
<div>
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
||||
<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>
|
||||
|
||||
${showCreate && html`
|
||||
@@ -187,8 +187,8 @@ export default function GroupsSection() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-top:8px;gap:8px;">
|
||||
<button type="submit" class="btn-small btn-primary">Create</button>
|
||||
<button type="button" class="btn-small" onClick=${() => setShowCreate(false)}>Cancel</button>
|
||||
<button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Create</button>
|
||||
<button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowCreate(false)}>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
`}
|
||||
|
||||
@@ -252,10 +252,10 @@ export default function PackagesSection() {
|
||||
options=${TYPE_OPTIONS.map(t => ({ label: t.charAt(0).toUpperCase() + t.slice(1), value: t }))}
|
||||
/>
|
||||
<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'}
|
||||
</button>
|
||||
<button class="btn-small" onClick=${toggleRegistry}>
|
||||
<button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${toggleRegistry}>
|
||||
${registryOpen ? 'Close Registry' : 'Browse Registry'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -276,28 +276,28 @@ export default function PackagesSection() {
|
||||
${pkg.is_system && html`<span class="badge" style="margin-left:4px;">system</span>`}
|
||||
</div>
|
||||
<div class="admin-actions-cell">
|
||||
<button class="btn-small"
|
||||
<button class="sw-btn sw-btn--secondary sw-btn--sm"
|
||||
onClick=${() => 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'}
|
||||
</button>
|
||||
${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'}
|
||||
</button>
|
||||
`}
|
||||
${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'}
|
||||
</button>
|
||||
`}
|
||||
${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`
|
||||
<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>
|
||||
@@ -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;" />
|
||||
</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>
|
||||
@@ -333,7 +333,7 @@ export default function PackagesSection() {
|
||||
${perms.map(p => html`
|
||||
<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>
|
||||
<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;"
|
||||
onClick=${() => p.granted ? revokePerm(pkg.id, p.permission) : grantPerm(pkg.id, p.permission)}>
|
||||
${p.granted ? 'Revoke' : 'Grant'}
|
||||
@@ -345,7 +345,7 @@ export default function PackagesSection() {
|
||||
`)}
|
||||
</div>
|
||||
${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 class="admin-actions-cell">
|
||||
${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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -231,11 +231,11 @@ export default function SettingsSection() {
|
||||
</div>
|
||||
|
||||
<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 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>
|
||||
`;
|
||||
|
||||
@@ -61,7 +61,7 @@ export default function StorageSection() {
|
||||
<div class="settings-section" style="margin-bottom:16px;">
|
||||
<h4>Orphaned Blobs</h4>
|
||||
<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>
|
||||
`}
|
||||
|
||||
|
||||
@@ -94,10 +94,10 @@ export default function TeamsSection() {
|
||||
if (detail) return html`
|
||||
<div>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
${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="admin">admin</option>
|
||||
</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>
|
||||
@@ -132,12 +132,12 @@ export default function TeamsSection() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-top:8px;gap:8px;">
|
||||
<button type="submit" class="btn-small btn-primary">Add</button>
|
||||
<button type="button" class="btn-small" onClick=${() => setShowAddMember(false)}>Cancel</button>
|
||||
<button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Add</button>
|
||||
<button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowAddMember(false)}>Cancel</button>
|
||||
</div>
|
||||
</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>
|
||||
`;
|
||||
@@ -147,7 +147,7 @@ export default function TeamsSection() {
|
||||
<div>
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
||||
<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>
|
||||
|
||||
${showCreate && html`
|
||||
@@ -157,8 +157,8 @@ export default function TeamsSection() {
|
||||
<div class="form-group"><label>Description</label><input name="desc" placeholder="Optional" /></div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-top:8px;gap:8px;">
|
||||
<button type="submit" class="btn-small btn-primary">Create</button>
|
||||
<button type="button" class="btn-small" onClick=${() => setShowCreate(false)}>Cancel</button>
|
||||
<button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Create</button>
|
||||
<button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowCreate(false)}>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
`}
|
||||
|
||||
@@ -103,7 +103,7 @@ export default function UsersSection() {
|
||||
<input placeholder="Search users\u2026" value=${search} onInput=${e => setSearch(e.target.value)} autocomplete="off" />
|
||||
</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>
|
||||
|
||||
${showCreate && html`
|
||||
@@ -117,8 +117,8 @@ export default function UsersSection() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-top:8px;gap:8px;">
|
||||
<button type="submit" class="btn-small btn-primary">Create</button>
|
||||
<button type="button" class="btn-small" onClick=${() => setShowCreate(false)}>Cancel</button>
|
||||
<button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Create</button>
|
||||
<button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowCreate(false)}>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
`}
|
||||
@@ -134,14 +134,14 @@ export default function UsersSection() {
|
||||
</div>
|
||||
<div class="admin-actions-cell">
|
||||
${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
|
||||
? html`<button class="btn-small" 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, false)}>Disable</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="btn-small btn-danger" onClick=${() => deleteUser(u.id, u.username)}>Delete</button>
|
||||
<button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => resetPassword(u.id, u.username)}>Reset PW</button>
|
||||
<button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => deleteUser(u.id, u.username)}>Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
`)}
|
||||
|
||||
@@ -80,10 +80,10 @@ export default function WorkflowsSection() {
|
||||
if (editing) return html`
|
||||
<form onSubmit=${updateWorkflow}>
|
||||
<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>
|
||||
<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 class="form-row">
|
||||
<div class="form-group"><label>Name</label><input name="name" value=${editing.name || ''} /></div>
|
||||
@@ -113,7 +113,7 @@ export default function WorkflowsSection() {
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</form>
|
||||
`;
|
||||
@@ -122,7 +122,7 @@ export default function WorkflowsSection() {
|
||||
<div>
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
||||
<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>
|
||||
|
||||
${showCreate && html`
|
||||
@@ -138,8 +138,8 @@ export default function WorkflowsSection() {
|
||||
</div>
|
||||
<div class="form-group"><label>Description</label><input name="description" placeholder="Optional" /></div>
|
||||
<div class="form-row" style="margin-top:8px;gap:8px;">
|
||||
<button type="submit" class="btn-small btn-primary">Create</button>
|
||||
<button type="button" class="btn-small" onClick=${() => setShowCreate(false)}>Cancel</button>
|
||||
<button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Create</button>
|
||||
<button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowCreate(false)}>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
`}
|
||||
|
||||
@@ -73,6 +73,6 @@ export function AppearanceSection() {
|
||||
</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>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ export function ConnectionsSection() {
|
||||
|
||||
return html`
|
||||
<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>
|
||||
${!connTypes.length && html`
|
||||
<span style="margin-left:8px;color:var(--text-3);font-size:12px;">
|
||||
@@ -169,11 +169,11 @@ export function ConnectionsSection() {
|
||||
</div>
|
||||
`)}
|
||||
<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}>
|
||||
${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'}
|
||||
</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>
|
||||
`}
|
||||
@@ -207,7 +207,7 @@ export function ConnectionsSection() {
|
||||
<button class="icon-btn" title="Edit" onClick=${() => openEdit(c)}>
|
||||
\u{270F}\u{FE0F}
|
||||
</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)}>
|
||||
\u{1F5D1}
|
||||
</button>
|
||||
|
||||
@@ -54,7 +54,7 @@ export function GeneralSection() {
|
||||
If your chosen surface is uninstalled, the global default is used.
|
||||
</span>
|
||||
</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}>
|
||||
${saving ? 'Saving\u2026' : 'Save'}
|
||||
</button>
|
||||
|
||||
@@ -81,7 +81,7 @@ export default function NotificationsSection() {
|
||||
onChange=${() => togglePref(type, enabled)} />
|
||||
<span class="toggle-track"></span>
|
||||
</label>
|
||||
<button class="btn-small btn-ghost"
|
||||
<button class="sw-btn sw-btn--ghost sw-btn--sm"
|
||||
disabled=${isSaving}
|
||||
onClick=${() => resetPref(type)}
|
||||
title="Reset to default">
|
||||
|
||||
@@ -124,12 +124,12 @@ export function ProfileSection() {
|
||||
}
|
||||
</div>
|
||||
<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()}>
|
||||
Upload
|
||||
</button>
|
||||
${avatarUrl && html`
|
||||
<button class="btn-sm btn-secondary" onClick=${removeAvatar}>
|
||||
<button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${removeAvatar}>
|
||||
Remove
|
||||
</button>
|
||||
`}
|
||||
@@ -148,7 +148,7 @@ export function ProfileSection() {
|
||||
<label>Email</label>
|
||||
<input type="email" disabled value=${email} />
|
||||
</div>
|
||||
<button class="btn-md btn-primary" disabled=${savingProfile}
|
||||
<button class="sw-btn sw-btn--primary sw-btn--md" disabled=${savingProfile}
|
||||
onClick=${saveProfile}>
|
||||
${savingProfile ? 'Saving\u2026' : 'Save Profile'}
|
||||
</button>
|
||||
@@ -171,7 +171,7 @@ export function ProfileSection() {
|
||||
<input type="password" autocomplete="new-password"
|
||||
value=${confirmPw} onInput=${e => setConfirmPw(e.target.value)} />
|
||||
</div>
|
||||
<button class="btn-md btn-primary" disabled=${savingPw}
|
||||
<button class="sw-btn sw-btn--primary sw-btn--md" disabled=${savingPw}
|
||||
onClick=${changePassword}>
|
||||
${savingPw ? 'Updating\u2026' : 'Update Password'}
|
||||
</button>
|
||||
|
||||
@@ -77,9 +77,9 @@ export default function ActivitySection({ teamId }) {
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
`
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ export default function ConnectionsSection({ teamId }) {
|
||||
|
||||
return html`
|
||||
<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>
|
||||
</div>
|
||||
|
||||
@@ -157,11 +157,11 @@ export default function ConnectionsSection({ teamId }) {
|
||||
</div>
|
||||
`)}
|
||||
<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}>
|
||||
${saving ? 'Saving\u2026' : editId ? 'Update' : 'Add'}
|
||||
</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>
|
||||
`}
|
||||
@@ -187,7 +187,7 @@ export default function ConnectionsSection({ teamId }) {
|
||||
<button class="icon-btn" title="Edit" onClick=${() => openEdit(c)}>
|
||||
\u{270F}\u{FE0F}
|
||||
</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)}>
|
||||
\u{1F5D1}
|
||||
</button>
|
||||
|
||||
@@ -185,7 +185,7 @@ function TeamAdminSurface() {
|
||||
<span class="settings-topbar-title">
|
||||
${teamName}
|
||||
${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>
|
||||
`}
|
||||
</span>
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function MembersSection({ teamId }) {
|
||||
<div>
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
||||
<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>
|
||||
|
||||
${showAdd && html`
|
||||
@@ -98,8 +98,8 @@ export default function MembersSection({ teamId }) {
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-top:8px;gap:8px;">
|
||||
<button type="submit" class="btn-small btn-primary">Add</button>
|
||||
<button type="button" class="btn-small" onClick=${() => setShowAdd(false)}>Cancel</button>
|
||||
<button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Add</button>
|
||||
<button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowAdd(false)}>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
`}
|
||||
@@ -113,7 +113,7 @@ export default function MembersSection({ teamId }) {
|
||||
onChange=${e => updateRole(m.id || m.user_id, e.target.value)}>
|
||||
${roles.map(r => html`<option key=${r} value=${r}>${r}</option>`)}
|
||||
</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>
|
||||
@@ -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="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
|
||||
<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'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -132,7 +132,7 @@ export default function MembersSection({ teamId }) {
|
||||
<div style="display:flex;gap:8px;align-items:center;">
|
||||
<input value=${newRole} onInput=${e => setNewRole(e.target.value)}
|
||||
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;
|
||||
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`
|
||||
<div class="admin-surface-row" key=${r}>
|
||||
<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);
|
||||
try {
|
||||
await sw.api.put('/api/v1/teams/' + teamId + '/roles', { roles: updated });
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function SettingsSection({ teamId }) {
|
||||
`}
|
||||
|
||||
<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>
|
||||
`;
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function WorkflowsSection({ teamId }) {
|
||||
<div class="admin-tabs" style="display:flex;gap:4px;margin-bottom:16px;">
|
||||
${TABS.map(t => html`
|
||||
<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>
|
||||
`)}
|
||||
</div>
|
||||
@@ -123,8 +123,8 @@ function WorkflowsTab({ teamId }) {
|
||||
<div>
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
||||
<div style="flex:1"></div>
|
||||
<button class="btn-md" onClick=${openAdopt}>Adopt Global</button>
|
||||
<button class="btn-md btn-primary" onClick=${() => setShowCreate(true)}>+ Add</button>
|
||||
<button class="sw-btn sw-btn--secondary sw-btn--md" onClick=${openAdopt}>Adopt Global</button>
|
||||
<button class="sw-btn sw-btn--primary sw-btn--md" onClick=${() => setShowCreate(true)}>+ Add</button>
|
||||
</div>
|
||||
|
||||
${showAdopt && html`
|
||||
@@ -132,7 +132,7 @@ function WorkflowsTab({ teamId }) {
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
|
||||
<strong>Adopt a Global Workflow</strong>
|
||||
<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>
|
||||
${available.length === 0 && html`<div class="empty-hint">No global workflows available</div>`}
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
@@ -163,8 +163,8 @@ function WorkflowsTab({ teamId }) {
|
||||
</div>
|
||||
<div class="form-group"><label>Description</label><input name="description" placeholder="Optional" /></div>
|
||||
<div class="form-row" style="margin-top:8px;gap:8px;">
|
||||
<button type="submit" class="btn-small btn-primary">Create</button>
|
||||
<button type="button" class="btn-small" onClick=${() => setShowCreate(false)}>Cancel</button>
|
||||
<button type="submit" class="sw-btn sw-btn--primary sw-btn--sm">Create</button>
|
||||
<button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setShowCreate(false)}>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
`}
|
||||
@@ -266,11 +266,11 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
|
||||
return html`
|
||||
<form onSubmit=${updateWorkflow}>
|
||||
<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>
|
||||
<div style="flex:1"></div>
|
||||
<button type="button" class="btn-small" onClick=${publishWorkflow}>Publish</button>
|
||||
<button type="button" class="btn-small btn-danger" onClick=${deleteWorkflow}>Delete</button>
|
||||
<button type="button" class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${publishWorkflow}>Publish</button>
|
||||
<button type="button" class="sw-btn sw-btn--danger sw-btn--sm" onClick=${deleteWorkflow}>Delete</button>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<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'}
|
||||
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()} />
|
||||
<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';
|
||||
navigator.clipboard.writeText(url).then(() => {
|
||||
e.target.textContent = 'Copied!';
|
||||
@@ -311,7 +311,7 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
|
||||
`}
|
||||
|
||||
<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>
|
||||
</form>
|
||||
|
||||
@@ -320,7 +320,7 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
|
||||
<h5 style="margin:0;">Stages (${stages.length})</h5>
|
||||
<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 class="admin-list">
|
||||
@@ -331,8 +331,8 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
|
||||
<span class="badge">${s.stage_mode || '\u2014'}</span>
|
||||
<span class="badge">${s.audience || 'team'}</span>
|
||||
${s.assignment_team_id && html`<span class="badge">team assign</span>`}
|
||||
<button class="btn-small" onClick=${() => setEditingStage(s.id)}>Edit</button>
|
||||
<button class="btn-small btn-danger" onClick=${() => deleteStage(s.id)}>\u00d7</button>
|
||||
<button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => setEditingStage(s.id)}>Edit</button>
|
||||
<button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => deleteStage(s.id)}>\u00d7</button>
|
||||
</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>
|
||||
</label>
|
||||
<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'}
|
||||
</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>
|
||||
`;
|
||||
@@ -578,10 +578,10 @@ function AssignmentsTab({ teamId }) {
|
||||
${a.sla_breached && html`<span class="badge badge-danger">SLA</span>`}
|
||||
</div>
|
||||
<div style="display:flex;gap:4px;">
|
||||
<button class="btn-small" onClick=${() => unclaim(a.id)}>Release</button>
|
||||
<button class="btn-small btn-primary"
|
||||
<button class="sw-btn sw-btn--secondary sw-btn--sm" onClick=${() => unclaim(a.id)}>Release</button>
|
||||
<button class="sw-btn sw-btn--primary sw-btn--sm"
|
||||
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>
|
||||
`)}
|
||||
@@ -597,7 +597,7 @@ function AssignmentsTab({ teamId }) {
|
||||
<span class="badge">${a.stage_name || `Stage ${a.stage}`}</span>
|
||||
<span class="text-muted" style="font-size:11px;">${_timeAgo(a.created_at)}</span>
|
||||
</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>
|
||||
`)}
|
||||
${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>
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
${expandedSignoff === inst.id && html`<${SignoffPanel} instanceId=${inst.id} teamId=${teamId} />`}
|
||||
</div>
|
||||
@@ -703,8 +703,8 @@ function SignoffPanel({ instanceId, teamId }) {
|
||||
<div style="display:flex;gap:4px;align-items:center;">
|
||||
<input value=${comment} onInput=${e => setComment(e.target.value)}
|
||||
placeholder="Comment (optional)" style="flex:1;font-size:12px;" />
|
||||
<button class="btn-small btn-success" onClick=${() => submit('approve')}>Approve</button>
|
||||
<button class="btn-small btn-danger" onClick=${() => submit('reject')}>Reject</button>
|
||||
<button class="sw-btn sw-btn--success sw-btn--sm" onClick=${() => submit('approve')}>Approve</button>
|
||||
<button class="sw-btn sw-btn--danger sw-btn--sm" onClick=${() => submit('reject')}>Reject</button>
|
||||
</div>
|
||||
${signoffs.length > 0 && html`
|
||||
<div style="margin-top:6px;">
|
||||
|
||||
Reference in New Issue
Block a user