clementine-agent 1.3.1 → 1.4.0

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.
Files changed (2) hide show
  1. package/dist/cli/dashboard.js +400 -149
  2. package/package.json +1 -1
@@ -2513,6 +2513,37 @@ export async function cmdDashboard(opts) {
2513
2513
  res.status(500).json({ error: 'mcp-discovery failed', detail: String(err) });
2514
2514
  }
2515
2515
  });
2516
+ app.delete('/api/builder/workflows/:id', async (req, res) => {
2517
+ try {
2518
+ const id = decodeURIComponent(req.params.id);
2519
+ const [{ readWorkflow, parseBuilderId }, { emitBuilderEvent }] = await Promise.all([
2520
+ import('../dashboard/builder/serializer.js'),
2521
+ import('../dashboard/builder/events.js'),
2522
+ ]);
2523
+ const parsed = parseBuilderId(id);
2524
+ if (!parsed) {
2525
+ res.status(400).json({ error: 'Bad id' });
2526
+ return;
2527
+ }
2528
+ if (parsed.origin === 'cron') {
2529
+ res.status(400).json({ error: 'Cron entries: use workflow_set_enabled false (or edit CRON.md directly).' });
2530
+ return;
2531
+ }
2532
+ const wf = readWorkflow(id);
2533
+ if (!wf) {
2534
+ res.status(404).json({ error: 'Not found' });
2535
+ return;
2536
+ }
2537
+ const { unlinkSync, existsSync } = await import('node:fs');
2538
+ if (wf.sourceFile && existsSync(wf.sourceFile))
2539
+ unlinkSync(wf.sourceFile);
2540
+ emitBuilderEvent({ type: 'workflow:deleted', workflowId: id });
2541
+ res.json({ ok: true });
2542
+ }
2543
+ catch (err) {
2544
+ res.status(500).json({ error: String(err) });
2545
+ }
2546
+ });
2516
2547
  app.post('/api/builder/workflows/:id/save-from-drawflow', async (req, res) => {
2517
2548
  try {
2518
2549
  const id = decodeURIComponent(req.params.id);
@@ -8120,20 +8151,24 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
8120
8151
  </script>
8121
8152
  <link rel="icon" href="/icon.svg" type="image/svg+xml">
8122
8153
  <link rel="stylesheet" href="/static/drawflow.min.css">
8154
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8155
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
8156
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap">
8123
8157
  <title>${name} Command Center</title>
8124
8158
  <style>
8125
8159
  :root {
8126
- --bg-primary: #f5f6f8;
8160
+ /* ── Slate palette (chromatic, not pure neutral) ── */
8161
+ --bg-primary: #f8fafc;
8127
8162
  --bg-secondary: #ffffff;
8128
- --bg-card: rgba(255,255,255,0.95);
8129
- --bg-hover: #eef1f5;
8130
- --bg-input: #f0f2f5;
8131
- --bg-tertiary: #ebedf0;
8132
- --border: #d8dde5;
8133
- --border-light: #c5ccd6;
8134
- --text-primary: #1a1a2e;
8135
- --text-secondary: #5a6070;
8136
- --text-muted: #8a92a0;
8163
+ --bg-card: rgba(255,255,255,0.96);
8164
+ --bg-hover: #f1f5f9;
8165
+ --bg-input: #f8fafc;
8166
+ --bg-tertiary: #e2e8f0;
8167
+ --border: #cbd5e1;
8168
+ --border-light: #e2e8f0;
8169
+ --text-primary: #0f172a;
8170
+ --text-secondary: #475569;
8171
+ --text-muted: #94a3b8;
8137
8172
  --accent: #4d9eff;
8138
8173
  --accent-glow: rgba(43, 125, 233, 0.10);
8139
8174
  --purple: #7c3aed;
@@ -8142,56 +8177,122 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
8142
8177
  --clementine-dark: #e67a10;
8143
8178
  --clementine-glow: rgba(255, 140, 33, 0.10);
8144
8179
  --clementine-bg: rgba(255, 140, 33, 0.08);
8145
- --green: #2ea043;
8146
- --green-bg: rgba(46, 160, 67, 0.12);
8147
- --red: #e5534b;
8148
- --red-bg: rgba(229, 83, 75, 0.12);
8149
- --yellow: #d4a72c;
8150
- --yellow-bg: rgba(212, 167, 44, 0.12);
8151
- --orange: #f0883e;
8152
- --shadow-sm: 0 2px 8px rgba(0,0,0,0.06);
8153
- --shadow-md: 0 8px 24px rgba(0,0,0,0.12);
8154
- --sidebar-w: 220px;
8155
- --header-h: 56px;
8156
- --radius: 10px;
8157
- --radius-sm: 5px;
8180
+ --green: #16a34a;
8181
+ --green-bg: rgba(22, 163, 74, 0.10);
8182
+ --red: #ef4444;
8183
+ --red-bg: rgba(239, 68, 68, 0.10);
8184
+ --yellow: #ca8a04;
8185
+ --yellow-bg: rgba(202, 138, 4, 0.10);
8186
+ /* ── Shadows (softer + more elevated) ── */
8187
+ --shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.04);
8188
+ --shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
8189
+ --shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08), 0 2px 4px rgba(15, 23, 42, 0.06);
8190
+ --shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.12), 0 4px 8px rgba(15, 23, 42, 0.08);
8191
+ /* ── Layout ── */
8192
+ --sidebar-w: 200px;
8193
+ --header-h: 52px;
8194
+ /* ── Radius scale (tightened) ── */
8195
+ --radius-xs: 4px;
8196
+ --radius-sm: 6px;
8197
+ --radius-md: 8px;
8198
+ --radius-lg: 12px;
8199
+ --radius-xl: 16px;
8200
+ --radius: 8px; /* alias for legacy callers */
8201
+ /* ── Type scale ── */
8202
+ --text-xs: 11px;
8203
+ --text-sm: 12px;
8204
+ --text-base: 13px;
8205
+ --text-md: 14px;
8206
+ --text-lg: 16px;
8207
+ --text-xl: 20px;
8208
+ --text-2xl: 24px;
8209
+ /* ── Motion ── */
8210
+ --motion-fast: 100ms cubic-bezier(0.4, 0, 0.2, 1);
8211
+ --motion: 150ms cubic-bezier(0.4, 0, 0.2, 1);
8212
+ --motion-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);
8213
+ /* ── Focus ring ── */
8214
+ --ring: 0 0 0 2px var(--clementine), 0 0 0 4px rgba(255, 140, 33, 0.18);
8158
8215
  }
8159
8216
  [data-theme="dark"] {
8160
- --bg-primary: #0d1117;
8161
- --bg-secondary: #161b22;
8162
- --bg-card: rgba(22,27,34,0.95);
8163
- --bg-hover: #1c2129;
8164
- --bg-input: #21262d;
8165
- --bg-tertiary: #1c2129;
8166
- --border: #30363d;
8167
- --border-light: #3d444d;
8168
- --text-primary: #e6edf3;
8169
- --text-secondary: #9dacba;
8170
- --text-muted: #7d8590;
8171
- --accent: #58a6ff;
8172
- --accent-glow: rgba(88, 166, 255, 0.12);
8173
- --purple: #a371f7;
8217
+ --bg-primary: #0b0f17;
8218
+ --bg-secondary: #131923;
8219
+ --bg-card: rgba(19, 25, 35, 0.96);
8220
+ --bg-hover: #1a212d;
8221
+ --bg-input: #1a212d;
8222
+ --bg-tertiary: #1f2733;
8223
+ --border: #2d3748;
8224
+ --border-light: #1f2733;
8225
+ --text-primary: #e2e8f0;
8226
+ --text-secondary: #94a3b8;
8227
+ --text-muted: #64748b;
8228
+ --accent: #60a5fa;
8229
+ --accent-glow: rgba(96, 165, 250, 0.12);
8230
+ --purple: #a78bfa;
8174
8231
  --clementine: #ffa54f;
8175
8232
  --clementine-dark: #ff8c21;
8176
- --clementine-glow: rgba(255, 165, 79, 0.12);
8233
+ --clementine-glow: rgba(255, 165, 79, 0.14);
8177
8234
  --clementine-bg: rgba(255, 165, 79, 0.08);
8178
- --green: #3fb950;
8179
- --green-bg: rgba(63, 185, 80, 0.15);
8180
- --red: #f85149;
8181
- --red-bg: rgba(248, 81, 73, 0.15);
8182
- --yellow: #d29922;
8183
- --yellow-bg: rgba(210, 153, 34, 0.15);
8184
- --orange: #f0883e;
8185
- --shadow-sm: 0 2px 8px rgba(0,0,0,0.3);
8186
- --shadow-md: 0 8px 24px rgba(0,0,0,0.5);
8235
+ --green: #22c55e;
8236
+ --green-bg: rgba(34, 197, 94, 0.14);
8237
+ --red: #f87171;
8238
+ --red-bg: rgba(248, 113, 113, 0.14);
8239
+ --yellow: #eab308;
8240
+ --yellow-bg: rgba(234, 179, 8, 0.14);
8241
+ --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
8242
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3);
8243
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3);
8244
+ --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.6), 0 4px 8px rgba(0, 0, 0, 0.4);
8245
+ --ring: 0 0 0 2px var(--clementine), 0 0 0 4px rgba(255, 165, 79, 0.22);
8246
+ }
8247
+ /* OS-preference dark mode unless user has explicitly chosen a theme */
8248
+ @media (prefers-color-scheme: dark) {
8249
+ :root:not([data-theme]) {
8250
+ --bg-primary: #0b0f17;
8251
+ --bg-secondary: #131923;
8252
+ --bg-card: rgba(19, 25, 35, 0.96);
8253
+ --bg-hover: #1a212d;
8254
+ --bg-input: #1a212d;
8255
+ --bg-tertiary: #1f2733;
8256
+ --border: #2d3748;
8257
+ --border-light: #1f2733;
8258
+ --text-primary: #e2e8f0;
8259
+ --text-secondary: #94a3b8;
8260
+ --text-muted: #64748b;
8261
+ --accent: #60a5fa;
8262
+ --accent-glow: rgba(96, 165, 250, 0.12);
8263
+ --purple: #a78bfa;
8264
+ --clementine: #ffa54f;
8265
+ --clementine-glow: rgba(255, 165, 79, 0.14);
8266
+ --clementine-bg: rgba(255, 165, 79, 0.08);
8267
+ --green: #22c55e;
8268
+ --green-bg: rgba(34, 197, 94, 0.14);
8269
+ --red: #f87171;
8270
+ --red-bg: rgba(248, 113, 113, 0.14);
8271
+ --yellow: #eab308;
8272
+ --yellow-bg: rgba(234, 179, 8, 0.14);
8273
+ --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
8274
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.3);
8275
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.3);
8276
+ --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.6), 0 4px 8px rgba(0, 0, 0, 0.4);
8277
+ }
8187
8278
  }
8188
8279
  * { margin: 0; padding: 0; box-sizing: border-box; }
8280
+ *:focus-visible {
8281
+ outline: none;
8282
+ box-shadow: var(--ring);
8283
+ border-radius: var(--radius-xs);
8284
+ }
8189
8285
  body {
8190
- font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', system-ui, sans-serif;
8286
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
8287
+ font-feature-settings: 'cv02', 'cv11', 'ss01', 'ss03';
8288
+ -webkit-font-smoothing: antialiased;
8289
+ -moz-osx-font-smoothing: grayscale;
8191
8290
  background: var(--bg-primary);
8192
8291
  color: var(--text-primary);
8193
8292
  min-height: 100vh;
8194
8293
  overflow: hidden;
8294
+ font-size: var(--text-base);
8295
+ line-height: 1.5;
8195
8296
  }
8196
8297
 
8197
8298
  /* ── Layout ─────────────────────────────── */
@@ -8326,41 +8427,58 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
8326
8427
  display: flex;
8327
8428
  align-items: center;
8328
8429
  gap: 10px;
8329
- padding: 8px 12px;
8430
+ padding: 7px 10px;
8330
8431
  border-radius: var(--radius-sm);
8331
- font-size: 13px;
8432
+ font-size: var(--text-base);
8433
+ font-weight: 500;
8332
8434
  color: var(--text-secondary);
8333
8435
  cursor: pointer;
8334
- transition: all 0.15s;
8436
+ transition: background var(--motion), color var(--motion);
8335
8437
  user-select: none;
8438
+ position: relative;
8336
8439
  }
8337
8440
  .nav-item:hover {
8338
8441
  background: var(--bg-hover);
8339
8442
  color: var(--text-primary);
8340
8443
  }
8341
8444
  .nav-item.active {
8342
- background: var(--clementine-glow);
8445
+ background: linear-gradient(90deg, rgba(255,140,33,0.14) 0%, rgba(255,140,33,0.06) 100%);
8343
8446
  color: var(--clementine);
8447
+ font-weight: 600;
8448
+ }
8449
+ .nav-item.active::before {
8450
+ content: '';
8451
+ position: absolute;
8452
+ left: 0;
8453
+ top: 6px;
8454
+ bottom: 6px;
8455
+ width: 3px;
8456
+ background: var(--clementine);
8457
+ border-radius: 0 2px 2px 0;
8344
8458
  }
8345
8459
  .nav-icon {
8460
+ display: inline-flex;
8461
+ align-items: center;
8462
+ justify-content: center;
8346
8463
  width: 18px;
8347
- text-align: center;
8348
- font-size: 14px;
8464
+ height: 18px;
8349
8465
  flex-shrink: 0;
8466
+ color: currentColor;
8350
8467
  }
8468
+ .nav-icon .icn { width: 16px; height: 16px; }
8351
8469
  .nav-badge {
8352
8470
  margin-left: auto;
8353
8471
  background: var(--bg-hover);
8354
8472
  color: var(--text-muted);
8355
- font-size: 10px;
8473
+ font-size: var(--text-xs);
8356
8474
  font-weight: 600;
8357
- padding: 2px 7px;
8475
+ padding: 1px 7px;
8358
8476
  border-radius: 10px;
8359
8477
  min-width: 18px;
8360
8478
  text-align: center;
8361
8479
  }
8362
8480
  .nav-item.active .nav-badge {
8363
- background: var(--clementine-glow);
8481
+ background: rgba(255,140,33,0.18);
8364
8482
  color: var(--clementine);
8365
8483
  }
8366
8484
 
@@ -8379,44 +8497,47 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
8379
8497
  letter-spacing: -0.02em;
8380
8498
  }
8381
8499
 
8382
- /* ── Standard page header (.page-head) — applied to most info pages ── */
8500
+ /* ── Standard page header (.page-head) — refined for v1.4 ── */
8383
8501
  .page-head {
8384
8502
  display: flex;
8385
- align-items: flex-start;
8503
+ align-items: center;
8386
8504
  gap: 14px;
8387
- padding: 18px 22px 14px;
8505
+ padding: 20px 24px 16px;
8388
8506
  border-bottom: 1px solid var(--border);
8389
- margin-bottom: 18px;
8507
+ margin-bottom: 20px;
8390
8508
  flex-wrap: wrap;
8509
+ position: relative;
8510
+ background: linear-gradient(180deg, rgba(255,140,33,0.025) 0%, transparent 70%);
8391
8511
  }
8392
8512
  .page-head .icon {
8393
- width: 36px;
8394
- height: 36px;
8395
- border-radius: 8px;
8396
- background: linear-gradient(135deg, rgba(255,140,33,0.15), rgba(255,140,33,0.04));
8513
+ width: 32px;
8514
+ height: 32px;
8515
+ border-radius: var(--radius-sm);
8516
+ background: var(--clementine-bg);
8397
8517
  color: var(--clementine);
8398
8518
  display: flex;
8399
8519
  align-items: center;
8400
8520
  justify-content: center;
8401
- font-size: 20px;
8402
8521
  flex-shrink: 0;
8403
8522
  }
8523
+ .page-head .icon .icn { width: 18px; height: 18px; }
8404
8524
  .page-head .title-block {
8405
8525
  flex: 1;
8406
8526
  min-width: 220px;
8407
8527
  }
8408
8528
  .page-head .title-block h1 {
8409
- font-size: 20px;
8529
+ font-size: var(--text-xl);
8410
8530
  font-weight: 600;
8411
8531
  margin: 0 0 2px;
8412
- letter-spacing: -0.01em;
8532
+ letter-spacing: -0.02em;
8413
8533
  color: var(--text-primary);
8534
+ line-height: 1.2;
8414
8535
  }
8415
8536
  .page-head .title-block .desc {
8416
- font-size: 13px;
8537
+ font-size: var(--text-base);
8417
8538
  color: var(--text-muted);
8418
8539
  margin: 0;
8419
- line-height: 1.4;
8540
+ line-height: 1.45;
8420
8541
  }
8421
8542
  .page-head .actions {
8422
8543
  display: flex;
@@ -8425,7 +8546,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
8425
8546
  flex-wrap: wrap;
8426
8547
  }
8427
8548
  .page-section {
8428
- padding: 0 22px 22px;
8549
+ padding: 0 24px 24px;
8429
8550
  }
8430
8551
  /* ── First-time empty state with CTA (use when there's truly no data yet) ── */
8431
8552
  .empty-cta {
@@ -8514,6 +8635,24 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
8514
8635
  /* Cmd+K palette */
8515
8636
  .cmdk-row:hover { background: var(--bg-hover) !important; }
8516
8637
 
8638
+ /* ── Lucide stroke icons (vendored) ── */
8639
+ .icn {
8640
+ display: inline-block;
8641
+ vertical-align: middle;
8642
+ width: 16px;
8643
+ height: 16px;
8644
+ stroke: currentColor;
8645
+ stroke-width: 2;
8646
+ stroke-linecap: round;
8647
+ stroke-linejoin: round;
8648
+ fill: none;
8649
+ flex-shrink: 0;
8650
+ }
8651
+ .icn-sm { width: 14px; height: 14px; }
8652
+ .icn-md { width: 18px; height: 18px; }
8653
+ .icn-lg { width: 22px; height: 22px; stroke-width: 1.75; }
8654
+ .icn-xl { width: 28px; height: 28px; stroke-width: 1.5; }
8655
+
8517
8656
  /* ── Home layout — chat-first daily driver ─── */
8518
8657
  .home-layout {
8519
8658
  display: grid;
@@ -8863,38 +9002,58 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
8863
9002
  background: currentColor;
8864
9003
  }
8865
9004
 
8866
- /* ── Buttons ────────────────────────────── */
9005
+ /* ── Buttons (v1.4 refresh) ─────────────────── */
8867
9006
  button, .btn {
8868
- background: var(--bg-hover);
9007
+ background: var(--bg-secondary);
8869
9008
  color: var(--text-primary);
8870
- border: 1px solid var(--border-light);
9009
+ border: 1px solid var(--border);
8871
9010
  border-radius: var(--radius-sm);
8872
- padding: 6px 14px;
8873
- font-size: 12px;
9011
+ padding: 7px 14px;
9012
+ font-size: var(--text-base);
8874
9013
  font-weight: 500;
8875
9014
  cursor: pointer;
8876
- transition: all 0.15s;
9015
+ transition: background var(--motion), border-color var(--motion), color var(--motion), transform var(--motion-fast), box-shadow var(--motion);
8877
9016
  font-family: inherit;
9017
+ line-height: 1.2;
9018
+ display: inline-flex;
9019
+ align-items: center;
9020
+ justify-content: center;
9021
+ gap: 6px;
9022
+ white-space: nowrap;
8878
9023
  }
8879
9024
  button:hover, .btn:hover {
8880
- background: var(--border-light);
9025
+ background: var(--bg-hover);
9026
+ border-color: var(--border-light);
8881
9027
  }
8882
- .btn-sm { padding: 4px 10px; font-size: 11px; }
9028
+ button:active, .btn:active { transform: translateY(1px); }
9029
+ .btn-sm { padding: 4px 10px; font-size: var(--text-sm); border-radius: var(--radius-xs); }
9030
+ .btn-lg { padding: 9px 18px; font-size: var(--text-md); }
8883
9031
  .btn-primary {
8884
- background: var(--accent);
8885
- border-color: var(--accent);
9032
+ background: var(--clementine);
9033
+ border-color: var(--clementine);
8886
9034
  color: #fff;
9035
+ box-shadow: var(--shadow-xs);
8887
9036
  }
8888
9037
  .btn-primary:hover {
8889
- background: #3d8ae8;
8890
- border-color: #3d8ae8;
9038
+ background: var(--clementine-dark);
9039
+ border-color: var(--clementine-dark);
9040
+ box-shadow: var(--shadow-sm);
9041
+ }
9042
+ .btn-accent {
9043
+ background: var(--accent);
9044
+ border-color: var(--accent);
9045
+ color: #fff;
9046
+ box-shadow: var(--shadow-xs);
8891
9047
  }
9048
+ .btn-accent:hover { filter: brightness(1.05); }
8892
9049
  .btn-success {
9050
+ background: var(--bg-secondary);
8893
9051
  border-color: var(--green);
8894
9052
  color: var(--green);
8895
9053
  }
8896
9054
  .btn-success:hover { background: var(--green-bg); }
8897
9055
  .btn-danger {
9056
+ background: var(--bg-secondary);
8898
9057
  border-color: var(--red);
8899
9058
  color: var(--red);
8900
9059
  }
@@ -8908,10 +9067,19 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
8908
9067
  background: var(--bg-hover);
8909
9068
  color: var(--text-primary);
8910
9069
  }
9070
+ .btn-icon {
9071
+ padding: 6px;
9072
+ width: 32px;
9073
+ height: 32px;
9074
+ border-radius: var(--radius-sm);
9075
+ }
9076
+ .btn-icon.btn-sm { width: 26px; height: 26px; padding: 4px; }
8911
9077
  .btn-group {
8912
9078
  display: flex;
8913
- gap: 8px;
9079
+ gap: 6px;
8914
9080
  }
9081
+ .btn .icn { width: 14px; height: 14px; }
9082
+ .btn-lg .icn { width: 16px; height: 16px; }
8915
9083
 
8916
9084
  /* ── Tables ─────────────────────────────── */
8917
9085
  table {
@@ -10100,16 +10268,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
10100
10268
  padding-top: 16px;
10101
10269
  border-top: 1px solid var(--border);
10102
10270
  }
10103
- .btn-primary {
10104
- background: var(--accent);
10105
- color: white;
10106
- border: none;
10107
- padding: 8px 20px;
10108
- border-radius: var(--radius);
10109
- font-weight: 600;
10110
- cursor: pointer;
10111
- }
10112
- .btn-primary:hover { opacity: 0.9; }
10271
+ /* Disabled-state hooks for primary buttons (rest is unified above). */
10113
10272
  .btn-primary:disabled { opacity: 0.4; cursor: not-allowed; }
10114
10273
 
10115
10274
  /* ── Desk Stats Strip ─────────────────── */
@@ -11000,33 +11159,40 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
11000
11159
  .toggle-switch input:checked + .toggle-slider { background: var(--green); }
11001
11160
  .toggle-switch input:checked + .toggle-slider::before { transform: translateX(16px); }
11002
11161
 
11003
- /* ── Tab Bar ────────────────────────────── */
11162
+ /* ── Tab Bar (v1.4 refresh) ─────────────────── */
11004
11163
  .tab-bar {
11005
11164
  display: flex;
11006
11165
  border-bottom: 1px solid var(--border);
11007
11166
  margin-bottom: 20px;
11008
- gap: 0;
11167
+ gap: 4px;
11009
11168
  overflow-x: auto;
11169
+ padding: 0 4px;
11010
11170
  }
11011
11171
  .tab-bar button {
11012
11172
  background: none;
11013
11173
  border: none;
11014
- padding: 10px 18px;
11015
- font-size: 13px;
11174
+ padding: 10px 14px;
11175
+ font-size: var(--text-base);
11016
11176
  font-weight: 500;
11017
11177
  color: var(--text-muted);
11018
11178
  cursor: pointer;
11019
11179
  border-bottom: 2px solid transparent;
11020
11180
  font-family: inherit;
11021
- transition: color 0.15s, border-color 0.15s;
11181
+ transition: color var(--motion), border-color var(--motion), background var(--motion);
11022
11182
  white-space: nowrap;
11023
11183
  position: relative;
11184
+ border-radius: var(--radius-xs) var(--radius-xs) 0 0;
11185
+ display: inline-flex;
11186
+ align-items: center;
11187
+ gap: 7px;
11024
11188
  }
11025
- .tab-bar button:hover { color: var(--text-primary); }
11189
+ .tab-bar button:hover { color: var(--text-primary); background: var(--bg-hover); }
11026
11190
  .tab-bar button.active {
11027
- color: var(--accent);
11028
- border-bottom-color: var(--accent);
11191
+ color: var(--clementine);
11192
+ border-bottom-color: var(--clementine);
11193
+ background: transparent;
11029
11194
  }
11195
+ .tab-bar button .icn { width: 14px; height: 14px; }
11030
11196
  .tab-bar .tab-badge {
11031
11197
  display: inline-flex;
11032
11198
  align-items: center;
@@ -11136,39 +11302,60 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
11136
11302
  <!-- Sidebar — 5 destinations only. Sub-pages live as tabs within each. -->
11137
11303
  <nav class="sidebar">
11138
11304
  <div class="nav-section">
11139
- <div class="nav-item active" data-page="home" title="Chat, today, activity">
11140
- <span class="nav-icon">&#127819;</span> Home
11305
+ <div class="nav-item active" data-page="home" data-icon="home" title="Chat, today, activity">
11306
+ <span class="nav-icon"></span> Home
11141
11307
  </div>
11142
- <div class="nav-item" data-page="build" title="Workflows, crons, skills">
11143
- <span class="nav-icon">&#128736;</span> Build
11144
- <span class="nav-badge" id="nav-cron-count">0</span>
11308
+ <div class="nav-item" data-page="build" data-icon="workflow" title="Workflows, crons, skills">
11309
+ <span class="nav-icon"></span> Build
11310
+ <span class="nav-badge" id="nav-cron-count" style="display:none">0</span>
11145
11311
  </div>
11146
- <div class="nav-item" data-page="team" title="Agents, activity, goals">
11147
- <span class="nav-icon">&#128101;</span> Team
11312
+ <div class="nav-item" data-page="team" data-icon="users" title="Agents, activity, goals">
11313
+ <span class="nav-icon"></span> Team
11148
11314
  </div>
11149
- <div class="nav-item" data-page="brain" title="Memory, knowledge, ingestion, health">
11150
- <span class="nav-icon">&#129504;</span> Brain
11315
+ <div class="nav-item" data-page="brain" data-icon="brain" title="Memory, knowledge, ingestion, health">
11316
+ <span class="nav-icon"></span> Brain
11151
11317
  </div>
11152
- <div class="nav-item" data-page="settings" title="Channels, integrations, system">
11153
- <span class="nav-icon">&#9881;</span> Settings
11318
+ <div class="nav-item" data-page="settings" data-icon="settings" title="Channels, integrations, system">
11319
+ <span class="nav-icon"></span> Settings
11154
11320
  </div>
11155
11321
  </div>
11156
11322
  <!-- Per-agent quick-jump (loaded from team-nav helper). -->
11157
- <div class="nav-section" style="margin-top:18px">
11323
+ <div class="nav-section" style="margin-top:14px">
11158
11324
  <div class="nav-section-title">Agents</div>
11159
11325
  <div id="team-nav"></div>
11160
- <div class="team-hire-btn" onclick="showAgentCreateModal()">
11161
- <span style="font-size:14px">+</span> Hire
11326
+ <div class="team-hire-btn" onclick="showAgentCreateModal()" data-icon="plus">
11327
+ <span class="hire-plus"></span> Hire
11162
11328
  </div>
11163
11329
  </div>
11164
11330
  <div style="flex:1"></div>
11165
11331
  <div class="nav-section">
11166
- <div class="nav-item" onclick="openCommandK()" style="font-size:12px;color:var(--text-muted);justify-content:space-between" title="Quick search (Cmd+K)">
11167
- <span><span class="nav-icon">&#128269;</span> Search</span>
11168
- <kbd style="font-size:10px;padding:1px 5px;border:1px solid var(--border);border-radius:3px">&#8984;K</kbd>
11332
+ <div class="nav-item" onclick="openCommandK()" data-icon="search" style="font-size:12px;color:var(--text-muted);justify-content:space-between" title="Quick search (Cmd+K)">
11333
+ <span style="display:inline-flex;align-items:center;gap:8px"><span class="nav-icon"></span> Search</span>
11334
+ <kbd style="font-size:10px;padding:2px 6px;border:1px solid var(--border);border-radius:var(--radius-xs);background:var(--bg-input);color:var(--text-secondary)">&#8984;K</kbd>
11169
11335
  </div>
11170
11336
  </div>
11171
11337
  </nav>
11338
+ <script>
11339
+ // Inject Lucide icons into elements that declared data-icon, after page render.
11340
+ (function hydrateIcons() {
11341
+ function run() {
11342
+ document.querySelectorAll('[data-icon]').forEach(function(el) {
11343
+ if (el.dataset._iconHydrated) return;
11344
+ el.dataset._iconHydrated = '1';
11345
+ var name = el.getAttribute('data-icon');
11346
+ if (!window.LUCIDE || !window.lucide) return;
11347
+ var slot = el.querySelector('.nav-icon, .hire-plus, .icon-slot');
11348
+ if (slot) slot.innerHTML = window.lucide(name, 'icn-md');
11349
+ });
11350
+ }
11351
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', run);
11352
+ else run();
11353
+ // Re-run on dynamic insertion (e.g. agent nav). Polled cheaply.
11354
+ setInterval(run, 1000);
11355
+ // Expose so dynamic markup can call after building DOM.
11356
+ window.hydrateLucideIcons = run;
11357
+ })();
11358
+ </script>
11172
11359
  <div class="sidebar-overlay" id="sidebar-overlay" onclick="toggleSidebar()"></div>
11173
11360
 
11174
11361
  <!-- Content -->
@@ -11317,10 +11504,10 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
11317
11504
  <!-- ═══ Builder Page — Conversational Artifact Creation ═══ -->
11318
11505
  <div class="page" id="page-build">
11319
11506
  <div class="tab-bar" id="build-tabs" style="margin:0;padding:0 18px;background:var(--bg-secondary);border-bottom:1px solid var(--border)">
11320
- <button class="active" data-build-tab="workflows" onclick="switchBuildTab('workflows')">&#128279; Workflows</button>
11321
- <button data-build-tab="crons" onclick="switchBuildTab('crons')">&#9200; Crons <span class="tab-badge" id="build-tab-cron-count" style="display:none">0</span></button>
11322
- <button data-build-tab="skills" onclick="switchBuildTab('skills')">&#128737; Skills <span class="tab-badge" id="build-tab-skill-count" style="display:none">0</span></button>
11323
- <button data-build-tab="templates" onclick="switchBuildTab('templates')">&#128221; Templates</button>
11507
+ <button class="active" data-build-tab="workflows" data-icon="workflow" onclick="switchBuildTab('workflows')"><span class="icon-slot"></span> Workflows</button>
11508
+ <button data-build-tab="crons" data-icon="clock" onclick="switchBuildTab('crons')"><span class="icon-slot"></span> Crons <span class="tab-badge" id="build-tab-cron-count" style="display:none">0</span></button>
11509
+ <button data-build-tab="skills" data-icon="shield" onclick="switchBuildTab('skills')"><span class="icon-slot"></span> Skills <span class="tab-badge" id="build-tab-skill-count" style="display:none">0</span></button>
11510
+ <button data-build-tab="templates" data-icon="fileText" onclick="switchBuildTab('templates')"><span class="icon-slot"></span> Templates</button>
11324
11511
  </div>
11325
11512
  <!-- Builder header strip — persists across tabs (except Templates) -->
11326
11513
  <div id="build-header-strip" style="display:flex;align-items:center;gap:12px;padding:10px 18px;border-bottom:1px solid var(--border)">
@@ -11337,10 +11524,10 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
11337
11524
  <button class="btn-sm" id="builder-test-btn" onclick="testBuilderSkill()" style="background:var(--bg-tertiary);border:1px solid var(--border);color:var(--text-secondary);padding:4px 12px;border-radius:6px;cursor:pointer;font-size:12px;display:none">Test</button>
11338
11525
  <button class="btn-sm btn-primary" id="builder-save-btn" onclick="saveBuilderArtifact()" style="padding:4px 16px;font-size:12px;display:none">Save</button>
11339
11526
  </div>
11340
- <!-- Build tab content area -->
11527
+ <!-- Build tab content area: canvas DOMINATES, chat is a sidebar -->
11341
11528
  <div id="build-tab-workflows" data-build-tabpane="workflows" style="display:flex;flex:1;min-height:0;overflow:hidden">
11342
- <!-- Left: Chat -->
11343
- <div style="flex:1;display:flex;flex-direction:column;border-right:1px solid var(--border)">
11529
+ <!-- Left: Chat sidebar (compact) -->
11530
+ <div id="builder-chat-sidebar" style="width:360px;flex-shrink:0;display:flex;flex-direction:column;border-right:1px solid var(--border);background:var(--bg-secondary)">
11344
11531
  <div id="builder-messages" style="flex:1;overflow-y:auto;padding:16px">
11345
11532
  <div class="empty-state" id="builder-empty-state" style="margin-top:40px">
11346
11533
  <p style="color:var(--text-muted);margin-bottom:12px">Describe what you want to build.</p>
@@ -11371,8 +11558,8 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
11371
11558
  <button class="btn-primary" onclick="sendBuilderChat()" style="padding:10px 18px;border-radius:8px">Send</button>
11372
11559
  </div>
11373
11560
  </div>
11374
- <!-- Right: Live Preview / Canvas + Existing Skills -->
11375
- <div id="builder-right-pane" style="width:520px;display:flex;flex-direction:column;background:var(--bg-secondary)">
11561
+ <!-- Right: Canvas (dominant) + Existing Skills drawer -->
11562
+ <div id="builder-right-pane" style="flex:1;min-width:0;display:flex;flex-direction:column;background:var(--bg-primary)">
11376
11563
  <div style="padding:12px 16px;border-bottom:1px solid var(--border);font-weight:600;font-size:13px;color:var(--text-secondary);display:flex;align-items:center;gap:10px;flex-wrap:wrap">
11377
11564
  <span id="builder-right-pane-title">Live Preview</span>
11378
11565
  <span id="builder-preview-status" style="font-size:11px;color:var(--text-muted)"></span>
@@ -11414,7 +11601,8 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
11414
11601
  <div id="builder-canvas-footer" style="padding:6px 14px;border-top:1px solid var(--border);font-size:11px;color:var(--text-muted);display:flex;gap:14px;align-items:center">
11415
11602
  <span id="builder-canvas-status"></span>
11416
11603
  <span style="flex:1"></span>
11417
- <span id="builder-canvas-id" style="font-family:monospace;opacity:0.6"></span>
11604
+ <button id="builder-delete-btn" onclick="deleteCurrentBuilderWorkflow()" title="Delete this workflow" style="display:none;background:none;border:1px solid transparent;color:var(--red);font-size:11px;cursor:pointer;padding:2px 8px;border-radius:var(--radius-xs)">Delete</button>
11605
+ <span id="builder-canvas-id" style="font-family:'JetBrains Mono',monospace;opacity:0.6"></span>
11418
11606
  </div>
11419
11607
  </div>
11420
11608
  <!-- Existing skills drawer (visible in skill mode) -->
@@ -11533,7 +11721,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
11533
11721
  <!-- ═══ Brain Page (unified: Search + Graph + Stats + Sources + Seed + Runs) ═══ -->
11534
11722
  <div class="page" id="page-brain">
11535
11723
  <div class="page-head">
11536
- <div class="icon">&#129504;</div>
11724
+ <div class="icon icon-slot" data-icon="brain"></div>
11537
11725
  <div class="title-block">
11538
11726
  <h1>Brain</h1>
11539
11727
  <p class="desc">Query what you know, feed new knowledge in, and watch the system learn.</p>
@@ -11544,12 +11732,12 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
11544
11732
  </div>
11545
11733
  </div>
11546
11734
  <div class="tab-bar" id="intelligence-tabs" style="margin:0 0 0 18px">
11547
- <button class="active" onclick="switchTab('intelligence','search')">Memory</button>
11548
- <button onclick="switchTab('intelligence','graph')">Knowledge</button>
11549
- <button onclick="switchTab('intelligence','sources')">Ingestion</button>
11550
- <button onclick="switchTab('intelligence','health')">Health <span class="tab-badge" id="brain-health-badge" style="display:none;background:#ef4444;color:#fff">0</span></button>
11551
- <button onclick="switchTab('intelligence','user-model')">User Model</button>
11552
- <button onclick="switchTab('intelligence','learning')">Learning <span class="tab-badge" id="brain-learning-badge" style="display:none;background:#f59e0b;color:#000">0</span></button>
11735
+ <button class="active" data-icon="database" onclick="switchTab('intelligence','search')"><span class="icon-slot"></span> Memory</button>
11736
+ <button data-icon="sparkles" onclick="switchTab('intelligence','graph')"><span class="icon-slot"></span> Knowledge</button>
11737
+ <button data-icon="folder" onclick="switchTab('intelligence','sources')"><span class="icon-slot"></span> Ingestion</button>
11738
+ <button data-icon="zap" onclick="switchTab('intelligence','health')"><span class="icon-slot"></span> Health <span class="tab-badge" id="brain-health-badge" style="display:none;background:#ef4444;color:#fff">0</span></button>
11739
+ <button data-icon="users" onclick="switchTab('intelligence','user-model')"><span class="icon-slot"></span> User Model</button>
11740
+ <button data-icon="brain" onclick="switchTab('intelligence','learning')"><span class="icon-slot"></span> Learning <span class="tab-badge" id="brain-learning-badge" style="display:none;background:#f59e0b;color:#000">0</span></button>
11553
11741
  <button onclick="switchTab('intelligence','memory')">Stats</button>
11554
11742
  <button onclick="switchTab('intelligence','seed')">Seed</button>
11555
11743
  <button onclick="switchTab('intelligence','runs')">Runs</button>
@@ -12791,7 +12979,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
12791
12979
  <!-- ═══ Team Page — The Office ═══ -->
12792
12980
  <div class="page" id="page-team">
12793
12981
  <div class="page-head">
12794
- <div class="icon">&#128101;</div>
12982
+ <div class="icon icon-slot" data-icon="users"></div>
12795
12983
  <div class="title-block">
12796
12984
  <h1>The Office</h1>
12797
12985
  <p class="desc">Your team of agents — what they're doing, what they're contributing to.</p>
@@ -12804,10 +12992,10 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
12804
12992
  </div>
12805
12993
  </div>
12806
12994
  <div class="tab-bar" id="team-tabs" style="margin:0 0 0 18px">
12807
- <button class="active" onclick="switchTab('team','roster')">Roster</button>
12808
- <button onclick="switchTab('team','activity')">Activity</button>
12809
- <button onclick="switchTab('team','goals')">Goals</button>
12810
- <button onclick="switchTab('team','comms')">Comms</button>
12995
+ <button class="active" data-icon="users" onclick="switchTab('team','roster')"><span class="icon-slot"></span> Roster</button>
12996
+ <button data-icon="list" onclick="switchTab('team','activity')"><span class="icon-slot"></span> Activity</button>
12997
+ <button data-icon="target" onclick="switchTab('team','goals')"><span class="icon-slot"></span> Goals</button>
12998
+ <button data-icon="messageSquare" onclick="switchTab('team','comms')"><span class="icon-slot"></span> Comms</button>
12811
12999
  </div>
12812
13000
  <div id="team-tab-content">
12813
13001
  <div class="tab-pane active" id="tab-team-roster">
@@ -13870,6 +14058,46 @@ var prevAgentSlugs = null;
13870
14058
  // Sub-pages live as tabs within each destination.
13871
14059
  // Old routes redirect once for back-compat.
13872
14060
 
14061
+ // ── Lucide stroke icons (MIT, lucide.dev). Inline so there's no font/asset fetch. ──
14062
+ var LUCIDE = {
14063
+ home: '<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/>',
14064
+ wrench: '<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>',
14065
+ users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
14066
+ brain: '<path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2"/>',
14067
+ settings: '<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2"/><circle cx="12" cy="12" r="3"/>',
14068
+ search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
14069
+ plus: '<path d="M5 12h14"/><path d="M12 5v14"/>',
14070
+ x: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
14071
+ check: '<path d="M20 6 9 17l-5-5"/>',
14072
+ play: '<polygon points="6 3 20 12 6 21 6 3"/>',
14073
+ eye: '<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>',
14074
+ trash: '<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>',
14075
+ pencil: '<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/>',
14076
+ link: '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>',
14077
+ clock: '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
14078
+ shield: '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/>',
14079
+ fileText: '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><polyline points="14 2 14 8 20 8"/><line x1="16" x2="8" y1="13" y2="13"/><line x1="16" x2="8" y1="17" y2="17"/><line x1="10" x2="8" y1="9" y2="9"/>',
14080
+ workflow: '<rect x="3" y="3" width="6" height="6" rx="1"/><rect x="15" y="3" width="6" height="6" rx="1"/><rect x="9" y="15" width="6" height="6" rx="1"/><path d="M6 9v3a2 2 0 0 0 2 2h2"/><path d="M18 9v3a2 2 0 0 1-2 2h-2"/>',
14081
+ bell: '<path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/>',
14082
+ calendar: '<rect width="18" height="18" x="3" y="4" rx="2"/><path d="M16 2v4"/><path d="M8 2v4"/><path d="M3 10h18"/>',
14083
+ list: '<line x1="8" x2="21" y1="6" y2="6"/><line x1="8" x2="21" y1="12" y2="12"/><line x1="8" x2="21" y1="18" y2="18"/><line x1="3" x2="3.01" y1="6" y2="6"/><line x1="3" x2="3.01" y1="12" y2="12"/><line x1="3" x2="3.01" y1="18" y2="18"/>',
14084
+ messageSquare:'<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>',
14085
+ target: '<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/>',
14086
+ folder: '<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/>',
14087
+ refresh: '<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M3 21v-5h5"/>',
14088
+ zap: '<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>',
14089
+ sparkles: '<path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/>',
14090
+ command: '<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3"/>',
14091
+ send: '<path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>',
14092
+ arrowRight: '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
14093
+ tool: '<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>',
14094
+ database: '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14a9 3 0 0 0 18 0V5"/><path d="M3 12a9 3 0 0 0 18 0"/>',
14095
+ };
14096
+ function lucide(name, cls) {
14097
+ var path = LUCIDE[name] || '';
14098
+ return '<svg class="icn ' + (cls || '') + '" viewBox="0 0 24 24" aria-hidden="true">' + path + '</svg>';
14099
+ }
14100
+
13873
14101
  var DESTINATIONS = ['home', 'build', 'team', 'brain', 'settings'];
13874
14102
 
13875
14103
  var ROUTE_REDIRECTS = {
@@ -18313,6 +18541,8 @@ async function openBuilderWorkflow(id) {
18313
18541
 
18314
18542
  var idEl = document.getElementById('builder-canvas-id');
18315
18543
  if (idEl) idEl.textContent = id;
18544
+ var delBtn = document.getElementById('builder-delete-btn');
18545
+ if (delBtn) delBtn.style.display = id.startsWith('workflow:') ? '' : 'none';
18316
18546
 
18317
18547
  var banner = document.getElementById('builder-canvas-banner');
18318
18548
  if (banner) {
@@ -18347,13 +18577,16 @@ function _renderBuilderCanvas(drawflowData) {
18347
18577
  editor.reroute = true;
18348
18578
  editor.editor_mode = 'edit';
18349
18579
  editor.start();
18580
+ // Assign global BEFORE decoration runs — decoration reads node data via
18581
+ // editor.export() to recover stepIds (Drawflow doesn't preserve our
18582
+ // df-* attrs without a template).
18583
+ _builderCanvasEditor = editor;
18350
18584
  try {
18351
18585
  editor.import(drawflowData || { drawflow: { Home: { data: {} } } });
18352
18586
  _decorateBuilderNodes(host, _builderCanvasLastWorkflow);
18353
18587
  } catch (err) {
18354
18588
  host.innerHTML = '<div style="padding:24px;color:var(--red)">Failed to render canvas: ' + esc(String(err)) + '</div>';
18355
18589
  }
18356
- _builderCanvasEditor = editor;
18357
18590
  _bindBuilderCanvasEvents(editor);
18358
18591
  }
18359
18592
 
@@ -18396,16 +18629,13 @@ async function _flushBuilderSave() {
18396
18629
  setTimeout(function() { _builderRecentSaveTokens.delete(saveToken); }, 5000);
18397
18630
  try {
18398
18631
  var data = _builderCanvasEditor.export();
18399
- var r = await apiJson('POST', '/api/builder/workflows/' + encodeURIComponent(_builderCanvasOpenId) + '/save-from-drawflow', { drawflow: data, saveToken: saveToken });
18632
+ // force:true autosave persists partial states (incomplete MCP/channel
18633
+ // configs, dangling deps). Validation surfaces in the banner; never blocks.
18634
+ var r = await apiJson('POST', '/api/builder/workflows/' + encodeURIComponent(_builderCanvasOpenId) + '/save-from-drawflow', { drawflow: data, saveToken: saveToken, force: true });
18400
18635
  if (r.error) {
18401
- if (r.validation && r.validation.issues) {
18402
- _setBuilderSaveStatus('error', r.validation.issues.length + ' validation error' + (r.validation.issues.length === 1 ? '' : 's'));
18403
- } else {
18404
- _setBuilderSaveStatus('error', r.error);
18405
- }
18636
+ _setBuilderSaveStatus('error', r.error);
18406
18637
  } else {
18407
18638
  _setBuilderSaveStatus('saved');
18408
- // Refresh banner from validation if warnings
18409
18639
  _updateBuilderBannerFromValidation(r.validation);
18410
18640
  }
18411
18641
  } catch (err) {
@@ -18516,6 +18746,27 @@ async function validateBuilderCanvas() {
18516
18746
  } catch (err) { toast('Validate failed: ' + err, 'error'); }
18517
18747
  }
18518
18748
 
18749
+ async function deleteCurrentBuilderWorkflow() {
18750
+ if (!_builderCanvasOpenId) return;
18751
+ if (!_builderCanvasOpenId.startsWith('workflow:')) {
18752
+ toast('Cron entries can\\x27t be deleted from here — disable instead.', 'info');
18753
+ return;
18754
+ }
18755
+ var name = _builderCanvasOpenId.replace(/^workflow:/, '');
18756
+ if (!confirm('Delete workflow "' + name + '"? This is permanent.')) return;
18757
+ try {
18758
+ var r = await fetch('/api/builder/workflows/' + encodeURIComponent(_builderCanvasOpenId), {
18759
+ method: 'DELETE',
18760
+ headers: { 'authorization': 'Bearer ' + _dashToken },
18761
+ });
18762
+ if (!r.ok) { var d = await r.json().catch(function() { return {}; }); toast('Delete failed: ' + (d.error || r.status), 'error'); return; }
18763
+ toast('Deleted ' + name, 'success');
18764
+ closeBuilderCanvas();
18765
+ var t = document.querySelector('#build-tabs button.active')?.getAttribute('data-build-tab');
18766
+ refreshBuilderCanvasPicker(t === 'crons' ? 'cron' : 'workflow');
18767
+ } catch (err) { toast('Delete error: ' + err, 'error'); }
18768
+ }
18769
+
18519
18770
  async function dryRunBuilderCanvas() {
18520
18771
  if (!_builderCanvasOpenId) { toast('Open a workflow first', 'info'); return; }
18521
18772
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",