openclacky 1.2.6 → 1.2.7

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.
@@ -1514,7 +1514,106 @@ body {
1514
1514
  #btn-welcome-new:hover { background: var(--color-button-primary-hover); }
1515
1515
 
1516
1516
  /* ── Chat panel ──────────────────────────────────────────────────────────── */
1517
- #chat-panel { flex: 1; display: flex; flex-direction: column; overflow: hidden; position: relative; }
1517
+ #chat-panel { flex: 1; display: flex; flex-direction: row; overflow: hidden; position: relative; }
1518
+ #chat-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; position: relative; min-width: 0; }
1519
+
1520
+ /* ── Workspace panel (right file browser) ───────────────────────────────── */
1521
+ #workspace-panel {
1522
+ width: 280px;
1523
+ flex-shrink: 0;
1524
+ display: flex;
1525
+ flex-direction: column;
1526
+ border-left: 1px solid var(--color-border-primary);
1527
+ background: var(--color-bg-primary);
1528
+ overflow: hidden;
1529
+ transition: width var(--transition-base);
1530
+ }
1531
+ #workspace-panel.collapsed { width: 0; border-left: none; }
1532
+ #workspace-header {
1533
+ display: flex;
1534
+ align-items: center;
1535
+ justify-content: space-between;
1536
+ height: 2.5rem;
1537
+ min-height: 2.5rem;
1538
+ padding: 0 0.5rem 0 0.75rem;
1539
+ border-bottom: 1px solid var(--color-border-primary);
1540
+ font-size: 0.8125rem;
1541
+ font-weight: 600;
1542
+ color: var(--color-text-primary);
1543
+ }
1544
+ .workspace-header-actions { display: flex; gap: 0.125rem; }
1545
+ .workspace-icon-btn {
1546
+ display: inline-flex;
1547
+ align-items: center;
1548
+ justify-content: center;
1549
+ width: 1.75rem;
1550
+ height: 1.75rem;
1551
+ border: none;
1552
+ background: transparent;
1553
+ color: var(--color-text-secondary);
1554
+ border-radius: var(--radius-sm);
1555
+ cursor: pointer;
1556
+ transition: background-color var(--transition-base), color var(--transition-base);
1557
+ }
1558
+ .workspace-icon-btn:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
1559
+ #workspace-tree {
1560
+ flex: 1;
1561
+ overflow-y: auto;
1562
+ padding: 0.375rem 0.25rem;
1563
+ font-size: 0.8125rem;
1564
+ }
1565
+ .wt-node { user-select: none; }
1566
+ .wt-row {
1567
+ display: flex;
1568
+ align-items: center;
1569
+ gap: 0.25rem;
1570
+ padding: 0.1875rem 0.375rem;
1571
+ border-radius: var(--radius-sm);
1572
+ cursor: pointer;
1573
+ color: var(--color-text-primary);
1574
+ white-space: nowrap;
1575
+ overflow: hidden;
1576
+ }
1577
+ .wt-row:hover { background: var(--color-bg-hover); }
1578
+ .wt-caret {
1579
+ display: inline-flex;
1580
+ width: 0.875rem;
1581
+ flex-shrink: 0;
1582
+ color: var(--color-text-secondary);
1583
+ transition: transform var(--transition-base);
1584
+ }
1585
+ .wt-caret.open { transform: rotate(90deg); }
1586
+ .wt-caret.leaf { visibility: hidden; }
1587
+ .wt-icon { display: inline-flex; flex-shrink: 0; color: var(--color-text-secondary); }
1588
+ .wt-name { overflow: hidden; text-overflow: ellipsis; }
1589
+ .wt-size { margin-left: auto; padding-left: 0.5rem; font-size: 0.6875rem; color: var(--color-text-tertiary, var(--color-text-secondary)); flex-shrink: 0; }
1590
+ .wt-children { margin-left: 0.75rem; }
1591
+ .wt-empty, .wt-loading, .wt-error {
1592
+ padding: 0.375rem 0.625rem;
1593
+ font-size: 0.75rem;
1594
+ color: var(--color-text-secondary);
1595
+ }
1596
+ .wt-error { color: var(--color-danger, #d23); }
1597
+
1598
+ /* Collapsed-state opener tab — floats at the right edge of the chat area */
1599
+ #btn-workspace-open {
1600
+ position: absolute;
1601
+ top: 0.5rem;
1602
+ right: 0.5rem;
1603
+ z-index: 8;
1604
+ display: inline-flex;
1605
+ align-items: center;
1606
+ justify-content: center;
1607
+ width: 2rem;
1608
+ height: 2rem;
1609
+ border: 1px solid var(--color-border-primary);
1610
+ background: var(--color-bg-secondary);
1611
+ color: var(--color-text-secondary);
1612
+ border-radius: var(--radius-sm);
1613
+ cursor: pointer;
1614
+ transition: background-color var(--transition-base), color var(--transition-base);
1615
+ }
1616
+ #btn-workspace-open:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
1518
1617
 
1519
1618
  /* Mobile-only floating back button — replaces the old in-header back button.
1520
1619
  Hidden on desktop; mobile media query enables it. Positioned absolutely so
@@ -1971,6 +2070,14 @@ body {
1971
2070
  opacity: 0.5;
1972
2071
  cursor: not-allowed;
1973
2072
  }
2073
+ .msg-error a {
2074
+ color: var(--color-error);
2075
+ font-weight: 600;
2076
+ text-decoration: underline;
2077
+ }
2078
+ .msg-error a:hover {
2079
+ opacity: 0.8;
2080
+ }
1974
2081
  .msg-success { color: var(--color-success); align-self: flex-start; font-size: 0.8125rem; }
1975
2082
  .tool-name { color: var(--color-warning); font-weight: 600; }
1976
2083
  .progress-msg { color: var(--color-accent-primary); font-size: 0.75rem; align-self: center; }
@@ -2551,6 +2658,99 @@ body {
2551
2658
  color: var(--color-accent-primary);
2552
2659
  }
2553
2660
 
2661
+ .sib-submodel-default-tag {
2662
+ margin-left: 0.5rem;
2663
+ font-size: 0.5625rem;
2664
+ padding: 1px 0.3125rem;
2665
+ border-radius: 3px;
2666
+ background: color-mix(in srgb, var(--color-text-secondary) 15%, transparent);
2667
+ color: var(--color-text-secondary);
2668
+ opacity: 0.8;
2669
+ }
2670
+
2671
+ .sib-submodel-toggle {
2672
+ margin-left: 0.375rem;
2673
+ width: 1.125rem;
2674
+ height: 1.125rem;
2675
+ padding: 0;
2676
+ border: none;
2677
+ border-radius: 4px;
2678
+ background: transparent;
2679
+ color: var(--color-text-secondary);
2680
+ cursor: pointer;
2681
+ display: inline-flex;
2682
+ align-items: center;
2683
+ justify-content: center;
2684
+ transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s ease;
2685
+ }
2686
+ .sib-submodel-toggle:hover {
2687
+ background: color-mix(in srgb, var(--color-accent-primary) 15%, transparent);
2688
+ color: var(--color-accent-primary);
2689
+ }
2690
+ .sib-submodel-toggle[aria-expanded="true"] {
2691
+ background: color-mix(in srgb, var(--color-accent-primary) 18%, transparent);
2692
+ color: var(--color-accent-primary);
2693
+ }
2694
+ .sib-submodel-toggle[aria-expanded="true"] svg {
2695
+ transform: rotate(90deg);
2696
+ }
2697
+ .sib-submodel-toggle svg {
2698
+ transition: transform 0.18s ease;
2699
+ }
2700
+
2701
+ .sib-model-option.submodel-open {
2702
+ background: var(--color-bg-hover);
2703
+ }
2704
+
2705
+ .sib-submodel-panel {
2706
+ position: fixed;
2707
+ min-width: 11rem;
2708
+ max-width: 16rem;
2709
+ background: var(--color-bg-secondary);
2710
+ border: 1px solid var(--color-border-primary);
2711
+ border-radius: 8px;
2712
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
2713
+ z-index: 10000;
2714
+ max-height: 18.75rem;
2715
+ overflow-y: auto;
2716
+ padding: 0.25rem 0;
2717
+ }
2718
+ .sib-submodel-panel-header {
2719
+ padding: 0.375rem 0.875rem 0.4375rem;
2720
+ font-size: 0.625rem;
2721
+ text-transform: uppercase;
2722
+ letter-spacing: 0.06em;
2723
+ color: var(--color-text-secondary);
2724
+ border-bottom: 1px solid var(--color-border-primary);
2725
+ margin-bottom: 0.25rem;
2726
+ }
2727
+ .sib-submodel-row {
2728
+ padding: 0.5rem 0.875rem;
2729
+ font-size: 0.7rem;
2730
+ font-family: var(--font-mono, monospace);
2731
+ color: var(--color-text-primary);
2732
+ cursor: pointer;
2733
+ transition: background-color 0.15s ease;
2734
+ display: flex;
2735
+ align-items: center;
2736
+ justify-content: space-between;
2737
+ gap: 0.5rem;
2738
+ }
2739
+ .sib-submodel-row:hover {
2740
+ background: var(--color-bg-hover);
2741
+ color: var(--color-accent-primary);
2742
+ }
2743
+ .sib-submodel-row.current {
2744
+ background: var(--color-bg-hover);
2745
+ font-weight: 600;
2746
+ color: var(--color-accent-primary);
2747
+ }
2748
+ .sib-submodel-row-name {
2749
+ overflow: hidden;
2750
+ text-overflow: ellipsis;
2751
+ white-space: nowrap;
2752
+ }
2753
+
2554
2754
  /* ── Model switcher benchmark banner & latency column ──────────────────────
2555
2755
  The banner sits at the top of the dropdown with a subtle border so it
2556
2756
  visually separates from the scrollable model list below. The ⚡ button is
@@ -2638,6 +2838,15 @@ body {
2638
2838
  white-space: nowrap;
2639
2839
  max-width: 100%;
2640
2840
  }
2841
+ .sib-model-option .sib-model-name-override {
2842
+ font-size: 0.6875rem;
2843
+ color: var(--color-accent-primary);
2844
+ font-weight: 600;
2845
+ overflow: hidden;
2846
+ text-overflow: ellipsis;
2847
+ white-space: nowrap;
2848
+ max-width: 100%;
2849
+ }
2641
2850
  .sib-model-option .sib-model-right {
2642
2851
  display: inline-flex;
2643
2852
  align-items: center;
@@ -3691,6 +3900,33 @@ body {
3691
3900
  color: var(--color-error);
3692
3901
  background: color-mix(in srgb, var(--color-error) 8%, transparent);
3693
3902
  }
3903
+ a.btn-card-grid-action {
3904
+ text-decoration: none;
3905
+ }
3906
+ .model-card-grid-footer {
3907
+ display: flex;
3908
+ justify-content: flex-end;
3909
+ margin-top: 0.25rem;
3910
+ }
3911
+ .model-card-grid-link {
3912
+ display: inline-flex;
3913
+ align-items: center;
3914
+ gap: 0.25rem;
3915
+ font-size: 0.6875rem;
3916
+ color: var(--color-text-tertiary);
3917
+ text-decoration: none;
3918
+ padding: 0.125rem 0.25rem;
3919
+ border-radius: 4px;
3920
+ transition: color 0.15s;
3921
+ }
3922
+ .model-card-grid-link:hover {
3923
+ color: var(--color-accent-primary);
3924
+ text-decoration: underline;
3925
+ }
3926
+ .model-card-grid-link svg {
3927
+ opacity: 0.7;
3928
+ flex-shrink: 0;
3929
+ }
3694
3930
 
3695
3931
  /* ── Settings Toggle List ───────────────────────────────────────────────────── */
3696
3932
  .settings-toggle-list {
@@ -158,7 +158,6 @@ const Router = (() => {
158
158
  }
159
159
  _setHash(`session/${id}`);
160
160
  $("chat-panel").style.display = "flex";
161
- $("chat-panel").style.flexDirection = "column";
162
161
  Sessions.updateChatHeader(s);
163
162
  Sessions.updateStatusBar(s.status);
164
163
  Sessions.updateInfoBar(s);
@@ -81,6 +81,14 @@ const I18n = (() => {
81
81
  "sessions.actions.downloadHint": "for debugging",
82
82
  "sib.dir.tooltip": "Click to change directory",
83
83
  "sib.dir.changePrompt": "Change working directory:",
84
+ "workspace.title": "Workspace",
85
+ "workspace.expand": "Open workspace",
86
+ "workspace.collapse": "Collapse workspace",
87
+ "workspace.refresh": "Refresh",
88
+ "workspace.empty": "This folder is empty",
89
+ "workspace.loading": "Loading…",
90
+ "workspace.error": "Failed to load files",
91
+ "workspace.downloadFailed": "Download failed",
84
92
  "sib.model.tooltip": "Click to switch model",
85
93
  "sib.model.tooltip.busy": "Model switching is disabled while the agent is responding",
86
94
  "sib.signal.tooltip": "Recent LLM latency",
@@ -507,6 +515,7 @@ const I18n = (() => {
507
515
  "settings.models.btn.saved": "Saved ✓",
508
516
  "settings.models.btn.testing": "Testing…",
509
517
  "settings.models.btn.test": "Test",
518
+ "settings.models.link.topUp": "Top up / Manage account",
510
519
  "settings.models.btn.edit": "Edit",
511
520
  "settings.models.btn.delete": "Delete",
512
521
  "settings.models.btn.cancel": "Cancel",
@@ -681,6 +690,9 @@ const I18n = (() => {
681
690
  "billing.tokenUsage": "Token Usage",
682
691
  "billing.costTrend": "Cost Trend",
683
692
  "billing.noData": "No data available",
693
+
694
+ "error.insufficient_credit": "Insufficient LLM credit. Please top up your account to continue.",
695
+ "error.insufficient_credit.action": "Top up",
684
696
  },
685
697
 
686
698
  zh: {
@@ -750,6 +762,14 @@ const I18n = (() => {
750
762
  "sessions.actions.downloadHint": "用于调试",
751
763
  "sib.dir.tooltip": "点击切换工作目录",
752
764
  "sib.dir.changePrompt": "切换工作目录:",
765
+ "workspace.title": "工作区",
766
+ "workspace.expand": "打开工作区",
767
+ "workspace.collapse": "收起工作区",
768
+ "workspace.refresh": "刷新",
769
+ "workspace.empty": "此文件夹为空",
770
+ "workspace.loading": "加载中…",
771
+ "workspace.error": "加载文件失败",
772
+ "workspace.downloadFailed": "下载失败",
753
773
  "sib.model.tooltip": "点击切换模型",
754
774
  "sib.model.tooltip.busy": "Agent 回复中,暂时无法切换模型",
755
775
  "sib.signal.tooltip": "最近一次 LLM 响应延迟",
@@ -1175,6 +1195,7 @@ const I18n = (() => {
1175
1195
  "settings.models.btn.saved": "已保存 ✓",
1176
1196
  "settings.models.btn.testing": "测试中…",
1177
1197
  "settings.models.btn.test": "测试",
1198
+ "settings.models.link.topUp": "充值 / 账户管理",
1178
1199
  "settings.models.btn.edit": "编辑",
1179
1200
  "settings.models.btn.delete": "删除",
1180
1201
  "settings.models.btn.cancel": "取消",
@@ -1349,6 +1370,9 @@ const I18n = (() => {
1349
1370
  "billing.tokenUsage": "Token 用量",
1350
1371
  "billing.costTrend": "费用趋势",
1351
1372
  "billing.noData": "暂无数据",
1373
+
1374
+ "error.insufficient_credit": "LLM(大模型)余额不足,请充值后继续使用。",
1375
+ "error.insufficient_credit.action": "去充值",
1352
1376
  }
1353
1377
  };
1354
1378
 
@@ -313,6 +313,8 @@
313
313
  <!-- Chat panel -->
314
314
  <div id="chat-panel" style="display:none">
315
315
 
316
+ <!-- Chat column (messages + info bar + input) -->
317
+ <div id="chat-main">
316
318
  <div id="messages"></div>
317
319
  <!-- New message notification banner -->
318
320
  <div id="new-message-banner" class="new-message-banner" style="display:none">
@@ -334,6 +336,7 @@
334
336
  <span id="sib-model-wrap">
335
337
  <span id="sib-model" class="sib-model-clickable" title="Click to switch model"></span>
336
338
  <div id="sib-model-dropdown" class="sib-model-dropdown" style="display:none"></div>
339
+ <div id="sib-submodel-panel" class="sib-submodel-panel" style="display:none"></div>
337
340
  </span>
338
341
  <span class="sib-sep sib-sep-after-model">│</span>
339
342
  <span id="sib-reasoning-wrap">
@@ -397,6 +400,35 @@
397
400
  <button id="btn-interrupt" style="display:none" title="Stop"></button>
398
401
  </div>
399
402
  </div>
403
+ </div><!-- /#chat-main -->
404
+
405
+ <!-- ── WORKSPACE PANEL (right) ──────────────────────────────────── -->
406
+ <aside id="workspace-panel" class="collapsed">
407
+ <div id="workspace-header">
408
+ <span id="workspace-title" data-i18n="workspace.title">Workspace</span>
409
+ <div class="workspace-header-actions">
410
+ <button id="btn-workspace-refresh" class="workspace-icon-btn" data-i18n-title="workspace.refresh" title="Refresh">
411
+ <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
412
+ <path d="M23 4v6h-6"/><path d="M1 20v-6h6"/>
413
+ <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
414
+ </svg>
415
+ </button>
416
+ <button id="btn-workspace-close" class="workspace-icon-btn" data-i18n-title="workspace.collapse" title="Collapse">
417
+ <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
418
+ <path d="M9 18l6-6-6-6"/>
419
+ </svg>
420
+ </button>
421
+ </div>
422
+ </div>
423
+ <div id="workspace-tree" role="tree"></div>
424
+ </aside>
425
+
426
+ <!-- Collapsed-state opener tab -->
427
+ <button id="btn-workspace-open" data-i18n-title="workspace.expand" title="Open workspace" style="display:none">
428
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
429
+ <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
430
+ </svg>
431
+ </button>
400
432
  </div>
401
433
 
402
434
  <!-- ── CREATOR PANEL ─────────────────────────────────────────────── -->
@@ -1250,6 +1282,7 @@
1250
1282
  <script src="/ws.js"></script>
1251
1283
  <script src="/ws-dispatcher.js"></script>
1252
1284
  <script src="/sessions.js"></script>
1285
+ <script src="/workspace.js"></script>
1253
1286
  <script src="/datepicker.js"></script>
1254
1287
  <script src="/tasks.js"></script>
1255
1288
  <script src="/skills.js"></script>