reflectt-node 0.1.7 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/defaults/TEAM-ROLES.yaml +317 -5
- package/defaults/gitignore.template +23 -0
- package/dist/agent-config.d.ts +51 -0
- package/dist/agent-config.d.ts.map +1 -0
- package/dist/agent-config.js +129 -0
- package/dist/agent-config.js.map +1 -0
- package/dist/agent-config.test.d.ts +2 -0
- package/dist/agent-config.test.d.ts.map +1 -0
- package/dist/agent-config.test.js +91 -0
- package/dist/agent-config.test.js.map +1 -0
- package/dist/agent-memories.d.ts +58 -0
- package/dist/agent-memories.d.ts.map +1 -0
- package/dist/agent-memories.js +168 -0
- package/dist/agent-memories.js.map +1 -0
- package/dist/agent-memories.test.d.ts +2 -0
- package/dist/agent-memories.test.d.ts.map +1 -0
- package/dist/agent-memories.test.js +327 -0
- package/dist/agent-memories.test.js.map +1 -0
- package/dist/agent-messaging.d.ts +50 -0
- package/dist/agent-messaging.d.ts.map +1 -0
- package/dist/agent-messaging.js +103 -0
- package/dist/agent-messaging.js.map +1 -0
- package/dist/agent-messaging.test.d.ts +2 -0
- package/dist/agent-messaging.test.d.ts.map +1 -0
- package/dist/agent-messaging.test.js +105 -0
- package/dist/agent-messaging.test.js.map +1 -0
- package/dist/agent-runs.d.ts +158 -0
- package/dist/agent-runs.d.ts.map +1 -0
- package/dist/agent-runs.js +514 -0
- package/dist/agent-runs.js.map +1 -0
- package/dist/agent-runs.test.d.ts +2 -0
- package/dist/agent-runs.test.d.ts.map +1 -0
- package/dist/agent-runs.test.js +386 -0
- package/dist/agent-runs.test.js.map +1 -0
- package/dist/approval-queue.test.d.ts +2 -0
- package/dist/approval-queue.test.d.ts.map +1 -0
- package/dist/approval-queue.test.js +118 -0
- package/dist/approval-queue.test.js.map +1 -0
- package/dist/artifact-store.d.ts +55 -0
- package/dist/artifact-store.d.ts.map +1 -0
- package/dist/artifact-store.js +128 -0
- package/dist/artifact-store.js.map +1 -0
- package/dist/artifact-store.test.d.ts +2 -0
- package/dist/artifact-store.test.d.ts.map +1 -0
- package/dist/artifact-store.test.js +119 -0
- package/dist/artifact-store.test.js.map +1 -0
- package/dist/boardHealthWorker.d.ts +32 -0
- package/dist/boardHealthWorker.d.ts.map +1 -1
- package/dist/boardHealthWorker.js +69 -2
- package/dist/boardHealthWorker.js.map +1 -1
- package/dist/buildInfo.d.ts.map +1 -1
- package/dist/buildInfo.js +47 -10
- package/dist/buildInfo.js.map +1 -1
- package/dist/canvas-input.test.d.ts +2 -0
- package/dist/canvas-input.test.d.ts.map +1 -0
- package/dist/canvas-input.test.js +96 -0
- package/dist/canvas-input.test.js.map +1 -0
- package/dist/canvas-render.test.d.ts +2 -0
- package/dist/canvas-render.test.d.ts.map +1 -0
- package/dist/canvas-render.test.js +95 -0
- package/dist/canvas-render.test.js.map +1 -0
- package/dist/capabilities/browser.d.ts +75 -0
- package/dist/capabilities/browser.d.ts.map +1 -0
- package/dist/capabilities/browser.js +172 -0
- package/dist/capabilities/browser.js.map +1 -0
- package/dist/channels.d.ts +1 -1
- package/dist/chat.d.ts +4 -0
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +6 -2
- package/dist/chat.js.map +1 -1
- package/dist/cli.js +41 -14
- package/dist/cli.js.map +1 -1
- package/dist/cloud.d.ts +2 -0
- package/dist/cloud.d.ts.map +1 -1
- package/dist/cloud.js +151 -64
- package/dist/cloud.js.map +1 -1
- package/dist/continuity-loop.d.ts.map +1 -1
- package/dist/continuity-loop.js +297 -29
- package/dist/continuity-loop.js.map +1 -1
- package/dist/cost-enforcement.d.ts +38 -0
- package/dist/cost-enforcement.d.ts.map +1 -0
- package/dist/cost-enforcement.js +84 -0
- package/dist/cost-enforcement.js.map +1 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +131 -0
- package/dist/db.js.map +1 -1
- package/dist/deploy-monitor.d.ts +18 -0
- package/dist/deploy-monitor.d.ts.map +1 -0
- package/dist/deploy-monitor.js +165 -0
- package/dist/deploy-monitor.js.map +1 -0
- package/dist/e2e-loop-proof.test.d.ts +2 -0
- package/dist/e2e-loop-proof.test.d.ts.map +1 -0
- package/dist/e2e-loop-proof.test.js +104 -0
- package/dist/e2e-loop-proof.test.js.map +1 -0
- package/dist/email-sms-send.test.d.ts +2 -0
- package/dist/email-sms-send.test.d.ts.map +1 -0
- package/dist/email-sms-send.test.js +96 -0
- package/dist/email-sms-send.test.js.map +1 -0
- package/dist/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -1
- package/dist/executionSweeper.d.ts +1 -0
- package/dist/executionSweeper.d.ts.map +1 -1
- package/dist/executionSweeper.js +43 -7
- package/dist/executionSweeper.js.map +1 -1
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +17 -3
- package/dist/files.js.map +1 -1
- package/dist/fingerprint.d.ts +30 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +117 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/github-webhook-attribution.d.ts +38 -0
- package/dist/github-webhook-attribution.d.ts.map +1 -0
- package/dist/github-webhook-attribution.js +123 -0
- package/dist/github-webhook-attribution.js.map +1 -0
- package/dist/github-webhook-chat.d.ts +75 -0
- package/dist/github-webhook-chat.d.ts.map +1 -0
- package/dist/github-webhook-chat.js +108 -0
- package/dist/github-webhook-chat.js.map +1 -0
- package/dist/handoff-state.test.d.ts +2 -0
- package/dist/handoff-state.test.d.ts.map +1 -0
- package/dist/handoff-state.test.js +102 -0
- package/dist/handoff-state.test.js.map +1 -0
- package/dist/health.d.ts +9 -0
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js +18 -0
- package/dist/health.js.map +1 -1
- package/dist/host-error-correlation.d.ts +65 -0
- package/dist/host-error-correlation.d.ts.map +1 -0
- package/dist/host-error-correlation.js +123 -0
- package/dist/host-error-correlation.js.map +1 -0
- package/dist/inbox.d.ts.map +1 -1
- package/dist/inbox.js +4 -0
- package/dist/inbox.js.map +1 -1
- package/dist/index.js +76 -11
- package/dist/index.js.map +1 -1
- package/dist/notificationDedupeGuard.d.ts +4 -0
- package/dist/notificationDedupeGuard.d.ts.map +1 -1
- package/dist/notificationDedupeGuard.js +8 -4
- package/dist/notificationDedupeGuard.js.map +1 -1
- package/dist/presence.d.ts +37 -5
- package/dist/presence.d.ts.map +1 -1
- package/dist/presence.js +127 -16
- package/dist/presence.js.map +1 -1
- package/dist/pulse.d.ts +7 -0
- package/dist/pulse.d.ts.map +1 -1
- package/dist/pulse.js +15 -0
- package/dist/pulse.js.map +1 -1
- package/dist/review-sla.d.ts +9 -0
- package/dist/review-sla.d.ts.map +1 -0
- package/dist/review-sla.js +51 -0
- package/dist/review-sla.js.map +1 -0
- package/dist/review-state.d.ts +9 -0
- package/dist/review-state.d.ts.map +1 -0
- package/dist/review-state.js +17 -0
- package/dist/review-state.js.map +1 -0
- package/dist/routing-enforcement.test.d.ts +2 -0
- package/dist/routing-enforcement.test.d.ts.map +1 -0
- package/dist/routing-enforcement.test.js +86 -0
- package/dist/routing-enforcement.test.js.map +1 -0
- package/dist/run-retention.test.d.ts +2 -0
- package/dist/run-retention.test.d.ts.map +1 -0
- package/dist/run-retention.test.js +57 -0
- package/dist/run-retention.test.js.map +1 -0
- package/dist/run-stream.test.d.ts +2 -0
- package/dist/run-stream.test.d.ts.map +1 -0
- package/dist/run-stream.test.js +70 -0
- package/dist/run-stream.test.js.map +1 -0
- package/dist/schedule.d.ts +60 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +176 -0
- package/dist/schedule.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1714 -88
- package/dist/server.js.map +1 -1
- package/dist/suppression-ledger.d.ts.map +1 -1
- package/dist/suppression-ledger.js +12 -3
- package/dist/suppression-ledger.js.map +1 -1
- package/dist/system-loop-state.d.ts +1 -1
- package/dist/system-loop-state.d.ts.map +1 -1
- package/dist/system-loop-state.js +1 -0
- package/dist/system-loop-state.js.map +1 -1
- package/dist/tasks.d.ts +9 -1
- package/dist/tasks.d.ts.map +1 -1
- package/dist/tasks.js +238 -41
- package/dist/tasks.js.map +1 -1
- package/dist/todoHoardingGuard.d.ts +17 -0
- package/dist/todoHoardingGuard.d.ts.map +1 -1
- package/dist/todoHoardingGuard.js +25 -2
- package/dist/todoHoardingGuard.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/usage-tracking.d.ts +26 -0
- package/dist/usage-tracking.d.ts.map +1 -1
- package/dist/usage-tracking.js +91 -4
- package/dist/usage-tracking.js.map +1 -1
- package/dist/webhook-storage.d.ts +50 -0
- package/dist/webhook-storage.d.ts.map +1 -0
- package/dist/webhook-storage.js +102 -0
- package/dist/webhook-storage.js.map +1 -0
- package/dist/webhook-storage.test.d.ts +2 -0
- package/dist/webhook-storage.test.d.ts.map +1 -0
- package/dist/webhook-storage.test.js +86 -0
- package/dist/webhook-storage.test.js.map +1 -0
- package/dist/workflow-templates.d.ts +44 -0
- package/dist/workflow-templates.d.ts.map +1 -0
- package/dist/workflow-templates.js +154 -0
- package/dist/workflow-templates.js.map +1 -0
- package/dist/workflow-templates.test.d.ts +2 -0
- package/dist/workflow-templates.test.d.ts.map +1 -0
- package/dist/workflow-templates.test.js +76 -0
- package/dist/workflow-templates.test.js.map +1 -0
- package/package.json +3 -1
- package/public/dashboard.js +130 -37
- package/public/design-tokens-platform.md +118 -0
- package/public/design-tokens.css +195 -0
- package/public/docs.md +145 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reflectt-node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Coordinate your AI agent team. Shared tasks, memory, reflections, and presence. Self-host for free.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"author": "Team Reflectt",
|
|
45
45
|
"license": "Apache-2.0",
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"@browserbasehq/stagehand": "^3.1.0",
|
|
47
48
|
"@fastify/cors": "^10.0.1",
|
|
48
49
|
"@fastify/multipart": "^9.4.0",
|
|
49
50
|
"@fastify/websocket": "^11.0.1",
|
|
@@ -87,6 +88,7 @@
|
|
|
87
88
|
"README.md"
|
|
88
89
|
],
|
|
89
90
|
"optionalDependencies": {
|
|
91
|
+
"@browserbasehq/stagehand": "^3.1.0",
|
|
90
92
|
"@xenova/transformers": "^2.17.2"
|
|
91
93
|
},
|
|
92
94
|
"overrides": {
|
package/public/dashboard.js
CHANGED
|
@@ -726,9 +726,10 @@ async function loadPresence() {
|
|
|
726
726
|
// ---- Tasks ----
|
|
727
727
|
async function loadTasks(forceFull = false) {
|
|
728
728
|
try {
|
|
729
|
-
|
|
729
|
+
// Force full load if we have no tasks yet (e.g. first load failed)
|
|
730
|
+
const useDelta = !forceFull && lastTaskSync > 0 && allTasks.length > 0;
|
|
730
731
|
const qs = new URLSearchParams();
|
|
731
|
-
qs.set('limit', '
|
|
732
|
+
qs.set('limit', '200');
|
|
732
733
|
if (useDelta) qs.set('updatedSince', String(lastTaskSync));
|
|
733
734
|
|
|
734
735
|
const r = await fetch(BASE + '/tasks?' + qs.toString());
|
|
@@ -1384,16 +1385,8 @@ async function loadSharedArtifacts() {
|
|
|
1384
1385
|
const pinnedRows = pinned.map(p => {
|
|
1385
1386
|
const found = files.find(f => (String(f.name || '')).toLowerCase() === p.name.toLowerCase());
|
|
1386
1387
|
if (!found) {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
<div style="font-size:13px;color:var(--text-bright);font-weight:600">${esc(p.label)}</div>
|
|
1390
|
-
<span class="assignee-tag" style="color:var(--yellow)">missing</span>
|
|
1391
|
-
</div>
|
|
1392
|
-
<div style="font-size:11px;color:var(--text-muted);margin-top:4px">
|
|
1393
|
-
To make this available here, create a symlink in the shared workspace:<br/>
|
|
1394
|
-
<code>ln -s ../RYANS-THOUGHTS.md ~/.openclaw/workspace-shared/process/RYANS-THOUGHTS.md</code>
|
|
1395
|
-
</div>
|
|
1396
|
-
</div>`;
|
|
1388
|
+
// Operator notes are optional. If not configured, do not show an operator-only instruction to end users.
|
|
1389
|
+
return '';
|
|
1397
1390
|
}
|
|
1398
1391
|
const href = '/shared/view?path=' + encodeURIComponent(found.path);
|
|
1399
1392
|
return `<div style="padding:10px 12px;border-bottom:1px solid var(--border-subtle)">
|
|
@@ -1422,9 +1415,13 @@ async function loadSharedArtifacts() {
|
|
|
1422
1415
|
</div>`;
|
|
1423
1416
|
}).join('');
|
|
1424
1417
|
|
|
1418
|
+
const pinnedBox = pinnedRows
|
|
1419
|
+
? `<div style="border:1px solid var(--border-subtle);border-radius:8px;overflow:hidden">${pinnedRows}</div>
|
|
1420
|
+
<div style="height:10px"></div>`
|
|
1421
|
+
: '';
|
|
1422
|
+
|
|
1425
1423
|
body.innerHTML = `
|
|
1426
|
-
|
|
1427
|
-
<div style="height:10px"></div>
|
|
1424
|
+
${pinnedBox}
|
|
1428
1425
|
<div style="font-size:11px;color:var(--text-muted);margin:0 0 8px">Shared workspace directory: <code>process/</code></div>
|
|
1429
1426
|
<div style="border:1px solid var(--border-subtle);border-radius:8px;overflow:hidden">${listRows}</div>
|
|
1430
1427
|
`;
|
|
@@ -1460,6 +1457,7 @@ async function loadHealth() {
|
|
|
1460
1457
|
const team = health.team || { blockers: [], overlaps: [], compliance: null, agents: [] };
|
|
1461
1458
|
const agentsSummary = health.agentsSummary || { agents: [] };
|
|
1462
1459
|
const idleNudgeDebug = health.idleNudgeDebug || null;
|
|
1460
|
+
const scope = team.scope || null;
|
|
1463
1461
|
|
|
1464
1462
|
healthAgentMap = new Map((team.agents || []).map(a => [String(a.agent || '').toLowerCase(), a]));
|
|
1465
1463
|
const workflow = health.workflow || { agents: [] };
|
|
@@ -1528,6 +1526,16 @@ async function loadHealth() {
|
|
|
1528
1526
|
const body = document.getElementById('health-body');
|
|
1529
1527
|
let html = '';
|
|
1530
1528
|
|
|
1529
|
+
if (scope) {
|
|
1530
|
+
const orgHealthLink = scope.orgHealthUrl
|
|
1531
|
+
? ` <a href="${esc(scope.orgHealthUrl)}" target="_blank" rel="noopener">Open org-health</a>`
|
|
1532
|
+
: '';
|
|
1533
|
+
html += `<div class="blocker-item" style="border-left:4px solid var(--yellow);margin-bottom:12px">
|
|
1534
|
+
<div class="blocker-agent">${esc(scope.label || 'Host-local health')}</div>
|
|
1535
|
+
<div class="blocker-text">${esc(scope.message || '')}${orgHealthLink}</div>
|
|
1536
|
+
</div>`;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1531
1539
|
// Agent Health Grid
|
|
1532
1540
|
if (displayAgents.length > 0) {
|
|
1533
1541
|
html += '<div class="health-section"><div class="health-section-title">Agent Status</div><div class="health-grid">';
|
|
@@ -1806,6 +1814,18 @@ function normalizeEpochMs(v) {
|
|
|
1806
1814
|
return v;
|
|
1807
1815
|
}
|
|
1808
1816
|
|
|
1817
|
+
function hasReviewerDecision(task) {
|
|
1818
|
+
const meta = task && task.metadata && typeof task.metadata === 'object' ? task.metadata : null;
|
|
1819
|
+
const decision = meta && meta.reviewer_decision && typeof meta.reviewer_decision === 'object'
|
|
1820
|
+
? meta.reviewer_decision
|
|
1821
|
+
: null;
|
|
1822
|
+
return !!decision;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
function shouldEscalateReviewerSla(task) {
|
|
1826
|
+
return task && task.slaState === 'breach' && !hasReviewerDecision(task);
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1809
1829
|
function renderReviewQueue() {
|
|
1810
1830
|
const panel = document.getElementById('review-queue-panel');
|
|
1811
1831
|
const body = document.getElementById('review-queue-body');
|
|
@@ -1817,11 +1837,23 @@ function renderReviewQueue() {
|
|
|
1817
1837
|
const validating = allTasks
|
|
1818
1838
|
.filter(t => t.status === 'validating')
|
|
1819
1839
|
.map(t => {
|
|
1820
|
-
const
|
|
1840
|
+
const meta = t.metadata || {};
|
|
1841
|
+
const reviewState = typeof meta.review_state === 'string' ? meta.review_state : '';
|
|
1842
|
+
const reviewerDecision = meta.reviewer_decision;
|
|
1843
|
+
|
|
1844
|
+
// If reviewer has acted (needs_author or reviewer_decision recorded), the ball is with the assignee.
|
|
1845
|
+
// We still track an SLA timer, but it should page the assignee (author), not the reviewer.
|
|
1846
|
+
const waitOn = (reviewState === 'needs_author' || reviewerDecision != null) ? 'author' : 'reviewer';
|
|
1847
|
+
|
|
1848
|
+
const rawEntered = waitOn === 'author'
|
|
1849
|
+
? (reviewerDecision && reviewerDecision.decidedAt) || meta.review_last_activity_at || meta.entered_validating_at || t.updatedAt || t.createdAt
|
|
1850
|
+
: meta.entered_validating_at || t.updatedAt || t.createdAt;
|
|
1851
|
+
|
|
1821
1852
|
const enteredAt = normalizeEpochMs(rawEntered) || now;
|
|
1822
1853
|
const timeInReview = Math.min(Math.max(0, now - enteredAt), MAX_REVIEW_MS);
|
|
1823
1854
|
const slaState = getReviewSlaState(timeInReview);
|
|
1824
|
-
|
|
1855
|
+
|
|
1856
|
+
return { ...t, timeInReview, slaState, enteredAt, waitOn, reviewState, hasReviewerDecision: reviewerDecision != null };
|
|
1825
1857
|
})
|
|
1826
1858
|
.sort((a, b) => {
|
|
1827
1859
|
// Breaches first, then by time descending
|
|
@@ -1831,6 +1863,9 @@ function renderReviewQueue() {
|
|
|
1831
1863
|
return b.timeInReview - a.timeInReview;
|
|
1832
1864
|
});
|
|
1833
1865
|
|
|
1866
|
+
const reviewerQueue = validating.filter(t => t.waitOn === 'reviewer');
|
|
1867
|
+
const authorQueue = validating.filter(t => t.waitOn === 'author');
|
|
1868
|
+
|
|
1834
1869
|
if (validating.length === 0) {
|
|
1835
1870
|
panel.style.display = '';
|
|
1836
1871
|
count.textContent = '';
|
|
@@ -1846,16 +1881,29 @@ function renderReviewQueue() {
|
|
|
1846
1881
|
}
|
|
1847
1882
|
|
|
1848
1883
|
panel.style.display = '';
|
|
1849
|
-
|
|
1850
|
-
|
|
1884
|
+
const reviewerBreachCount = reviewerQueue.filter(t => t.slaState === 'breach').length;
|
|
1885
|
+
const authorBreachCount = authorQueue.filter(t => t.slaState === 'breach').length;
|
|
1886
|
+
|
|
1887
|
+
// Primary count is "awaiting reviewer" — author-wait tasks are tracked separately.
|
|
1888
|
+
count.textContent = reviewerQueue.length + ' awaiting review';
|
|
1889
|
+
|
|
1890
|
+
// Update sidebar badge (reviewer queue only)
|
|
1851
1891
|
const navReviewBadge = document.getElementById('nav-review-count');
|
|
1852
|
-
if (navReviewBadge) navReviewBadge.textContent =
|
|
1892
|
+
if (navReviewBadge) navReviewBadge.textContent = String(reviewerQueue.length);
|
|
1893
|
+
|
|
1894
|
+
const headerExtra = (reviewerBreachCount > 0 || authorBreachCount > 0)
|
|
1895
|
+
? ' <span style="color:var(--red);font-size:11px;font-weight:600">'
|
|
1896
|
+
+ (reviewerBreachCount > 0 ? (reviewerBreachCount + ' reviewer breach' + (reviewerBreachCount > 1 ? 'es' : '')) : '')
|
|
1897
|
+
+ (reviewerBreachCount > 0 && authorBreachCount > 0 ? ' · ' : '')
|
|
1898
|
+
+ (authorBreachCount > 0 ? (authorBreachCount + ' author breach' + (authorBreachCount > 1 ? 'es' : '')) : '')
|
|
1899
|
+
+ '</span>'
|
|
1900
|
+
: '';
|
|
1853
1901
|
|
|
1854
|
-
const
|
|
1855
|
-
|
|
1856
|
-
? ' <span style="color:var(--red);font-size:11px;font-weight:600">' + breachCount + ' breach' + (breachCount > 1 ? 'es' : '') + '</span>'
|
|
1902
|
+
const authorExtra = authorQueue.length > 0
|
|
1903
|
+
? ' <span style="color:var(--text-muted);font-size:11px;font-weight:500">· ' + authorQueue.length + ' awaiting author</span>'
|
|
1857
1904
|
: '';
|
|
1858
|
-
|
|
1905
|
+
|
|
1906
|
+
count.innerHTML = reviewerQueue.length + ' awaiting review' + authorExtra + headerExtra;
|
|
1859
1907
|
|
|
1860
1908
|
body.innerHTML = validating.map(t => {
|
|
1861
1909
|
const reviewer = t.reviewer || '<span style="color:var(--yellow)">unassigned</span>';
|
|
@@ -1884,33 +1932,40 @@ function renderReviewQueue() {
|
|
|
1884
1932
|
|
|
1885
1933
|
bindTaskLinkHandlers(body);
|
|
1886
1934
|
|
|
1887
|
-
// SLA breach escalation:
|
|
1888
|
-
|
|
1889
|
-
|
|
1935
|
+
// SLA breach escalation: split reviewer-wait vs author-wait so we page the right person.
|
|
1936
|
+
// shouldEscalateReviewerSla() suppresses escalation when reviewer_decision already exists.
|
|
1937
|
+
if (reviewerBreachCount > 0) {
|
|
1938
|
+
escalateReviewerBreaches(reviewerQueue.filter(shouldEscalateReviewerSla));
|
|
1939
|
+
}
|
|
1940
|
+
if (authorBreachCount > 0) {
|
|
1941
|
+
escalateAuthorBreaches(authorQueue.filter(t => t.slaState === 'breach'));
|
|
1890
1942
|
}
|
|
1891
1943
|
}
|
|
1892
1944
|
|
|
1893
|
-
let
|
|
1945
|
+
let lastReviewerEscalationAt = 0;
|
|
1946
|
+
let lastAuthorEscalationAt = 0;
|
|
1894
1947
|
const REVIEW_ESCALATION_COOLDOWN = 20 * 60 * 1000; // 20m
|
|
1895
1948
|
|
|
1896
|
-
async function
|
|
1949
|
+
async function escalateReviewerBreaches(breachedTasks) {
|
|
1897
1950
|
const now = Date.now();
|
|
1898
|
-
if (now -
|
|
1899
|
-
|
|
1951
|
+
if (now - lastReviewerEscalationAt < REVIEW_ESCALATION_COOLDOWN) return;
|
|
1952
|
+
lastReviewerEscalationAt = now;
|
|
1900
1953
|
|
|
1901
1954
|
const lines = breachedTasks.slice(0, 5).map(t => {
|
|
1902
1955
|
const reviewer = t.reviewer || 'unassigned';
|
|
1903
|
-
|
|
1956
|
+
const prUrl = t.metadata?.review_handoff?.pr_url || t.metadata?.review_handoff?.prUrl || '';
|
|
1957
|
+
const prPart = prUrl ? ' — ' + prUrl : '';
|
|
1958
|
+
return '- ' + t.id + ' (' + (t.title || '').slice(0, 50) + ') — reviewer: @' + reviewer + ', waiting ' + formatDuration(t.timeInReview) + prPart;
|
|
1904
1959
|
});
|
|
1905
1960
|
|
|
1906
|
-
const content = '
|
|
1961
|
+
const content = 'Review overdue (reviewer SLA):\n' + lines.join('\n');
|
|
1907
1962
|
|
|
1908
1963
|
try {
|
|
1909
1964
|
await fetch(BASE + '/chat/messages', {
|
|
1910
1965
|
method: 'POST',
|
|
1911
1966
|
headers: { 'Content-Type': 'application/json' },
|
|
1912
1967
|
body: JSON.stringify({
|
|
1913
|
-
from: '
|
|
1968
|
+
from: 'dashboard',
|
|
1914
1969
|
content,
|
|
1915
1970
|
channel: 'general',
|
|
1916
1971
|
timestamp: now
|
|
@@ -1921,6 +1976,36 @@ async function escalateReviewBreaches(breachedTasks) {
|
|
|
1921
1976
|
}
|
|
1922
1977
|
}
|
|
1923
1978
|
|
|
1979
|
+
async function escalateAuthorBreaches(breachedTasks) {
|
|
1980
|
+
const now = Date.now();
|
|
1981
|
+
if (now - lastAuthorEscalationAt < REVIEW_ESCALATION_COOLDOWN) return;
|
|
1982
|
+
lastAuthorEscalationAt = now;
|
|
1983
|
+
|
|
1984
|
+
const lines = breachedTasks.slice(0, 5).map(t => {
|
|
1985
|
+
const assignee = t.assignee || 'unassigned';
|
|
1986
|
+
const prUrl = t.metadata?.review_handoff?.pr_url || t.metadata?.review_handoff?.prUrl || '';
|
|
1987
|
+
const prPart = prUrl ? ' — ' + prUrl : '';
|
|
1988
|
+
return '- ' + t.id + ' (' + (t.title || '').slice(0, 50) + ') — assignee: @' + assignee + ', waiting ' + formatDuration(t.timeInReview) + prPart;
|
|
1989
|
+
});
|
|
1990
|
+
|
|
1991
|
+
const content = 'Author action needed (post-review):\n' + lines.join('\n');
|
|
1992
|
+
|
|
1993
|
+
try {
|
|
1994
|
+
await fetch(BASE + '/chat/messages', {
|
|
1995
|
+
method: 'POST',
|
|
1996
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1997
|
+
body: JSON.stringify({
|
|
1998
|
+
from: 'dashboard',
|
|
1999
|
+
content,
|
|
2000
|
+
channel: 'general',
|
|
2001
|
+
timestamp: now
|
|
2002
|
+
})
|
|
2003
|
+
});
|
|
2004
|
+
} catch (err) {
|
|
2005
|
+
console.error('Failed to escalate author breach:', err);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
|
|
1924
2009
|
// ---- Feedback ----
|
|
1925
2010
|
let feedbackData = null;
|
|
1926
2011
|
|
|
@@ -3020,13 +3105,21 @@ async function checkGettingStarted() {
|
|
|
3020
3105
|
|
|
3021
3106
|
function dismissGettingStarted() {
|
|
3022
3107
|
const panel = document.getElementById('getting-started');
|
|
3023
|
-
if (panel)
|
|
3108
|
+
if (panel) {
|
|
3109
|
+
panel.classList.add('hidden');
|
|
3110
|
+
panel.style.display = 'none'; // belt + suspenders
|
|
3111
|
+
}
|
|
3024
3112
|
try { localStorage.setItem('reflectt-gs-dismissed', '1'); } catch {}
|
|
3025
3113
|
}
|
|
3114
|
+
// Expose globally for onclick handlers (safety net)
|
|
3115
|
+
window.dismissGettingStarted = dismissGettingStarted;
|
|
3116
|
+
window.dismissFirstBootBanner = dismissFirstBootBanner;
|
|
3026
3117
|
|
|
3027
3118
|
updateClock();
|
|
3028
3119
|
setInterval(updateClock, 30000);
|
|
3029
3120
|
checkGettingStarted();
|
|
3121
|
+
// Retry getting-started check after 5s in case health wasn't ready at boot
|
|
3122
|
+
setTimeout(checkGettingStarted, 5000);
|
|
3030
3123
|
refresh();
|
|
3031
3124
|
connectEventStream();
|
|
3032
3125
|
startAdaptiveRefresh();
|
|
@@ -3406,7 +3499,7 @@ function fileIcon(mimeType, name) {
|
|
|
3406
3499
|
}
|
|
3407
3500
|
|
|
3408
3501
|
function formatFileSize(bytes) {
|
|
3409
|
-
if (
|
|
3502
|
+
if (typeof bytes !== 'number' || bytes <= 0) return '0 B';
|
|
3410
3503
|
if (bytes < 1024) return bytes + ' B';
|
|
3411
3504
|
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
|
|
3412
3505
|
return (bytes / 1048576).toFixed(1) + ' MB';
|
|
@@ -3455,7 +3548,7 @@ function renderFiles(files) {
|
|
|
3455
3548
|
return '<div class="file-card" tabindex="0" role="button" aria-label="' + (f.originalName || f.filename) + '">' +
|
|
3456
3549
|
'<div class="thumb" style="display:flex;align-items:center;justify-content:center;height:80px;background:var(--surface-raised);border-radius:var(--radius-sm);overflow:hidden">' + thumb + '</div>' +
|
|
3457
3550
|
'<div class="card-name">' + (f.originalName || f.filename) + '</div>' +
|
|
3458
|
-
'<div class="card-meta">' + formatFileSize(f.
|
|
3551
|
+
'<div class="card-meta">' + formatFileSize(f.sizeBytes) + ' · ' + timeAgo(f.uploadedAt || f.createdAt) + '</div>' +
|
|
3459
3552
|
'<div class="card-actions">' +
|
|
3460
3553
|
'<a href="/files/' + f.id + '" download="' + (f.originalName || f.filename) + '" class="action-btn" aria-label="Download" onclick="event.stopPropagation()">⬇</a>' +
|
|
3461
3554
|
'<button class="action-btn delete" aria-label="Delete" onclick="event.stopPropagation();deleteFile(\'' + f.id + '\')">🗑</button>' +
|
|
@@ -3468,7 +3561,7 @@ function renderFiles(files) {
|
|
|
3468
3561
|
return '<div class="file-list-item" tabindex="0" role="button">' +
|
|
3469
3562
|
'<span class="list-icon">' + icon + '</span>' +
|
|
3470
3563
|
'<span class="list-name">' + (f.originalName || f.filename) + '</span>' +
|
|
3471
|
-
'<span class="list-meta">' + formatFileSize(f.
|
|
3564
|
+
'<span class="list-meta">' + formatFileSize(f.sizeBytes) + '</span>' +
|
|
3472
3565
|
'<span class="list-meta">' + timeAgo(f.uploadedAt || f.createdAt) + '</span>' +
|
|
3473
3566
|
'<div class="list-actions">' +
|
|
3474
3567
|
'<a href="/files/' + f.id + '" download="' + (f.originalName || f.filename) + '" class="action-btn" aria-label="Download" onclick="event.stopPropagation()">⬇</a>' +
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Design Tokens — Platform Mapping
|
|
2
|
+
|
|
3
|
+
Source of truth: `/public/design-tokens.css`
|
|
4
|
+
|
|
5
|
+
## iOS (Swift)
|
|
6
|
+
|
|
7
|
+
```swift
|
|
8
|
+
// Colors.swift — auto-generate from design-tokens.css
|
|
9
|
+
import SwiftUI
|
|
10
|
+
|
|
11
|
+
extension Color {
|
|
12
|
+
static let brandPrimary = Color(hex: "#7C3AED")
|
|
13
|
+
static let brandPrimaryLight = Color(hex: "#A78BFA")
|
|
14
|
+
static let brandPrimaryDark = Color(hex: "#5B21B6")
|
|
15
|
+
|
|
16
|
+
// Canvas states
|
|
17
|
+
static let stateFloor = Color(hex: "#1F2937")
|
|
18
|
+
static let stateListening = Color(hex: "#7C3AED")
|
|
19
|
+
static let stateThinking = Color(hex: "#6366F1")
|
|
20
|
+
static let stateRendering = Color(hex: "#8B5CF6")
|
|
21
|
+
static let stateAmbient = Color(hex: "#374151")
|
|
22
|
+
static let stateDecision = Color(hex: "#F59E0B")
|
|
23
|
+
static let stateUrgent = Color(hex: "#EF4444")
|
|
24
|
+
static let stateHandoff = Color(hex: "#10B981")
|
|
25
|
+
|
|
26
|
+
// Trust
|
|
27
|
+
static let trustActive = Color(hex: "#F87171") // Red 400 — visible without alarming
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Dimensions.swift
|
|
31
|
+
enum Dimension {
|
|
32
|
+
static let tapTargetMin: CGFloat = 44
|
|
33
|
+
static let tapTargetUrgent: CGFloat = 52
|
|
34
|
+
static let orbSizeIdle: CGFloat = 64
|
|
35
|
+
static let orbSizeTranscript: CGFloat = 44
|
|
36
|
+
static let trustIndicatorSize: CGFloat = 10
|
|
37
|
+
static let overrideBarHeight: CGFloat = 52
|
|
38
|
+
static let presenceDotSize: CGFloat = 8
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Animation.swift
|
|
42
|
+
enum Timing {
|
|
43
|
+
static let fast: Double = 0.15
|
|
44
|
+
static let base: Double = 0.25
|
|
45
|
+
static let slow: Double = 0.35
|
|
46
|
+
static let canvas: Double = 0.5
|
|
47
|
+
static let orbGlow: Double = 1.8
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Android (Kotlin)
|
|
52
|
+
|
|
53
|
+
```xml
|
|
54
|
+
<!-- colors.xml -->
|
|
55
|
+
<color name="brand_primary">#FF7C3AED</color>
|
|
56
|
+
<color name="brand_primary_light">#FFA78BFA</color>
|
|
57
|
+
<color name="brand_primary_dark">#FF5B21B6</color>
|
|
58
|
+
|
|
59
|
+
<color name="state_floor">#FF1F2937</color>
|
|
60
|
+
<color name="state_listening">#FF7C3AED</color>
|
|
61
|
+
<color name="state_thinking">#FF6366F1</color>
|
|
62
|
+
<color name="state_rendering">#FF8B5CF6</color>
|
|
63
|
+
<color name="state_ambient">#FF374151</color>
|
|
64
|
+
<color name="state_decision">#FFF59E0B</color>
|
|
65
|
+
<color name="state_urgent">#FFEF4444</color>
|
|
66
|
+
<color name="state_handoff">#FF10B981</color>
|
|
67
|
+
|
|
68
|
+
<color name="trust_active">#FFF87171</color> <!-- Red 400 -->
|
|
69
|
+
|
|
70
|
+
<!-- dimens.xml -->
|
|
71
|
+
<dimen name="tap_target_min">44dp</dimen>
|
|
72
|
+
<dimen name="tap_target_urgent">52dp</dimen>
|
|
73
|
+
<dimen name="orb_size_idle">64dp</dimen>
|
|
74
|
+
<dimen name="orb_size_transcript">44dp</dimen>
|
|
75
|
+
<dimen name="trust_indicator_size">10dp</dimen>
|
|
76
|
+
<dimen name="override_bar_height">52dp</dimen>
|
|
77
|
+
<dimen name="presence_dot_size">8dp</dimen>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```kotlin
|
|
81
|
+
// Timing.kt
|
|
82
|
+
object Timing {
|
|
83
|
+
const val FAST = 150L
|
|
84
|
+
const val BASE = 250L
|
|
85
|
+
const val SLOW = 350L
|
|
86
|
+
const val CANVAS = 500L
|
|
87
|
+
const val ORB_GLOW = 1800L
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Important Notes
|
|
92
|
+
|
|
93
|
+
**Canvas state colors are tint references, not solid fills.** The spec uses these as radial gradient tints. Platform implementations should apply them as gradient bases, not flat backgrounds. For v0 surfaces using solid fills, these values are acceptable approximations.
|
|
94
|
+
|
|
95
|
+
**Trust indicator uses red-400 (#F87171)** — deliberately lighter than error red. Present and visible without being alarming. Both-sensors escalates to red-500 (#EF4444).
|
|
96
|
+
|
|
97
|
+
## Token Categories
|
|
98
|
+
|
|
99
|
+
| Category | Count | Notes |
|
|
100
|
+
|----------|-------|-------|
|
|
101
|
+
| Brand colors | 4 | Purple family |
|
|
102
|
+
| Semantic colors | 8 | Success/warning/error/info + backgrounds |
|
|
103
|
+
| Canvas states | 8 | Maps to state machine spec |
|
|
104
|
+
| Trust | 4 | Mic/camera indicators |
|
|
105
|
+
| Surface/bg | 6 | Light + dark mode |
|
|
106
|
+
| Text | 6 | Primary through inverse |
|
|
107
|
+
| Borders | 4 | Default/hover/focus/error |
|
|
108
|
+
| Typography | 14 | Font family, size, weight, line-height |
|
|
109
|
+
| Spacing | 12 | 0–64px scale |
|
|
110
|
+
| Radius | 6 | 4px–full |
|
|
111
|
+
| Shadows | 6 | sm through glow |
|
|
112
|
+
| Transitions | 9 | Duration + easing |
|
|
113
|
+
| Z-index | 8 | Layering scale |
|
|
114
|
+
| Interactive | 3 | Tap targets + focus ring |
|
|
115
|
+
| Canvas/orb | 5 | Orb dimensions + glow |
|
|
116
|
+
| Override bar | 3 | Height/bg/blur |
|
|
117
|
+
| Presence | 3 | Dot states |
|
|
118
|
+
| **Total** | **~105** | |
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/* ═══════════════════════════════════════════════════════════════════════
|
|
2
|
+
Reflectt Design Tokens v0
|
|
3
|
+
═══════════════════════════════════════════════════════════════════════
|
|
4
|
+
Single source of truth for all visual values across web, iOS, Android.
|
|
5
|
+
Platform implementations should map these to native equivalents:
|
|
6
|
+
- iOS: UIColor extensions + CGFloat constants
|
|
7
|
+
- Android: colors.xml + dimens.xml + attrs.xml
|
|
8
|
+
- Web: CSS custom properties (this file)
|
|
9
|
+
|
|
10
|
+
Usage: @import url('/design-tokens.css');
|
|
11
|
+
Or: <link rel="stylesheet" href="/design-tokens.css">
|
|
12
|
+
═══════════════════════════════════════════════════════════════════════ */
|
|
13
|
+
|
|
14
|
+
:root {
|
|
15
|
+
/* ── Brand Colors ─────────────────────────────────────────────────── */
|
|
16
|
+
--color-brand-primary: #7C3AED; /* Purple 600 — main brand */
|
|
17
|
+
--color-brand-primary-light: #A78BFA; /* Purple 400 — hover/active */
|
|
18
|
+
--color-brand-primary-dark: #5B21B6; /* Purple 800 — pressed */
|
|
19
|
+
--color-brand-accent: #8B5CF6; /* Purple 500 — secondary accent */
|
|
20
|
+
|
|
21
|
+
/* ── Semantic Colors ──────────────────────────────────────────────── */
|
|
22
|
+
--color-success: #10B981; /* Emerald 500 */
|
|
23
|
+
--color-success-bg: #D1FAE5; /* Emerald 100 */
|
|
24
|
+
--color-warning: #F59E0B; /* Amber 500 */
|
|
25
|
+
--color-warning-bg: #FEF3C7; /* Amber 100 */
|
|
26
|
+
--color-error: #EF4444; /* Red 500 */
|
|
27
|
+
--color-error-bg: #FEE2E2; /* Red 100 */
|
|
28
|
+
--color-info: #3B82F6; /* Blue 500 */
|
|
29
|
+
--color-info-bg: #DBEAFE; /* Blue 100 */
|
|
30
|
+
|
|
31
|
+
/* ── Canvas State Colors ──────────────────────────────────────────── */
|
|
32
|
+
/* Maps to the 8-state machine from interface-os-v0-state-machine.html */
|
|
33
|
+
--color-state-floor: #1F2937; /* Gray 800 — resting */
|
|
34
|
+
--color-state-listening: #7C3AED; /* Purple 600 — active listening */
|
|
35
|
+
--color-state-thinking: #6366F1; /* Indigo 500 — processing */
|
|
36
|
+
--color-state-rendering: #8B5CF6; /* Purple 500 — outputting */
|
|
37
|
+
--color-state-ambient: #374151; /* Gray 700 — passive monitoring */
|
|
38
|
+
--color-state-decision: #F59E0B; /* Amber 500 — needs human input */
|
|
39
|
+
--color-state-urgent: #EF4444; /* Red 500 — critical alert */
|
|
40
|
+
--color-state-handoff: #10B981; /* Emerald 500 — transferring control */
|
|
41
|
+
|
|
42
|
+
/* ── Trust Indicator ──────────────────────────────────────────────── */
|
|
43
|
+
--color-trust-mic: #F87171; /* Red 400 — visible without alarming */ /* Red — microphone active */
|
|
44
|
+
--color-trust-camera: #F87171; /* Red — camera active */
|
|
45
|
+
--color-trust-mic-camera: #EF4444; /* Red 500 — both sensors escalated */ /* Red 600 — both active */
|
|
46
|
+
--trust-indicator-size: 10px;
|
|
47
|
+
--trust-indicator-position-top: env(safe-area-inset-top, 8px);
|
|
48
|
+
--trust-indicator-position-left: 12px;
|
|
49
|
+
--trust-indicator-z-index: 9999;
|
|
50
|
+
|
|
51
|
+
/* ── Surface / Background ─────────────────────────────────────────── */
|
|
52
|
+
--color-bg-primary: #FFFFFF;
|
|
53
|
+
--color-bg-secondary: #F9FAFB; /* Gray 50 */
|
|
54
|
+
--color-bg-tertiary: #F3F4F6; /* Gray 100 */
|
|
55
|
+
--color-bg-elevated: #FFFFFF;
|
|
56
|
+
--color-bg-overlay: rgba(0, 0, 0, 0.5);
|
|
57
|
+
--color-bg-canvas: #0F0F0F; /* Near-black for canvas surface */
|
|
58
|
+
|
|
59
|
+
/* ── Text ─────────────────────────────────────────────────────────── */
|
|
60
|
+
--color-text-primary: #111827; /* Gray 900 */
|
|
61
|
+
--color-text-secondary: #6B7280; /* Gray 500 */
|
|
62
|
+
--color-text-tertiary: #9CA3AF; /* Gray 400 */
|
|
63
|
+
--color-text-inverse: #FFFFFF;
|
|
64
|
+
--color-text-link: #7C3AED;
|
|
65
|
+
--color-text-on-brand: #FFFFFF;
|
|
66
|
+
|
|
67
|
+
/* ── Borders ──────────────────────────────────────────────────────── */
|
|
68
|
+
--color-border-default: #E5E7EB; /* Gray 200 */
|
|
69
|
+
--color-border-hover: #D1D5DB; /* Gray 300 */
|
|
70
|
+
--color-border-focus: #7C3AED;
|
|
71
|
+
--color-border-error: #EF4444;
|
|
72
|
+
|
|
73
|
+
/* ── Typography ───────────────────────────────────────────────────── */
|
|
74
|
+
--font-family-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
75
|
+
--font-family-mono: 'SF Mono', SFMono-Regular, ui-monospace, 'Cascadia Code', Menlo, monospace;
|
|
76
|
+
|
|
77
|
+
--font-size-xs: 0.75rem; /* 12px */
|
|
78
|
+
--font-size-sm: 0.875rem; /* 14px */
|
|
79
|
+
--font-size-base: 1rem; /* 16px */
|
|
80
|
+
--font-size-lg: 1.125rem; /* 18px */
|
|
81
|
+
--font-size-xl: 1.25rem; /* 20px */
|
|
82
|
+
--font-size-2xl: 1.5rem; /* 24px */
|
|
83
|
+
--font-size-3xl: 1.875rem; /* 30px */
|
|
84
|
+
|
|
85
|
+
--font-weight-normal: 400;
|
|
86
|
+
--font-weight-medium: 500;
|
|
87
|
+
--font-weight-semibold: 600;
|
|
88
|
+
--font-weight-bold: 700;
|
|
89
|
+
|
|
90
|
+
--line-height-tight: 1.25;
|
|
91
|
+
--line-height-normal: 1.5;
|
|
92
|
+
--line-height-relaxed: 1.75;
|
|
93
|
+
|
|
94
|
+
/* ── Spacing ──────────────────────────────────────────────────────── */
|
|
95
|
+
--space-0: 0;
|
|
96
|
+
--space-1: 0.25rem; /* 4px */
|
|
97
|
+
--space-2: 0.5rem; /* 8px */
|
|
98
|
+
--space-3: 0.75rem; /* 12px */
|
|
99
|
+
--space-4: 1rem; /* 16px */
|
|
100
|
+
--space-5: 1.25rem; /* 20px */
|
|
101
|
+
--space-6: 1.5rem; /* 24px */
|
|
102
|
+
--space-8: 2rem; /* 32px */
|
|
103
|
+
--space-10: 2.5rem; /* 40px */
|
|
104
|
+
--space-12: 3rem; /* 48px */
|
|
105
|
+
--space-16: 4rem; /* 64px */
|
|
106
|
+
|
|
107
|
+
/* ── Border Radius ────────────────────────────────────────────────── */
|
|
108
|
+
--radius-sm: 4px;
|
|
109
|
+
--radius-md: 8px;
|
|
110
|
+
--radius-lg: 12px;
|
|
111
|
+
--radius-xl: 16px;
|
|
112
|
+
--radius-2xl: 24px;
|
|
113
|
+
--radius-full: 9999px;
|
|
114
|
+
|
|
115
|
+
/* ── Shadows ──────────────────────────────────────────────────────── */
|
|
116
|
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
117
|
+
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
|
118
|
+
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
|
|
119
|
+
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
120
|
+
--shadow-glow-brand: 0 0 20px rgba(124, 58, 237, 0.3);
|
|
121
|
+
--shadow-glow-urgent: 0 0 20px rgba(239, 68, 68, 0.4);
|
|
122
|
+
|
|
123
|
+
/* ── Transitions ──────────────────────────────────────────────────── */
|
|
124
|
+
--transition-fast: 150ms;
|
|
125
|
+
--transition-base: 250ms;
|
|
126
|
+
--transition-slow: 350ms;
|
|
127
|
+
--transition-canvas: 500ms; /* Canvas state transitions */
|
|
128
|
+
|
|
129
|
+
--easing-smooth: cubic-bezier(0.4, 0, 0.2, 1);
|
|
130
|
+
--easing-ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
131
|
+
--easing-ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
132
|
+
--easing-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
133
|
+
--easing-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
134
|
+
|
|
135
|
+
/* ── Z-Index Scale ────────────────────────────────────────────────── */
|
|
136
|
+
--z-base: 0;
|
|
137
|
+
--z-dropdown: 100;
|
|
138
|
+
--z-sticky: 200;
|
|
139
|
+
--z-overlay: 300;
|
|
140
|
+
--z-modal: 400;
|
|
141
|
+
--z-popover: 500;
|
|
142
|
+
--z-toast: 600;
|
|
143
|
+
--z-override-bar: 800; /* Persistent controls */
|
|
144
|
+
--z-trust-indicator: 9999; /* Non-dismissable, always on top */
|
|
145
|
+
|
|
146
|
+
/* ── Interactive Elements ─────────────────────────────────────────── */
|
|
147
|
+
--tap-target-min: 44px; /* WCAG minimum */
|
|
148
|
+
--tap-target-urgent: 52px; /* Enlarged in urgent state */
|
|
149
|
+
--focus-ring: 0 0 0 3px rgba(124, 58, 237, 0.4);
|
|
150
|
+
|
|
151
|
+
/* ── Canvas / Orb ─────────────────────────────────────────────────── */
|
|
152
|
+
--orb-size-idle: 64px;
|
|
153
|
+
--orb-size-active: 64px;
|
|
154
|
+
--orb-size-transcript: 44px; /* Shrinks when transcript appears */
|
|
155
|
+
--orb-glow-duration: 1.8s;
|
|
156
|
+
--orb-color: var(--color-brand-primary);
|
|
157
|
+
|
|
158
|
+
/* ── Override Bar ─────────────────────────────────────────────────── */
|
|
159
|
+
--override-bar-height: 52px;
|
|
160
|
+
--override-bar-bg: rgba(0, 0, 0, 0.85);
|
|
161
|
+
--override-bar-blur: 20px;
|
|
162
|
+
|
|
163
|
+
/* ── Presence Dot ─────────────────────────────────────────────────── */
|
|
164
|
+
--presence-dot-size: 8px;
|
|
165
|
+
--presence-dot-active: #10B981; /* Online */
|
|
166
|
+
--presence-dot-idle: #F59E0B; /* Away */
|
|
167
|
+
--presence-dot-offline: #6B7280; /* Disconnected */
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* ── Dark Mode ──────────────────────────────────────────────────────── */
|
|
171
|
+
@media (prefers-color-scheme: dark) {
|
|
172
|
+
:root {
|
|
173
|
+
--color-bg-primary: #111827;
|
|
174
|
+
--color-bg-secondary: #1F2937;
|
|
175
|
+
--color-bg-tertiary: #374151;
|
|
176
|
+
--color-bg-elevated: #1F2937;
|
|
177
|
+
--color-bg-overlay: rgba(0, 0, 0, 0.7);
|
|
178
|
+
|
|
179
|
+
--color-text-primary: #F9FAFB;
|
|
180
|
+
--color-text-secondary: #9CA3AF;
|
|
181
|
+
--color-text-tertiary: #6B7280;
|
|
182
|
+
|
|
183
|
+
--color-border-default: #374151;
|
|
184
|
+
--color-border-hover: #4B5563;
|
|
185
|
+
|
|
186
|
+
--color-success-bg: rgba(16, 185, 129, 0.15);
|
|
187
|
+
--color-warning-bg: rgba(245, 158, 11, 0.15);
|
|
188
|
+
--color-error-bg: rgba(239, 68, 68, 0.15);
|
|
189
|
+
--color-info-bg: rgba(59, 130, 246, 0.15);
|
|
190
|
+
|
|
191
|
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
192
|
+
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
|
193
|
+
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4);
|
|
194
|
+
}
|
|
195
|
+
}
|