openclacky 1.3.3 → 1.3.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -0
- data/docs/rich_ui_guide.md +277 -0
- data/docs/rich_ui_refactor_plan.md +396 -0
- data/lib/clacky/agent/fake_tool_call_detector.rb +52 -0
- data/lib/clacky/agent/llm_caller.rb +10 -4
- data/lib/clacky/agent/session_serializer.rb +5 -3
- data/lib/clacky/agent/tool_executor.rb +0 -12
- data/lib/clacky/agent.rb +77 -11
- data/lib/clacky/agent_config.rb +2 -14
- data/lib/clacky/api_extension.rb +343 -0
- data/lib/clacky/api_extension_loader.rb +168 -0
- data/lib/clacky/cli.rb +93 -3
- data/lib/clacky/client.rb +47 -25
- data/lib/clacky/default_agents/_panels/git/panel.js +1 -1
- data/lib/clacky/default_agents/_panels/time_machine/panel.js +23 -1
- data/lib/clacky/default_agents/base_prompt.md +1 -0
- data/lib/clacky/default_extensions/meeting/handler.rb +331 -0
- data/lib/clacky/default_extensions/meeting/meeting.js +790 -0
- data/lib/clacky/default_extensions/meeting/meta.yml +3 -0
- data/lib/clacky/default_extensions/meeting/skills/meeting-summarizer/SKILL.md +44 -0
- data/lib/clacky/default_skills/media-gen/SKILL.md +72 -6
- data/lib/clacky/default_skills/media-gen/scripts/video_seq.sh +114 -0
- data/lib/clacky/idle_compression_timer.rb +3 -1
- data/lib/clacky/json_ui_controller.rb +1 -1
- data/lib/clacky/locales/en.rb +26 -0
- data/lib/clacky/locales/i18n.rb +26 -0
- data/lib/clacky/locales/zh.rb +26 -0
- data/lib/clacky/media/base.rb +60 -0
- data/lib/clacky/media/dashscope.rb +385 -21
- data/lib/clacky/media/gemini.rb +9 -0
- data/lib/clacky/media/generator.rb +52 -0
- data/lib/clacky/media/openai_compat.rb +166 -0
- data/lib/clacky/null_ui_controller.rb +13 -0
- data/lib/clacky/plain_ui_controller.rb +1 -1
- data/lib/clacky/providers.rb +50 -2
- data/lib/clacky/rich_ui/components/base_component.rb +50 -0
- data/lib/clacky/rich_ui/components/dialogs/approval_dialog.rb +142 -0
- data/lib/clacky/rich_ui/components/dialogs/config_menu_dialog.rb +106 -0
- data/lib/clacky/rich_ui/components/dialogs/form_dialog.rb +128 -0
- data/lib/clacky/rich_ui/components/sidebar.rb +119 -0
- data/lib/clacky/rich_ui/components/sidebar_panels.rb +134 -0
- data/lib/clacky/rich_ui/components/status_view.rb +58 -0
- data/lib/clacky/rich_ui/components/thinking_live_view.rb +79 -0
- data/lib/clacky/rich_ui/entry_tracker.rb +56 -0
- data/lib/clacky/rich_ui/layout_adapter.rb +16 -0
- data/lib/clacky/rich_ui/progress_handle_adapter.rb +24 -0
- data/lib/clacky/rich_ui/rich_ui_controller.rb +868 -0
- data/lib/clacky/rich_ui/shell/rich_agent_shell.rb +184 -0
- data/lib/clacky/rich_ui/view_renderer.rb +291 -0
- data/lib/clacky/rich_ui.rb +57 -0
- data/lib/clacky/rich_ui_controller.rb +3 -1549
- data/lib/clacky/server/api_extension_dispatcher.rb +120 -0
- data/lib/clacky/server/channel/channel_ui_controller.rb +1 -1
- data/lib/clacky/server/http_server.rb +291 -109
- data/lib/clacky/server/session_registry.rb +5 -3
- data/lib/clacky/server/web_ui_controller.rb +3 -2
- data/lib/clacky/shell_hook_loader.rb +1 -1
- data/lib/clacky/skill_loader.rb +14 -2
- data/lib/clacky/tools/edit.rb +14 -2
- data/lib/clacky/tools/terminal/output_cleaner.rb +1 -3
- data/lib/clacky/tools/terminal.rb +0 -43
- data/lib/clacky/ui2/components/modal_component.rb +1 -1
- data/lib/clacky/ui2/ui_controller.rb +147 -31
- data/lib/clacky/ui_interface.rb +10 -1
- data/lib/clacky/utils/encoding.rb +25 -0
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +201 -81
- data/lib/clacky/web/app.js +65 -7
- data/lib/clacky/web/components/onboard.js +19 -16
- data/lib/clacky/web/core/aside.js +8 -3
- data/lib/clacky/web/core/ext.js +1 -1
- data/lib/clacky/web/features/brand/view.js +8 -5
- data/lib/clacky/web/features/channels/store.js +1 -20
- data/lib/clacky/web/features/mcp/store.js +1 -20
- data/lib/clacky/web/features/profile/store.js +1 -13
- data/lib/clacky/web/features/profile/view.js +16 -4
- data/lib/clacky/web/features/skills/store.js +36 -23
- data/lib/clacky/web/features/skills/view.js +32 -1
- data/lib/clacky/web/features/version/store.js +2 -0
- data/lib/clacky/web/features/workspace/view.js +1 -1
- data/lib/clacky/web/i18n.js +48 -13
- data/lib/clacky/web/index.html +24 -17
- data/lib/clacky/web/sessions.js +427 -79
- data/lib/clacky/web/settings.js +143 -113
- data/lib/clacky/web/ws-dispatcher.js +18 -6
- data/lib/clacky.rb +27 -5
- metadata +45 -2
- data/lib/clacky/media/output_dir.rb +0 -43
data/lib/clacky/web/app.css
CHANGED
|
@@ -526,7 +526,7 @@ body {
|
|
|
526
526
|
/* The sidebar sits on the outer frame (bg-primary) so it's visibly a layer
|
|
527
527
|
BEHIND the chat surface (bg-secondary). Subtle right border separates them. */
|
|
528
528
|
#sidebar {
|
|
529
|
-
--sidebar-width:
|
|
529
|
+
--sidebar-width: 16rem;
|
|
530
530
|
width: var(--sidebar-width);
|
|
531
531
|
min-width: var(--sidebar-width);
|
|
532
532
|
background: var(--color-bg-primary);
|
|
@@ -650,38 +650,38 @@ body {
|
|
|
650
650
|
position: relative;
|
|
651
651
|
display: flex;
|
|
652
652
|
align-items: center;
|
|
653
|
-
border-radius:
|
|
653
|
+
border-radius: 6px;
|
|
654
654
|
overflow: visible;
|
|
655
655
|
}
|
|
656
656
|
.btn-split-main {
|
|
657
|
-
height: 1.
|
|
658
|
-
padding: 0 0.
|
|
657
|
+
height: 1.5rem;
|
|
658
|
+
padding: 0 0.625rem;
|
|
659
659
|
border: none;
|
|
660
|
-
border-radius:
|
|
660
|
+
border-radius: 6px 0 0 6px;
|
|
661
661
|
background: var(--color-accent-primary);
|
|
662
662
|
color: #fff;
|
|
663
|
-
font-size: 0.
|
|
663
|
+
font-size: 0.75rem;
|
|
664
664
|
font-weight: 500;
|
|
665
665
|
white-space: nowrap;
|
|
666
666
|
line-height: 1;
|
|
667
667
|
cursor: pointer;
|
|
668
|
-
transition: background 0.15s,
|
|
668
|
+
transition: background 0.15s, box-shadow 0.15s;
|
|
669
669
|
}
|
|
670
670
|
.btn-split-main:hover {
|
|
671
671
|
background: var(--color-button-primary-hover);
|
|
672
|
-
box-shadow: 0 2px
|
|
672
|
+
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
|
|
673
673
|
}
|
|
674
674
|
.btn-split-arrow {
|
|
675
|
-
height: 1.
|
|
676
|
-
padding: 0 0.
|
|
675
|
+
height: 1.5rem;
|
|
676
|
+
padding: 0 0.35rem;
|
|
677
677
|
border: none;
|
|
678
678
|
border-left: 1px solid rgba(255,255,255,0.3);
|
|
679
|
-
border-radius: 0
|
|
679
|
+
border-radius: 0 6px 6px 0;
|
|
680
680
|
background: var(--color-accent-primary);
|
|
681
681
|
color: #fff;
|
|
682
|
-
font-size: 0.
|
|
682
|
+
font-size: 0.75rem;
|
|
683
683
|
cursor: pointer;
|
|
684
|
-
transition: background 0.15s
|
|
684
|
+
transition: background 0.15s;
|
|
685
685
|
}
|
|
686
686
|
.btn-split-arrow:hover {
|
|
687
687
|
background: var(--color-button-primary-hover);
|
|
@@ -690,7 +690,7 @@ body {
|
|
|
690
690
|
/* ── Dropdown menu ───────────────────────────────────────────────────────── */
|
|
691
691
|
.new-session-dropdown {
|
|
692
692
|
position: absolute;
|
|
693
|
-
top:
|
|
693
|
+
top: 100%;
|
|
694
694
|
right: 0;
|
|
695
695
|
min-width: 11.25rem;
|
|
696
696
|
background: var(--color-bg-card);
|
|
@@ -698,13 +698,13 @@ body {
|
|
|
698
698
|
border-radius: 7px;
|
|
699
699
|
box-shadow: var(--shadow-md);
|
|
700
700
|
z-index: 200;
|
|
701
|
-
padding: 0.
|
|
701
|
+
padding: 0.375rem 0 0.25rem;
|
|
702
702
|
overflow: hidden;
|
|
703
703
|
}
|
|
704
704
|
.new-session-dropdown[hidden] { display: none; }
|
|
705
705
|
.dropdown-item {
|
|
706
|
-
padding: 0.
|
|
707
|
-
font-size: 0.
|
|
706
|
+
padding: 0.4rem 0.875rem;
|
|
707
|
+
font-size: 0.8125rem;
|
|
708
708
|
color: var(--color-text-secondary);
|
|
709
709
|
cursor: pointer;
|
|
710
710
|
white-space: nowrap;
|
|
@@ -1799,7 +1799,7 @@ body {
|
|
|
1799
1799
|
white-space: nowrap;
|
|
1800
1800
|
transition: background .12s;
|
|
1801
1801
|
}
|
|
1802
|
-
.task-run-btn:hover { background: var(--color-
|
|
1802
|
+
.task-run-btn:hover { background: var(--color-button-primary-hover); }
|
|
1803
1803
|
.task-action-btn {
|
|
1804
1804
|
display: inline-flex;
|
|
1805
1805
|
align-items: center;
|
|
@@ -1938,7 +1938,7 @@ body {
|
|
|
1938
1938
|
the slot has no panels for the current agent it stays empty and the whole
|
|
1939
1939
|
column collapses to zero width. */
|
|
1940
1940
|
#session-aside {
|
|
1941
|
-
--session-aside-width:
|
|
1941
|
+
--session-aside-width: 16rem;
|
|
1942
1942
|
width: var(--session-aside-width);
|
|
1943
1943
|
flex-shrink: 0;
|
|
1944
1944
|
position: relative;
|
|
@@ -1976,7 +1976,7 @@ body {
|
|
|
1976
1976
|
transition: background-color var(--transition-base);
|
|
1977
1977
|
}
|
|
1978
1978
|
.session-aside-resize:hover::after,
|
|
1979
|
-
.session-aside-resize.active::after { background:
|
|
1979
|
+
.session-aside-resize.active::after { background: transparent; }
|
|
1980
1980
|
|
|
1981
1981
|
.session-aside-collapse {
|
|
1982
1982
|
position: absolute;
|
|
@@ -2271,13 +2271,16 @@ body {
|
|
|
2271
2271
|
/* Sit above the next message in case text would otherwise clip beneath it. */
|
|
2272
2272
|
z-index: 1;
|
|
2273
2273
|
}
|
|
2274
|
-
.msg:hover .msg-time
|
|
2274
|
+
.msg:hover .msg-time,
|
|
2275
|
+
.msg-user-wrap:hover .msg-time {
|
|
2275
2276
|
opacity: 1;
|
|
2276
2277
|
transform: translateY(0);
|
|
2277
2278
|
}
|
|
2278
2279
|
/* Time color / alignment: anchor to the bubble's own side, let width be
|
|
2279
2280
|
driven by content — prevents overflow on narrow bubbles. */
|
|
2280
|
-
.msg-user
|
|
2281
|
+
/* User timestamp: appended to .msg-user-wrap (not bubble), sits below action
|
|
2282
|
+
buttons, right-aligned. */
|
|
2283
|
+
.msg-user-wrap > .msg-time { color: var(--color-text-secondary); top: auto; bottom: 0.2rem; right: 0; left: auto; padding-right: 0.25rem; }
|
|
2281
2284
|
.msg-assistant .msg-time { color: var(--color-text-secondary); left: 0; right: auto; padding-left: 0.25rem; }
|
|
2282
2285
|
|
|
2283
2286
|
.msg-user { background: var(--color-accent-soft); color: var(--color-text-primary); white-space: pre-wrap; }
|
|
@@ -2289,11 +2292,11 @@ body {
|
|
|
2289
2292
|
position: relative;
|
|
2290
2293
|
display: flex;
|
|
2291
2294
|
justify-content: flex-end;
|
|
2292
|
-
padding-bottom:
|
|
2295
|
+
padding-bottom: 3rem;
|
|
2293
2296
|
}
|
|
2294
2297
|
.msg-user-actions {
|
|
2295
2298
|
position: absolute;
|
|
2296
|
-
|
|
2299
|
+
bottom: 1.1rem;
|
|
2297
2300
|
right: 0;
|
|
2298
2301
|
display: flex;
|
|
2299
2302
|
gap: 0.125rem;
|
|
@@ -2305,6 +2308,7 @@ body {
|
|
|
2305
2308
|
.msg-user-wrap:hover .msg-user-actions { opacity: 1; pointer-events: auto; }
|
|
2306
2309
|
.msg-user-wrap:has(.msg-user.editing) { padding-bottom: 0; }
|
|
2307
2310
|
.msg-user-wrap:has(.msg-user.editing) .msg-user-actions { display: none; }
|
|
2311
|
+
.msg-user-wrap:has(.msg-user.editing) .msg-time { display: none; }
|
|
2308
2312
|
|
|
2309
2313
|
.msg-user-action-btn {
|
|
2310
2314
|
display: inline-flex;
|
|
@@ -2452,6 +2456,36 @@ body {
|
|
|
2452
2456
|
.msg-copy-btn { opacity: 0.65; }
|
|
2453
2457
|
}
|
|
2454
2458
|
|
|
2459
|
+
/* ── Inline "start" button on unchecked task-list items ──────────────────── */
|
|
2460
|
+
.msg-assistant li.task-list-item { position: relative; }
|
|
2461
|
+
.msg-todo-spawn {
|
|
2462
|
+
display: inline-flex;
|
|
2463
|
+
align-items: center;
|
|
2464
|
+
margin-left: 0.5rem;
|
|
2465
|
+
padding: 0.05rem 0.5rem;
|
|
2466
|
+
font-size: 0.75rem;
|
|
2467
|
+
line-height: 1.4;
|
|
2468
|
+
border: 1px solid var(--color-border-primary);
|
|
2469
|
+
border-radius: 5px;
|
|
2470
|
+
background: var(--color-bg-primary);
|
|
2471
|
+
color: var(--color-text-secondary);
|
|
2472
|
+
cursor: pointer;
|
|
2473
|
+
opacity: 0;
|
|
2474
|
+
vertical-align: middle;
|
|
2475
|
+
transition: opacity 0.15s ease, color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
|
|
2476
|
+
}
|
|
2477
|
+
.msg-assistant:hover .msg-todo-spawn,
|
|
2478
|
+
.msg-todo-spawn:focus-visible { opacity: 1; }
|
|
2479
|
+
.msg-todo-spawn:hover {
|
|
2480
|
+
color: var(--color-text-primary);
|
|
2481
|
+
background: var(--color-bg-tertiary);
|
|
2482
|
+
border-color: var(--color-border-secondary);
|
|
2483
|
+
}
|
|
2484
|
+
.msg-todo-spawn:disabled { opacity: 0.5; cursor: default; }
|
|
2485
|
+
@media (hover: none) {
|
|
2486
|
+
.msg-todo-spawn { opacity: 0.7; }
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2455
2489
|
/* ── Markdown rendering inside assistant messages ────────────────────────── */
|
|
2456
2490
|
.msg-assistant p:last-child { margin-bottom: 0; }
|
|
2457
2491
|
.msg-assistant h1, .msg-assistant h2, .msg-assistant h3,
|
|
@@ -2686,7 +2720,7 @@ body {
|
|
|
2686
2720
|
align-self: flex-start;
|
|
2687
2721
|
display: flex;
|
|
2688
2722
|
flex-direction: column;
|
|
2689
|
-
gap: 0.
|
|
2723
|
+
gap: 0.5rem;
|
|
2690
2724
|
}
|
|
2691
2725
|
.msg-error .retry-btn {
|
|
2692
2726
|
align-self: flex-start;
|
|
@@ -2717,6 +2751,56 @@ body {
|
|
|
2717
2751
|
.msg-error a:hover {
|
|
2718
2752
|
opacity: 0.8;
|
|
2719
2753
|
}
|
|
2754
|
+
.error-raw-detail {
|
|
2755
|
+
margin-top: -0.125rem;
|
|
2756
|
+
font-size: 0.75rem;
|
|
2757
|
+
align-self: flex-start;
|
|
2758
|
+
max-width: 100%;
|
|
2759
|
+
}
|
|
2760
|
+
.error-raw-detail summary {
|
|
2761
|
+
cursor: pointer;
|
|
2762
|
+
font-size: 0.7rem;
|
|
2763
|
+
font-weight: 500;
|
|
2764
|
+
user-select: none;
|
|
2765
|
+
color: var(--color-error);
|
|
2766
|
+
opacity: 0.6;
|
|
2767
|
+
list-style: none;
|
|
2768
|
+
display: inline-flex;
|
|
2769
|
+
align-items: center;
|
|
2770
|
+
gap: 0.25rem;
|
|
2771
|
+
transition: opacity 0.15s;
|
|
2772
|
+
}
|
|
2773
|
+
.error-raw-detail summary::-webkit-details-marker { display: none; }
|
|
2774
|
+
.error-raw-detail summary::before {
|
|
2775
|
+
content: "";
|
|
2776
|
+
display: inline-block;
|
|
2777
|
+
width: 0;
|
|
2778
|
+
height: 0;
|
|
2779
|
+
border-top: 4px solid transparent;
|
|
2780
|
+
border-bottom: 4px solid transparent;
|
|
2781
|
+
border-left: 5px solid currentColor;
|
|
2782
|
+
transition: transform 0.15s;
|
|
2783
|
+
flex-shrink: 0;
|
|
2784
|
+
}
|
|
2785
|
+
.error-raw-detail[open] summary::before {
|
|
2786
|
+
transform: rotate(90deg);
|
|
2787
|
+
}
|
|
2788
|
+
.error-raw-detail summary:hover {
|
|
2789
|
+
opacity: 0.9;
|
|
2790
|
+
}
|
|
2791
|
+
.error-raw-detail pre {
|
|
2792
|
+
margin: 0.375rem 0 0;
|
|
2793
|
+
padding: 0.5rem 0.625rem;
|
|
2794
|
+
background: rgba(255, 59, 48, 0.05);
|
|
2795
|
+
border: 1px solid rgba(255, 59, 48, 0.12);
|
|
2796
|
+
border-radius: 5px;
|
|
2797
|
+
font-family: var(--font-mono, monospace);
|
|
2798
|
+
font-size: 0.7rem;
|
|
2799
|
+
line-height: 1.5;
|
|
2800
|
+
white-space: pre-wrap;
|
|
2801
|
+
word-break: break-all;
|
|
2802
|
+
color: var(--color-text-secondary);
|
|
2803
|
+
}
|
|
2720
2804
|
.msg-success { color: var(--color-success); align-self: flex-start; font-size: 0.8125rem; }
|
|
2721
2805
|
.tool-name { color: var(--color-warning); font-weight: 600; }
|
|
2722
2806
|
.progress-msg { color: var(--color-accent-primary); font-size: 0.75rem; align-self: center; }
|
|
@@ -4583,7 +4667,6 @@ body {
|
|
|
4583
4667
|
font-size: 0.8125rem;
|
|
4584
4668
|
color: var(--color-text-primary);
|
|
4585
4669
|
cursor: pointer;
|
|
4586
|
-
margin-top: 0.5rem;
|
|
4587
4670
|
}
|
|
4588
4671
|
.model-field-checkbox input[type="checkbox"] {
|
|
4589
4672
|
width: 15px;
|
|
@@ -4780,7 +4863,7 @@ body {
|
|
|
4780
4863
|
/* ── Model Cards Grid Container ─────────────────────────────────────────────── */
|
|
4781
4864
|
#model-cards {
|
|
4782
4865
|
display: grid;
|
|
4783
|
-
grid-template-columns: repeat(auto-fill, minmax(
|
|
4866
|
+
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
4784
4867
|
gap: 1rem;
|
|
4785
4868
|
}
|
|
4786
4869
|
|
|
@@ -6698,24 +6781,14 @@ body {
|
|
|
6698
6781
|
white-space: nowrap;
|
|
6699
6782
|
}
|
|
6700
6783
|
.brand-skill-version.installed {
|
|
6701
|
-
background:
|
|
6702
|
-
color:
|
|
6703
|
-
border: 1px solid
|
|
6784
|
+
background: color-mix(in srgb, var(--color-accent-primary) 15%, transparent);
|
|
6785
|
+
color: var(--color-accent-primary);
|
|
6786
|
+
border: 1px solid color-mix(in srgb, var(--color-accent-primary) 35%, transparent);
|
|
6704
6787
|
}
|
|
6705
6788
|
.brand-skill-version.latest {
|
|
6706
|
-
background:
|
|
6707
|
-
color: #bfdbfe;
|
|
6708
|
-
border: 1px solid #1d4070;
|
|
6709
|
-
}
|
|
6710
|
-
[data-theme="light"] .brand-skill-version.installed {
|
|
6711
|
-
background: #eff6ff;
|
|
6712
|
-
color: #1d4ed8;
|
|
6713
|
-
border: 1px solid #bfdbfe;
|
|
6714
|
-
}
|
|
6715
|
-
[data-theme="light"] .brand-skill-version.latest {
|
|
6716
|
-
background: #1d4ed8;
|
|
6789
|
+
background: var(--color-accent-primary);
|
|
6717
6790
|
color: #ffffff;
|
|
6718
|
-
border: 1px solid
|
|
6791
|
+
border: 1px solid var(--color-accent-primary);
|
|
6719
6792
|
}
|
|
6720
6793
|
.brand-skill-update-arrow {
|
|
6721
6794
|
color: var(--color-text-secondary);
|
|
@@ -6795,17 +6868,11 @@ body {
|
|
|
6795
6868
|
border-color: var(--color-accent-primary);
|
|
6796
6869
|
}
|
|
6797
6870
|
.btn-brand-update {
|
|
6798
|
-
background:
|
|
6799
|
-
color: #bfdbfe;
|
|
6800
|
-
border: 1px solid #1d4070;
|
|
6801
|
-
}
|
|
6802
|
-
.btn-brand-update:hover { background: #1a3f85; }
|
|
6803
|
-
[data-theme="light"] .btn-brand-update {
|
|
6804
|
-
background: #1d4ed8;
|
|
6871
|
+
background: var(--color-accent-primary);
|
|
6805
6872
|
color: #ffffff;
|
|
6806
|
-
border: 1px solid
|
|
6873
|
+
border: 1px solid var(--color-accent-primary);
|
|
6807
6874
|
}
|
|
6808
|
-
|
|
6875
|
+
.btn-brand-update:hover { background: var(--color-accent-hover); }
|
|
6809
6876
|
.btn-brand-install:disabled,
|
|
6810
6877
|
.btn-brand-update:disabled {
|
|
6811
6878
|
opacity: 0.5;
|
|
@@ -6846,6 +6913,22 @@ body {
|
|
|
6846
6913
|
border-color: var(--color-error-border, #f5c6c6);
|
|
6847
6914
|
background: var(--color-error-bg, #fff0f0);
|
|
6848
6915
|
}
|
|
6916
|
+
.btn-skill-edit {
|
|
6917
|
+
background: transparent;
|
|
6918
|
+
border: 1px solid transparent;
|
|
6919
|
+
border-radius: 6px;
|
|
6920
|
+
color: var(--color-text-tertiary);
|
|
6921
|
+
cursor: pointer;
|
|
6922
|
+
padding: 0.25rem;
|
|
6923
|
+
display: inline-flex;
|
|
6924
|
+
align-items: center;
|
|
6925
|
+
transition: color .15s, border-color .15s, background .15s;
|
|
6926
|
+
}
|
|
6927
|
+
.btn-skill-edit:hover {
|
|
6928
|
+
color: var(--color-text-primary);
|
|
6929
|
+
border-color: var(--color-border-primary);
|
|
6930
|
+
background: var(--color-bg-hover);
|
|
6931
|
+
}
|
|
6849
6932
|
|
|
6850
6933
|
|
|
6851
6934
|
/* ── CodeEditor (CodeMirror 6) Modal ────────────────────────────────────── */
|
|
@@ -6926,10 +7009,10 @@ body {
|
|
|
6926
7009
|
background: var(--color-bg-hover);
|
|
6927
7010
|
}
|
|
6928
7011
|
.code-editor-body .cm-activeLine {
|
|
6929
|
-
background: var(--color-bg-hover);
|
|
7012
|
+
background: color-mix(in srgb, var(--color-bg-hover) 60%, transparent);
|
|
6930
7013
|
}
|
|
6931
7014
|
.code-editor-body .cm-selectionBackground {
|
|
6932
|
-
background: color-mix(in srgb, var(--color-accent-primary)
|
|
7015
|
+
background: color-mix(in srgb, var(--color-accent-primary) 40%, transparent) !important;
|
|
6933
7016
|
}
|
|
6934
7017
|
.code-editor-footer {
|
|
6935
7018
|
display: flex;
|
|
@@ -7396,28 +7479,35 @@ body.setup-mode[data-theme="dark"] {
|
|
|
7396
7479
|
transform: translateY(1px);
|
|
7397
7480
|
}
|
|
7398
7481
|
#setup-device-error {
|
|
7399
|
-
margin-top: 0
|
|
7482
|
+
margin-top: 0;
|
|
7483
|
+
min-height: 0;
|
|
7400
7484
|
}
|
|
7401
7485
|
#setup-device-error.result-fail { color: var(--color-error); }
|
|
7402
7486
|
|
|
7403
7487
|
/* Manual-config collapsible (secondary path) */
|
|
7404
|
-
#setup-manual-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7488
|
+
#setup-manual-toggle {
|
|
7489
|
+
display: block;
|
|
7490
|
+
width: 100%;
|
|
7491
|
+
margin-top: 0.5rem;
|
|
7492
|
+
padding: 0.5rem 0;
|
|
7493
|
+
background: none;
|
|
7494
|
+
border: none;
|
|
7408
7495
|
cursor: pointer;
|
|
7409
|
-
font-size: 0.
|
|
7496
|
+
font-size: 0.8rem;
|
|
7410
7497
|
color: var(--color-text-secondary);
|
|
7411
7498
|
text-align: center;
|
|
7412
|
-
|
|
7413
|
-
|
|
7499
|
+
text-decoration: underline;
|
|
7500
|
+
text-underline-offset: 3px;
|
|
7414
7501
|
user-select: none;
|
|
7502
|
+
transition: color 0.15s;
|
|
7415
7503
|
}
|
|
7416
|
-
#setup-manual-
|
|
7417
|
-
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7504
|
+
#setup-manual-toggle:hover {
|
|
7505
|
+
color: var(--color-text-primary);
|
|
7506
|
+
}
|
|
7507
|
+
#setup-manual-section {
|
|
7508
|
+
margin-top: 0.75rem;
|
|
7509
|
+
padding-top: 0.75rem;
|
|
7510
|
+
border-top: 1px solid var(--color-border-primary);
|
|
7421
7511
|
}
|
|
7422
7512
|
|
|
7423
7513
|
/* Bottom action row: [← Back] [Test & Continue →] */
|
|
@@ -10036,7 +10126,7 @@ body.setup-mode[data-theme="dark"] {
|
|
|
10036
10126
|
border-right: 1px solid var(--color-border-primary);
|
|
10037
10127
|
}
|
|
10038
10128
|
.billing-period-btn:hover {
|
|
10039
|
-
background: var(--color-bg-
|
|
10129
|
+
background: var(--color-bg-hover);
|
|
10040
10130
|
color: var(--color-text-primary);
|
|
10041
10131
|
}
|
|
10042
10132
|
.billing-period-btn.active {
|
|
@@ -10896,7 +10986,7 @@ body.setup-mode[data-theme="dark"] {
|
|
|
10896
10986
|
white-space: nowrap;
|
|
10897
10987
|
transition: opacity 0.15s ease, transform 0.05s ease;
|
|
10898
10988
|
}
|
|
10899
|
-
.btn-mcp-cta:hover {
|
|
10989
|
+
.btn-mcp-cta:hover { background: var(--color-button-primary-hover) }
|
|
10900
10990
|
.btn-mcp-cta:active { transform: scale(0.98); }
|
|
10901
10991
|
.btn-mcp-cta-large {
|
|
10902
10992
|
padding: 0.625rem 1.125rem;
|
|
@@ -11009,6 +11099,49 @@ body.setup-mode[data-theme="dark"] {
|
|
|
11009
11099
|
color: var(--color-error, #d33);
|
|
11010
11100
|
}
|
|
11011
11101
|
|
|
11102
|
+
/* ── Accent Color Swatches ───────────────────────────────────────────── */
|
|
11103
|
+
.settings-accent-swatches {
|
|
11104
|
+
display: flex;
|
|
11105
|
+
flex-wrap: wrap;
|
|
11106
|
+
gap: 0.5rem;
|
|
11107
|
+
padding: 0.25rem 0;
|
|
11108
|
+
}
|
|
11109
|
+
|
|
11110
|
+
.settings-accent-swatch {
|
|
11111
|
+
width: 2.25rem;
|
|
11112
|
+
height: 2.25rem;
|
|
11113
|
+
border-radius: 50%;
|
|
11114
|
+
border: none;
|
|
11115
|
+
cursor: pointer;
|
|
11116
|
+
padding: 0;
|
|
11117
|
+
transition: transform 0.15s;
|
|
11118
|
+
outline: none;
|
|
11119
|
+
position: relative;
|
|
11120
|
+
}
|
|
11121
|
+
|
|
11122
|
+
.settings-accent-swatch:hover {
|
|
11123
|
+
transform: scale(1.15);
|
|
11124
|
+
}
|
|
11125
|
+
|
|
11126
|
+
.settings-accent-swatch.active::after {
|
|
11127
|
+
content: "";
|
|
11128
|
+
position: absolute;
|
|
11129
|
+
width: 0.375rem;
|
|
11130
|
+
height: 0.625rem;
|
|
11131
|
+
border-right: 0.125rem solid white;
|
|
11132
|
+
border-bottom: 0.125rem solid white;
|
|
11133
|
+
top: 46%;
|
|
11134
|
+
left: 50%;
|
|
11135
|
+
transform: translate(-50%, -50%) rotate(45deg);
|
|
11136
|
+
}
|
|
11137
|
+
|
|
11138
|
+
.swatch-indigo { background: #4f46e5; }
|
|
11139
|
+
.swatch-aurora-blue { background: #3B82F6; }
|
|
11140
|
+
.swatch-forest-green { background: #10B981; }
|
|
11141
|
+
.swatch-sunrise-orange { background: #F59E0B; }
|
|
11142
|
+
.swatch-rose-violet { background: #8B5CF6; }
|
|
11143
|
+
.swatch-coral-red { background: #EF4444; }
|
|
11144
|
+
|
|
11012
11145
|
/* ── Sessions List ───────────────────────────────────────────────────── */
|
|
11013
11146
|
.billing-sessions-row {
|
|
11014
11147
|
margin-top: 1rem;
|
|
@@ -12113,16 +12246,3 @@ body.setup-mode[data-theme="dark"] {
|
|
|
12113
12246
|
}
|
|
12114
12247
|
}
|
|
12115
12248
|
|
|
12116
|
-
/* ════ Media output directory subsection (nested under #media-section) ════ */
|
|
12117
|
-
#media-output-dir-section.settings-subsection {
|
|
12118
|
-
display: flex;
|
|
12119
|
-
flex-direction: column;
|
|
12120
|
-
gap: 0.5rem;
|
|
12121
|
-
max-width: 48rem;
|
|
12122
|
-
}
|
|
12123
|
-
#media-output-dir-section .settings-subsection-desc {
|
|
12124
|
-
font-size: 0.8125rem;
|
|
12125
|
-
color: var(--color-text-secondary);
|
|
12126
|
-
line-height: 1.5;
|
|
12127
|
-
margin: 0;
|
|
12128
|
-
}
|
data/lib/clacky/web/app.js
CHANGED
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
// ── DOM helper (shared by all modules loaded after this) ──────────────────
|
|
11
11
|
const $ = id => document.getElementById(id);
|
|
12
12
|
|
|
13
|
+
// ── Inject X-Lang header into every fetch request ─────────────────────────
|
|
14
|
+
const _nativeFetch = window.fetch;
|
|
15
|
+
window.fetch = function(input, init = {}) {
|
|
16
|
+
const headers = new Headers(init.headers || {});
|
|
17
|
+
if (!headers.has("X-Lang")) headers.set("X-Lang", I18n.lang());
|
|
18
|
+
return _nativeFetch.call(this, input, { ...init, headers });
|
|
19
|
+
};
|
|
20
|
+
|
|
13
21
|
// ── Utilities (shared) ────────────────────────────────────────────────────
|
|
14
22
|
function escapeHtml(str) {
|
|
15
23
|
return String(str)
|
|
@@ -325,13 +333,53 @@ const Modal = (() => {
|
|
|
325
333
|
/** Show a yes/no confirmation dialog. Returns a Promise<boolean>. */
|
|
326
334
|
function confirm(message) {
|
|
327
335
|
return new Promise(resolve => {
|
|
336
|
+
const overlay = $("modal-overlay");
|
|
337
|
+
$("modal-message").textContent = message;
|
|
338
|
+
$("modal-skip-label").style.display = "none";
|
|
339
|
+
$("modal-skip-cb").checked = false;
|
|
340
|
+
if (overlay.parentNode !== document.body || overlay.nextSibling) {
|
|
341
|
+
document.body.appendChild(overlay);
|
|
342
|
+
}
|
|
343
|
+
overlay.style.display = "flex";
|
|
344
|
+
|
|
345
|
+
const cleanup = (result) => {
|
|
346
|
+
overlay.style.display = "none";
|
|
347
|
+
$("modal-yes").onclick = null;
|
|
348
|
+
$("modal-no").onclick = null;
|
|
349
|
+
resolve(result);
|
|
350
|
+
};
|
|
351
|
+
$("modal-yes").onclick = () => cleanup(true);
|
|
352
|
+
$("modal-no").onclick = () => cleanup(false);
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Like confirm(), but shows a "don't show again" checkbox.
|
|
358
|
+
* If localStorage[storageKey] === "1", resolves true immediately.
|
|
359
|
+
* Returns Promise<boolean>.
|
|
360
|
+
*/
|
|
361
|
+
function confirmOnce(storageKey, message, skipLabel) {
|
|
362
|
+
if (localStorage.getItem(storageKey) === "1") return Promise.resolve(true);
|
|
363
|
+
|
|
364
|
+
return new Promise(resolve => {
|
|
365
|
+
const overlay = $("modal-overlay");
|
|
328
366
|
$("modal-message").textContent = message;
|
|
329
|
-
$("modal-
|
|
367
|
+
$("modal-skip-text").textContent = skipLabel;
|
|
368
|
+
$("modal-skip-cb").checked = false;
|
|
369
|
+
$("modal-skip-label").style.display = "flex";
|
|
370
|
+
if (overlay.parentNode !== document.body || overlay.nextSibling) {
|
|
371
|
+
document.body.appendChild(overlay);
|
|
372
|
+
}
|
|
373
|
+
overlay.style.display = "flex";
|
|
330
374
|
|
|
331
375
|
const cleanup = (result) => {
|
|
332
|
-
|
|
376
|
+
overlay.style.display = "none";
|
|
377
|
+
$("modal-skip-label").style.display = "none";
|
|
333
378
|
$("modal-yes").onclick = null;
|
|
334
379
|
$("modal-no").onclick = null;
|
|
380
|
+
if (result && $("modal-skip-cb").checked) {
|
|
381
|
+
localStorage.setItem(storageKey, "1");
|
|
382
|
+
}
|
|
335
383
|
resolve(result);
|
|
336
384
|
};
|
|
337
385
|
$("modal-yes").onclick = () => cleanup(true);
|
|
@@ -342,10 +390,15 @@ const Modal = (() => {
|
|
|
342
390
|
/** Show a text input prompt dialog. Returns a Promise<string|null>. */
|
|
343
391
|
function prompt(message, defaultValue = "") {
|
|
344
392
|
return new Promise(resolve => {
|
|
393
|
+
const overlay = $("prompt-modal-overlay");
|
|
345
394
|
$("prompt-modal-message").textContent = message;
|
|
346
395
|
const input = $("prompt-modal-input");
|
|
347
396
|
input.value = defaultValue;
|
|
348
|
-
|
|
397
|
+
// Re-attach to <body> end so it stacks above dynamically-appended overlays.
|
|
398
|
+
if (overlay.parentNode !== document.body || overlay.nextSibling) {
|
|
399
|
+
document.body.appendChild(overlay);
|
|
400
|
+
}
|
|
401
|
+
overlay.style.display = "flex";
|
|
349
402
|
|
|
350
403
|
// Auto-focus and select all text
|
|
351
404
|
setTimeout(() => {
|
|
@@ -375,10 +428,15 @@ const Modal = (() => {
|
|
|
375
428
|
/** Show a rename dialog. Returns a Promise<string|null>. */
|
|
376
429
|
function rename(currentName = "") {
|
|
377
430
|
return new Promise(resolve => {
|
|
431
|
+
const overlay = $("rename-modal-overlay");
|
|
378
432
|
const input = $("rename-modal-input");
|
|
379
433
|
input.value = currentName;
|
|
380
434
|
input.classList.remove("input-error");
|
|
381
|
-
|
|
435
|
+
// Re-attach to <body> end so it stacks above dynamically-appended overlays.
|
|
436
|
+
if (overlay.parentNode !== document.body || overlay.nextSibling) {
|
|
437
|
+
document.body.appendChild(overlay);
|
|
438
|
+
}
|
|
439
|
+
overlay.style.display = "flex";
|
|
382
440
|
|
|
383
441
|
setTimeout(() => {
|
|
384
442
|
input.focus();
|
|
@@ -423,7 +481,7 @@ const Modal = (() => {
|
|
|
423
481
|
});
|
|
424
482
|
}
|
|
425
483
|
|
|
426
|
-
return { confirm, prompt, rename };
|
|
484
|
+
return { confirm, confirmOnce, prompt, rename };
|
|
427
485
|
})();
|
|
428
486
|
|
|
429
487
|
// ── Toast helper ──────────────────────────────────────────────────────────
|
|
@@ -550,7 +608,7 @@ $("sidebar-overlay").addEventListener("click", _closeSidebar);
|
|
|
550
608
|
let startW = 0;
|
|
551
609
|
|
|
552
610
|
// Restore saved width
|
|
553
|
-
const saved = localStorage.getItem("sidebar-width");
|
|
611
|
+
const saved = localStorage.getItem("clacky-sidebar-width");
|
|
554
612
|
if (saved) {
|
|
555
613
|
const w = parseFloat(saved);
|
|
556
614
|
if (w >= MIN_W && w <= MAX_W) {
|
|
@@ -583,7 +641,7 @@ $("sidebar-overlay").addEventListener("click", _closeSidebar);
|
|
|
583
641
|
handle.classList.remove("active");
|
|
584
642
|
document.body.style.cursor = "";
|
|
585
643
|
document.body.style.userSelect = "";
|
|
586
|
-
localStorage.setItem("sidebar-width", _getWidth());
|
|
644
|
+
localStorage.setItem("clacky-sidebar-width", _getWidth());
|
|
587
645
|
});
|
|
588
646
|
})();
|
|
589
647
|
|
|
@@ -110,6 +110,11 @@ const Onboard = (() => {
|
|
|
110
110
|
$("setup-phase-key").style.display = step === "key" ? "" : "none";
|
|
111
111
|
$("setup-dot-1").className = "setup-step" + (step === "lang" ? " active" : " done");
|
|
112
112
|
$("setup-dot-2").className = "setup-step" + (step === "key" ? " active" : "");
|
|
113
|
+
if (step === "key") {
|
|
114
|
+
$("setup-device-block").style.display = "";
|
|
115
|
+
$("setup-manual-toggle").style.display = "";
|
|
116
|
+
$("setup-manual-section").style.display = "none";
|
|
117
|
+
}
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
// Step 2 — API key setup
|
|
@@ -367,9 +372,20 @@ const Onboard = (() => {
|
|
|
367
372
|
|
|
368
373
|
$("setup-btn-test").addEventListener("click", _testAndSave);
|
|
369
374
|
|
|
370
|
-
|
|
375
|
+
$("setup-manual-toggle").addEventListener("click", () => {
|
|
376
|
+
$("setup-device-block").style.display = "none";
|
|
377
|
+
$("setup-manual-toggle").style.display = "none";
|
|
378
|
+
$("setup-manual-section").style.display = "";
|
|
379
|
+
});
|
|
380
|
+
|
|
371
381
|
$("setup-btn-back").addEventListener("click", () => {
|
|
372
|
-
|
|
382
|
+
if ($("setup-manual-section").style.display !== "none") {
|
|
383
|
+
$("setup-device-block").style.display = "";
|
|
384
|
+
$("setup-manual-toggle").style.display = "";
|
|
385
|
+
$("setup-manual-section").style.display = "none";
|
|
386
|
+
} else {
|
|
387
|
+
_showSetupStep("lang");
|
|
388
|
+
}
|
|
373
389
|
});
|
|
374
390
|
|
|
375
391
|
_bindDeviceStep();
|
|
@@ -559,20 +575,7 @@ const Onboard = (() => {
|
|
|
559
575
|
async function _launchOnboardSession() {
|
|
560
576
|
try {
|
|
561
577
|
await _complete();
|
|
562
|
-
|
|
563
|
-
method: "POST",
|
|
564
|
-
headers: { "Content-Type": "application/json" },
|
|
565
|
-
body: JSON.stringify({ name: "Onboard", source: "setup" })
|
|
566
|
-
});
|
|
567
|
-
const data = await res.json();
|
|
568
|
-
const session = data.session;
|
|
569
|
-
if (!session) throw new Error("No session returned");
|
|
570
|
-
|
|
571
|
-
Sessions.add(session);
|
|
572
|
-
Sessions.renderList();
|
|
573
|
-
Sessions.setPendingMessage(session.id, `/onboard lang:${_selectedLang}`);
|
|
574
|
-
Sessions.select(session.id);
|
|
575
|
-
|
|
578
|
+
await Sessions.startWith(`/onboard lang:${_selectedLang}`, { name: "Onboard", source: "setup" });
|
|
576
579
|
_bootUI();
|
|
577
580
|
} catch (_) {
|
|
578
581
|
// Fallback: just boot normally if session creation fails
|