openclacky 1.3.0 → 1.3.2
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 +30 -0
- data/lib/clacky/agent/session_serializer.rb +16 -6
- data/lib/clacky/brand_config.rb +1 -1
- data/lib/clacky/providers.rb +1 -1
- data/lib/clacky/server/http_server.rb +35 -1
- data/lib/clacky/utils/environment_detector.rb +16 -0
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +192 -38
- data/lib/clacky/web/app.js +51 -0
- data/lib/clacky/web/billing.js +134 -8
- data/lib/clacky/web/i18n.js +12 -0
- data/lib/clacky/web/index.html +2 -0
- data/lib/clacky/web/onboard.js +8 -1
- data/lib/clacky/web/sessions.js +11 -0
- data/lib/clacky/web/settings.js +8 -8
- data/lib/clacky/web/skills.js +81 -6
- data/lib/clacky/web/tasks.js +18 -10
- data/lib/clacky/web/workspace.js +104 -0
- metadata +4 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed703cddac3ffa9887b5c500c5dfbe57c895eb5aab0e9dc3338592c9317555ec
|
|
4
|
+
data.tar.gz: 67d21c3c857e72bb729cfcecbdb3749eaaac1c76b21e1d0600081d1e4d79e880
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4fe46c92c67e5937bf24e325be47046a9ebf555afb722aaaaf108a17b2c13403c7e42c08d4b1a7abafad5c5ffb5d79d2749264c051572d157d0094dcb067ac78
|
|
7
|
+
data.tar.gz: 5fa8e909159560aa13944c0435716e7a98265504c6f61e8cfc298bc09553aa218ac404423d4e9b45f5a8d17563f0ae8a7a50547460fb497e06a98bc46b55aa53
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.3.2] - 2026-06-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Right-click context menu on workspace file tree with "Reveal in Finder" support (macOS/Linux/WSL)
|
|
12
|
+
- Resizable workspace panel with a drag handle
|
|
13
|
+
- Expandable task prompt preview — click a task card to view the full prompt
|
|
14
|
+
|
|
15
|
+
### Improved
|
|
16
|
+
- Skill cards now show a delete button, with shimmer skeleton loading while fetching
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- Every-N-hours cron schedule label now renders correctly (e.g. `0 */2 * * *`)
|
|
20
|
+
- Billing sessions list now shows all rows instead of being clipped
|
|
21
|
+
- Session replay skips reasoning-only assistant messages to match the live view
|
|
22
|
+
- Set-as-default button in settings aligned to the footer row with the top-up link
|
|
23
|
+
|
|
24
|
+
## [1.3.1] - 2026-06-17
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- Delete skills directly from the Web UI
|
|
28
|
+
|
|
29
|
+
### Improved
|
|
30
|
+
- Sidebar is now draggable to resize, with width persisted across sessions
|
|
31
|
+
- Sidebar scrollbar only appears while scrolling for a cleaner look
|
|
32
|
+
- Billing page UI polish and mobile adaptation
|
|
33
|
+
- Default openclacky image model switched to Nano Banana 2
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- Onboarding device-login window no longer gets blocked as a popup
|
|
37
|
+
|
|
8
38
|
## [1.3.0] - 2026-06-17
|
|
9
39
|
|
|
10
40
|
### Added
|
|
@@ -502,14 +502,24 @@ module Clacky
|
|
|
502
502
|
|
|
503
503
|
case msg[:role].to_s
|
|
504
504
|
when "assistant"
|
|
505
|
-
#
|
|
506
|
-
#
|
|
507
|
-
|
|
505
|
+
# Mirror the live guard at agent.rb (`if response[:content] && !response[:content].empty?`):
|
|
506
|
+
# only emit an assistant_message when the model produced actual content.
|
|
507
|
+
# Reasoning-only turns (empty content + reasoning_content + tool_calls)
|
|
508
|
+
# are silent in live mode; on replay they must stay silent too — otherwise
|
|
509
|
+
# a phantom <think>-only bubble splits consecutive tool_calls into separate
|
|
510
|
+
# UI groups, breaking the "N tool(s) used" collapse after refresh (C-5672).
|
|
511
|
+
raw_text = extract_text_from_content(msg[:content]).to_s.strip
|
|
508
512
|
reasoning = msg[:reasoning_content]
|
|
509
|
-
|
|
510
|
-
text =
|
|
513
|
+
unless raw_text.empty?
|
|
514
|
+
text = if reasoning && !reasoning.to_s.strip.empty?
|
|
515
|
+
# Prepend reasoning wrapped in <think> tags so the Web UI renders it
|
|
516
|
+
# as a collapsible thinking block.
|
|
517
|
+
"<think>\n#{reasoning}\n</think>\n#{raw_text}"
|
|
518
|
+
else
|
|
519
|
+
raw_text
|
|
520
|
+
end
|
|
521
|
+
ui.show_assistant_message(text, files: [])
|
|
511
522
|
end
|
|
512
|
-
ui.show_assistant_message(text, files: []) unless text.empty?
|
|
513
523
|
|
|
514
524
|
# Tool calls embedded in assistant message
|
|
515
525
|
Array(msg[:tool_calls]).each do |tc|
|
data/lib/clacky/brand_config.rb
CHANGED
|
@@ -961,7 +961,7 @@ module Clacky
|
|
|
961
961
|
#
|
|
962
962
|
# @param skill_name [String] The slug/name of the skill to remove.
|
|
963
963
|
# @return [void]
|
|
964
|
-
|
|
964
|
+
def delete_brand_skill!(skill_name)
|
|
965
965
|
# Remove files from disk.
|
|
966
966
|
skill_dir = File.join(brand_skills_dir, skill_name)
|
|
967
967
|
FileUtils.rm_rf(skill_dir) if Dir.exist?(skill_dir)
|
data/lib/clacky/providers.rb
CHANGED
|
@@ -59,7 +59,7 @@ module Clacky
|
|
|
59
59
|
"or-gemini-3-1-flash-image" => "Nano Banana 2",
|
|
60
60
|
"or-gpt-image-2" => "GPT Image 2"
|
|
61
61
|
},
|
|
62
|
-
"default_image_model" => "or-
|
|
62
|
+
"default_image_model" => "or-gemini-3-1-flash-image",
|
|
63
63
|
# Video generation models served by the openclacky gateway, which
|
|
64
64
|
# routes them to Vertex AI Veo (async predictLongRunning under the
|
|
65
65
|
# hood; the gateway hides the polling and returns the MP4 inline).
|
|
@@ -627,6 +627,12 @@ module Clacky
|
|
|
627
627
|
elsif method == "PATCH" && path.match?(%r{^/api/skills/[^/]+/toggle$})
|
|
628
628
|
name = URI.decode_www_form_component(path.sub("/api/skills/", "").sub("/toggle", ""))
|
|
629
629
|
api_toggle_skill(name, req, res)
|
|
630
|
+
elsif method == "DELETE" && path.match?(%r{^/api/skills/[^/]+$})
|
|
631
|
+
name = URI.decode_www_form_component(path.sub("/api/skills/", ""))
|
|
632
|
+
api_delete_skill(name, res)
|
|
633
|
+
elsif method == "DELETE" && path.match?(%r{^/api/brand/skills/[^/]+$})
|
|
634
|
+
slug = URI.decode_www_form_component(path.sub("/api/brand/skills/", ""))
|
|
635
|
+
api_delete_brand_skill(slug, res)
|
|
630
636
|
elsif method == "POST" && path.match?(%r{^/api/brand/skills/[^/]+/install$})
|
|
631
637
|
slug = URI.decode_www_form_component(path.sub("/api/brand/skills/", "").sub("/install", ""))
|
|
632
638
|
api_brand_skill_install(slug, req, res)
|
|
@@ -1939,6 +1945,23 @@ module Clacky
|
|
|
1939
1945
|
json_response(res, 500, { ok: false, error: e.message })
|
|
1940
1946
|
end
|
|
1941
1947
|
|
|
1948
|
+
# DELETE /api/brand/skills/:slug
|
|
1949
|
+
# Uninstalls a brand skill by removing its files and metadata.
|
|
1950
|
+
def api_delete_brand_skill(slug, res)
|
|
1951
|
+
brand = Clacky::BrandConfig.load
|
|
1952
|
+
installed = brand.installed_brand_skills
|
|
1953
|
+
unless installed.key?(slug)
|
|
1954
|
+
json_response(res, 404, { ok: false, error: "Brand skill '#{slug}' is not installed" })
|
|
1955
|
+
return
|
|
1956
|
+
end
|
|
1957
|
+
|
|
1958
|
+
brand.delete_brand_skill!(slug)
|
|
1959
|
+
@skill_loader = Clacky::SkillLoader.new(working_dir: nil, brand_config: brand)
|
|
1960
|
+
json_response(res, 200, { ok: true })
|
|
1961
|
+
rescue StandardError => e
|
|
1962
|
+
json_response(res, 500, { ok: false, error: e.message })
|
|
1963
|
+
end
|
|
1964
|
+
|
|
1942
1965
|
# GET /api/brand
|
|
1943
1966
|
# Returns brand metadata consumed by the WebUI on boot
|
|
1944
1967
|
# to dynamically replace branding strings.
|
|
@@ -2892,10 +2915,13 @@ module Clacky
|
|
|
2892
2915
|
result = Utils::EnvironmentDetector.open_file(linux_path)
|
|
2893
2916
|
return json_response(res, 501, { error: "unsupported OS" }) if result.nil?
|
|
2894
2917
|
json_response(res, 200, { ok: true })
|
|
2918
|
+
when "reveal"
|
|
2919
|
+
Utils::EnvironmentDetector.reveal_file(linux_path)
|
|
2920
|
+
json_response(res, 200, { ok: true })
|
|
2895
2921
|
when "download"
|
|
2896
2922
|
serve_file_download(res, linux_path)
|
|
2897
2923
|
else
|
|
2898
|
-
json_response(res, 400, { error: "invalid action. Must be 'open' or 'download'" })
|
|
2924
|
+
json_response(res, 400, { error: "invalid action. Must be 'open', 'reveal' or 'download'" })
|
|
2899
2925
|
end
|
|
2900
2926
|
rescue => e
|
|
2901
2927
|
json_response(res, 500, { ok: false, error: e.message })
|
|
@@ -3463,6 +3489,14 @@ module Clacky
|
|
|
3463
3489
|
json_response(res, 422, { error: e.message })
|
|
3464
3490
|
end
|
|
3465
3491
|
|
|
3492
|
+
private def api_delete_skill(name, res)
|
|
3493
|
+
skill = @skill_loader[name]
|
|
3494
|
+
return json_response(res, 404, { error: "Skill not found: #{name}" }) unless skill
|
|
3495
|
+
|
|
3496
|
+
FileUtils.rm_rf(skill.directory)
|
|
3497
|
+
json_response(res, 200, { ok: true })
|
|
3498
|
+
end
|
|
3499
|
+
|
|
3466
3500
|
# POST /api/my-skills/:name/publish
|
|
3467
3501
|
# GET /api/creator/skills
|
|
3468
3502
|
# Returns two separate groups:
|
|
@@ -36,6 +36,22 @@ module Clacky
|
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
# Reveal a file in the OS file manager (select/highlight it).
|
|
40
|
+
# macOS: open -R; Linux: xdg-open on parent dir; WSL: explorer /select
|
|
41
|
+
# @param path [String] Linux-side file path
|
|
42
|
+
# @return [Boolean, nil] true/false from system(), or nil on unsupported OS
|
|
43
|
+
def self.reveal_file(path)
|
|
44
|
+
case os_type
|
|
45
|
+
when :macos then system("open", "-R", path)
|
|
46
|
+
when :linux then system("xdg-open", File.dirname(path))
|
|
47
|
+
when :wsl
|
|
48
|
+
win_path = linux_to_win_path(path)
|
|
49
|
+
system("explorer.exe", "/select,#{win_path}")
|
|
50
|
+
else
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
39
55
|
# Convert a Windows-style path to a WSL/Linux-side path.
|
|
40
56
|
# e.g. "C:/Users/foo/file.txt" → "/mnt/c/Users/foo/file.txt"
|
|
41
57
|
# Returns the original path unchanged on non-WSL or if already a Linux path.
|
data/lib/clacky/version.rb
CHANGED
data/lib/clacky/web/app.css
CHANGED
|
@@ -250,7 +250,7 @@ html { font-size: 16px; }
|
|
|
250
250
|
.skel-label { height: 0.75rem; width: 4rem; margin-top: 0.375rem; }
|
|
251
251
|
.skel-block { height: 9rem; width: 100%; }
|
|
252
252
|
.skel-block-sm { height: 5rem; width: 100%; }
|
|
253
|
-
.skel-heatmap { height:
|
|
253
|
+
.skel-heatmap { height: 5rem; width: 178px; }
|
|
254
254
|
|
|
255
255
|
|
|
256
256
|
|
|
@@ -375,7 +375,7 @@ body {
|
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
.header-logo-img {
|
|
378
|
-
height:
|
|
378
|
+
height: 2rem;
|
|
379
379
|
object-fit: contain;
|
|
380
380
|
display: block;
|
|
381
381
|
flex-shrink: 0;
|
|
@@ -498,10 +498,6 @@ body {
|
|
|
498
498
|
.theme-toggle-btn:active {
|
|
499
499
|
background: var(--color-bg-hover);
|
|
500
500
|
}
|
|
501
|
-
/* Sound-notification toggle shares .theme-toggle-btn; highlight when ON. */
|
|
502
|
-
#notify-toggle-header.notify-on:hover {
|
|
503
|
-
color: var(--color-accent-primary, var(--color-text-primary));
|
|
504
|
-
}
|
|
505
501
|
|
|
506
502
|
/* ── Content Row (Sidebar + Main) ───────────────────────────────────────── */
|
|
507
503
|
#app > aside,
|
|
@@ -530,8 +526,9 @@ body {
|
|
|
530
526
|
/* The sidebar sits on the outer frame (bg-primary) so it's visibly a layer
|
|
531
527
|
BEHIND the chat surface (bg-secondary). Subtle right border separates them. */
|
|
532
528
|
#sidebar {
|
|
533
|
-
width: 15rem;
|
|
534
|
-
|
|
529
|
+
--sidebar-width: 15rem;
|
|
530
|
+
width: var(--sidebar-width);
|
|
531
|
+
min-width: var(--sidebar-width);
|
|
535
532
|
background: var(--color-bg-primary);
|
|
536
533
|
border-right: 1px solid var(--color-border-primary);
|
|
537
534
|
display: flex;
|
|
@@ -540,9 +537,22 @@ body {
|
|
|
540
537
|
height: 100%;
|
|
541
538
|
flex-shrink: 0;
|
|
542
539
|
margin-left: 0;
|
|
540
|
+
position: relative;
|
|
543
541
|
}
|
|
544
542
|
#sidebar.hidden {
|
|
545
|
-
margin-left: -
|
|
543
|
+
margin-left: calc(-1 * var(--sidebar-width));
|
|
544
|
+
}
|
|
545
|
+
#sidebar-resize-handle {
|
|
546
|
+
position: absolute;
|
|
547
|
+
top: 0;
|
|
548
|
+
right: -3px;
|
|
549
|
+
bottom: 0;
|
|
550
|
+
width: 6px;
|
|
551
|
+
cursor: col-resize;
|
|
552
|
+
z-index: 10;
|
|
553
|
+
}
|
|
554
|
+
#sidebar:has(#sidebar-resize-handle.active) {
|
|
555
|
+
transition: background-color var(--transition-base);
|
|
546
556
|
}
|
|
547
557
|
#sidebar-header {
|
|
548
558
|
display: flex;
|
|
@@ -578,6 +588,22 @@ body {
|
|
|
578
588
|
flex-direction: column;
|
|
579
589
|
min-height: 0;
|
|
580
590
|
padding: 0 6px 8px;
|
|
591
|
+
margin-right: -1px;
|
|
592
|
+
scrollbar-width: thin;
|
|
593
|
+
scrollbar-color: transparent transparent;
|
|
594
|
+
}
|
|
595
|
+
#sidebar-list.is-scrolling {
|
|
596
|
+
scrollbar-color: var(--color-border-secondary) transparent;
|
|
597
|
+
}
|
|
598
|
+
#sidebar-list::-webkit-scrollbar { width: 4px; }
|
|
599
|
+
#sidebar-list::-webkit-scrollbar-track { background: transparent; }
|
|
600
|
+
#sidebar-list::-webkit-scrollbar-thumb {
|
|
601
|
+
background: transparent;
|
|
602
|
+
border-radius: 4px;
|
|
603
|
+
transition: background 0.4s;
|
|
604
|
+
}
|
|
605
|
+
#sidebar-list.is-scrolling::-webkit-scrollbar-thumb {
|
|
606
|
+
background: var(--color-border-secondary);
|
|
581
607
|
}
|
|
582
608
|
#session-list { padding: 4px 0 0; min-height: 6.75rem; }
|
|
583
609
|
|
|
@@ -598,10 +624,6 @@ body {
|
|
|
598
624
|
background: var(--color-bg-primary);
|
|
599
625
|
z-index: 10;
|
|
600
626
|
}
|
|
601
|
-
.sidebar-divider:first-child {
|
|
602
|
-
margin-top: 0;
|
|
603
|
-
padding-top: 18px;
|
|
604
|
-
}
|
|
605
627
|
.sidebar-divider span {
|
|
606
628
|
white-space: nowrap;
|
|
607
629
|
flex: 0 0 auto;
|
|
@@ -1732,6 +1754,30 @@ body {
|
|
|
1732
1754
|
overflow: hidden;
|
|
1733
1755
|
text-overflow: ellipsis;
|
|
1734
1756
|
}
|
|
1757
|
+
.task-card-preview-expandable {
|
|
1758
|
+
cursor: pointer;
|
|
1759
|
+
}
|
|
1760
|
+
.task-card-preview-expandable:hover {
|
|
1761
|
+
color: var(--color-accent-primary);
|
|
1762
|
+
}
|
|
1763
|
+
.task-card-detail {
|
|
1764
|
+
margin-top: 0.625rem;
|
|
1765
|
+
padding: 0.625rem 0.75rem;
|
|
1766
|
+
background: var(--color-bg-primary);
|
|
1767
|
+
border: 1px solid var(--color-border-primary);
|
|
1768
|
+
border-radius: 6px;
|
|
1769
|
+
}
|
|
1770
|
+
.task-card-detail-content {
|
|
1771
|
+
margin: 0;
|
|
1772
|
+
font-size: 0.75rem;
|
|
1773
|
+
font-family: inherit;
|
|
1774
|
+
color: var(--color-text-secondary);
|
|
1775
|
+
line-height: 1.6;
|
|
1776
|
+
white-space: pre-wrap;
|
|
1777
|
+
word-break: break-word;
|
|
1778
|
+
max-height: 12rem;
|
|
1779
|
+
overflow-y: auto;
|
|
1780
|
+
}
|
|
1735
1781
|
.task-card-actions {
|
|
1736
1782
|
display: flex;
|
|
1737
1783
|
align-items: center;
|
|
@@ -1885,7 +1931,8 @@ body {
|
|
|
1885
1931
|
|
|
1886
1932
|
/* ── Workspace panel (right file browser) ───────────────────────────────── */
|
|
1887
1933
|
#workspace-panel {
|
|
1888
|
-
width: 280px;
|
|
1934
|
+
--workspace-width: 280px;
|
|
1935
|
+
width: var(--workspace-width);
|
|
1889
1936
|
flex-shrink: 0;
|
|
1890
1937
|
display: flex;
|
|
1891
1938
|
flex-direction: column;
|
|
@@ -1893,8 +1940,21 @@ body {
|
|
|
1893
1940
|
background: var(--color-bg-primary);
|
|
1894
1941
|
overflow: hidden;
|
|
1895
1942
|
transition: width var(--transition-base);
|
|
1943
|
+
position: relative;
|
|
1896
1944
|
}
|
|
1897
1945
|
#workspace-panel.collapsed { width: 0; border-left: none; }
|
|
1946
|
+
#workspace-resize-handle {
|
|
1947
|
+
position: absolute;
|
|
1948
|
+
top: 0;
|
|
1949
|
+
left: -3px;
|
|
1950
|
+
bottom: 0;
|
|
1951
|
+
width: 6px;
|
|
1952
|
+
cursor: col-resize;
|
|
1953
|
+
z-index: 10;
|
|
1954
|
+
}
|
|
1955
|
+
#workspace-panel:has(#workspace-resize-handle.active) {
|
|
1956
|
+
transition: background-color var(--transition-base);
|
|
1957
|
+
}
|
|
1898
1958
|
#workspace-header {
|
|
1899
1959
|
display: flex;
|
|
1900
1960
|
align-items: center;
|
|
@@ -2128,7 +2188,7 @@ body {
|
|
|
2128
2188
|
.msg-user .msg-time { color: var(--color-text-secondary); right: 0; left: auto; padding-right: 0.25rem; }
|
|
2129
2189
|
.msg-assistant .msg-time { color: var(--color-text-secondary); left: 0; right: auto; padding-left: 0.25rem; }
|
|
2130
2190
|
|
|
2131
|
-
.msg-user { background: var(--color-accent-soft); color: var(--color-text-primary); align-self: flex-end; white-space: pre-wrap;
|
|
2191
|
+
.msg-user { background: var(--color-accent-soft); color: var(--color-text-primary); align-self: flex-end; white-space: pre-wrap; }
|
|
2132
2192
|
[data-theme="dark"] .msg-user { background: var(--color-accent-soft); }
|
|
2133
2193
|
.msg-assistant { background: var(--color-bg-tertiary); border: 1px solid var(--color-border-primary); align-self: flex-start; }
|
|
2134
2194
|
|
|
@@ -2777,11 +2837,11 @@ body {
|
|
|
2777
2837
|
#session-info-bar {
|
|
2778
2838
|
display: flex;
|
|
2779
2839
|
align-items: center;
|
|
2780
|
-
gap:
|
|
2840
|
+
gap: 0.375rem;
|
|
2781
2841
|
flex-wrap: wrap;
|
|
2782
|
-
padding:
|
|
2783
|
-
margin-left:
|
|
2784
|
-
margin-right:
|
|
2842
|
+
padding: 0.375rem 0.75rem;
|
|
2843
|
+
margin-left: 1.5rem;
|
|
2844
|
+
margin-right: 1.5rem;
|
|
2785
2845
|
background: transparent;
|
|
2786
2846
|
border-top: none;
|
|
2787
2847
|
font-size: 0.6875rem;
|
|
@@ -4611,14 +4671,15 @@ body {
|
|
|
4611
4671
|
}
|
|
4612
4672
|
.model-card-grid-toolbar {
|
|
4613
4673
|
display: flex;
|
|
4614
|
-
flex-wrap:
|
|
4674
|
+
flex-wrap: nowrap;
|
|
4615
4675
|
gap: 0.375rem;
|
|
4616
4676
|
margin-left: auto;
|
|
4617
4677
|
justify-content: flex-end;
|
|
4618
4678
|
}
|
|
4619
4679
|
.model-card-grid-footer {
|
|
4620
4680
|
display: flex;
|
|
4621
|
-
justify-content:
|
|
4681
|
+
justify-content: space-between;
|
|
4682
|
+
align-items: center;
|
|
4622
4683
|
margin-top: 0.25rem;
|
|
4623
4684
|
}
|
|
4624
4685
|
.model-card-grid-link {
|
|
@@ -6541,6 +6602,24 @@ body {
|
|
|
6541
6602
|
background: var(--color-success-bg);
|
|
6542
6603
|
}
|
|
6543
6604
|
|
|
6605
|
+
/* ── Delete button ────────────────────────────────────────────────────────── */
|
|
6606
|
+
.btn-skill-delete {
|
|
6607
|
+
background: transparent;
|
|
6608
|
+
border: 1px solid transparent;
|
|
6609
|
+
border-radius: 6px;
|
|
6610
|
+
color: var(--color-text-tertiary);
|
|
6611
|
+
cursor: pointer;
|
|
6612
|
+
padding: 0.25rem;
|
|
6613
|
+
display: inline-flex;
|
|
6614
|
+
align-items: center;
|
|
6615
|
+
transition: color .15s, border-color .15s, background .15s;
|
|
6616
|
+
}
|
|
6617
|
+
.btn-skill-delete:hover {
|
|
6618
|
+
color: var(--color-error, #c0392b);
|
|
6619
|
+
border-color: var(--color-error-border, #f5c6c6);
|
|
6620
|
+
background: var(--color-error-bg, #fff0f0);
|
|
6621
|
+
}
|
|
6622
|
+
|
|
6544
6623
|
/* ── Skills sidebar section ──────────────────────────────────────────────── */
|
|
6545
6624
|
#skill-list-items { padding: 0 0.5rem 0.5rem; display: flex; flex-direction: column; gap: 2px; }
|
|
6546
6625
|
|
|
@@ -9712,9 +9791,6 @@ body.setup-mode[data-theme="dark"] {
|
|
|
9712
9791
|
border-radius: 12px;
|
|
9713
9792
|
transition: border-color 0.15s ease;
|
|
9714
9793
|
}
|
|
9715
|
-
.billing-stat-card:hover {
|
|
9716
|
-
border-color: color-mix(in srgb, var(--color-accent-primary) 40%, transparent);
|
|
9717
|
-
}
|
|
9718
9794
|
.billing-stat-primary {
|
|
9719
9795
|
border-color: color-mix(in srgb, var(--color-accent-primary) 35%, transparent);
|
|
9720
9796
|
}
|
|
@@ -9762,7 +9838,12 @@ body.setup-mode[data-theme="dark"] {
|
|
|
9762
9838
|
}
|
|
9763
9839
|
|
|
9764
9840
|
/* ── Heatmap ─────────────────────────────────────────────────────────── */
|
|
9765
|
-
.billing-heatmap-row {
|
|
9841
|
+
.billing-heatmap-row {
|
|
9842
|
+
display: grid;
|
|
9843
|
+
grid-template-columns: auto 1fr;
|
|
9844
|
+
gap: 1rem;
|
|
9845
|
+
width: 100%;
|
|
9846
|
+
}
|
|
9766
9847
|
.billing-heat-dow-row {
|
|
9767
9848
|
display: grid;
|
|
9768
9849
|
grid-template-columns: repeat(7, 28px);
|
|
@@ -9790,11 +9871,11 @@ body.setup-mode[data-theme="dark"] {
|
|
|
9790
9871
|
}
|
|
9791
9872
|
.billing-heat-cell.is-empty { background: transparent; }
|
|
9792
9873
|
.billing-heat-cell[data-level="0"] { background: var(--color-border-primary); }
|
|
9793
|
-
.billing-heat-cell[data-level="1"] { background:
|
|
9794
|
-
.billing-heat-cell[data-level="2"] { background:
|
|
9795
|
-
.billing-heat-cell[data-level="3"] { background:
|
|
9796
|
-
.billing-heat-cell[data-level="4"] { background:
|
|
9797
|
-
.billing-heat-cell[data-level="5"] { background:
|
|
9874
|
+
.billing-heat-cell[data-level="1"] { background: color-mix(in srgb, var(--color-accent-primary) 20%, var(--color-bg-secondary)); }
|
|
9875
|
+
.billing-heat-cell[data-level="2"] { background: color-mix(in srgb, var(--color-accent-primary) 40%, var(--color-bg-secondary)); }
|
|
9876
|
+
.billing-heat-cell[data-level="3"] { background: color-mix(in srgb, var(--color-accent-primary) 60%, var(--color-bg-secondary)); }
|
|
9877
|
+
.billing-heat-cell[data-level="4"] { background: color-mix(in srgb, var(--color-accent-primary) 80%, var(--color-bg-secondary)); }
|
|
9878
|
+
.billing-heat-cell[data-level="5"] { background: var(--color-accent-primary); }
|
|
9798
9879
|
.billing-heat-legend {
|
|
9799
9880
|
display: flex;
|
|
9800
9881
|
align-items: center;
|
|
@@ -9808,6 +9889,63 @@ body.setup-mode[data-theme="dark"] {
|
|
|
9808
9889
|
aspect-ratio: auto;
|
|
9809
9890
|
}
|
|
9810
9891
|
|
|
9892
|
+
/* ── Heatmap + Trend two-column row ──────────────────────────────────── */
|
|
9893
|
+
.billing-heatmap-card,
|
|
9894
|
+
.billing-trend-card {
|
|
9895
|
+
min-width: 0;
|
|
9896
|
+
min-height: 140px;
|
|
9897
|
+
}
|
|
9898
|
+
|
|
9899
|
+
/* ── Cost Trend Line Chart ───────────────────────────────────────────── */
|
|
9900
|
+
.billing-trend-total {
|
|
9901
|
+
font-size: 0.75rem;
|
|
9902
|
+
font-weight: 600;
|
|
9903
|
+
color: var(--color-accent-primary);
|
|
9904
|
+
}
|
|
9905
|
+
.billing-trend-chart {
|
|
9906
|
+
flex: 1;
|
|
9907
|
+
min-height: 0;
|
|
9908
|
+
}
|
|
9909
|
+
.billing-trend-svg {
|
|
9910
|
+
width: 100%;
|
|
9911
|
+
height: 100%;
|
|
9912
|
+
overflow: visible;
|
|
9913
|
+
}
|
|
9914
|
+
.billing-trend-grid-line {
|
|
9915
|
+
stroke: var(--color-border-primary);
|
|
9916
|
+
stroke-width: 1;
|
|
9917
|
+
}
|
|
9918
|
+
.billing-trend-y-label {
|
|
9919
|
+
font-size: 9px;
|
|
9920
|
+
fill: var(--color-text-secondary);
|
|
9921
|
+
text-anchor: end;
|
|
9922
|
+
}
|
|
9923
|
+
.billing-trend-x-label {
|
|
9924
|
+
font-size: 9px;
|
|
9925
|
+
fill: var(--color-text-secondary);
|
|
9926
|
+
text-anchor: middle;
|
|
9927
|
+
}
|
|
9928
|
+
.billing-trend-line {
|
|
9929
|
+
stroke: var(--color-accent-primary);
|
|
9930
|
+
stroke-width: 1.5;
|
|
9931
|
+
stroke-linecap: round;
|
|
9932
|
+
stroke-linejoin: round;
|
|
9933
|
+
}
|
|
9934
|
+
.billing-trend-area {
|
|
9935
|
+
fill: url(#billing-trend-grad);
|
|
9936
|
+
}
|
|
9937
|
+
.billing-trend-dot {
|
|
9938
|
+
fill: var(--color-accent-primary);
|
|
9939
|
+
stroke: var(--color-bg-secondary);
|
|
9940
|
+
stroke-width: 2;
|
|
9941
|
+
cursor: pointer;
|
|
9942
|
+
opacity: 0;
|
|
9943
|
+
transition: opacity 0.15s;
|
|
9944
|
+
}
|
|
9945
|
+
.billing-trend-svg:hover .billing-trend-dot {
|
|
9946
|
+
opacity: 1;
|
|
9947
|
+
}
|
|
9948
|
+
|
|
9811
9949
|
/* ── Chart Card base ─────────────────────────────────────────────────── */
|
|
9812
9950
|
.billing-chart-row { width: 100%; }
|
|
9813
9951
|
.billing-chart-card {
|
|
@@ -10002,6 +10140,8 @@ body.setup-mode[data-theme="dark"] {
|
|
|
10002
10140
|
border: 1px solid var(--color-border-primary);
|
|
10003
10141
|
border-radius: 12px;
|
|
10004
10142
|
padding: 1.25rem;
|
|
10143
|
+
height: 16rem;
|
|
10144
|
+
overflow-y: auto;
|
|
10005
10145
|
}
|
|
10006
10146
|
.billing-section h3 {
|
|
10007
10147
|
font-size: 0.9375rem;
|
|
@@ -10103,6 +10243,7 @@ body.setup-mode[data-theme="dark"] {
|
|
|
10103
10243
|
height: 100%;
|
|
10104
10244
|
background: var(--color-accent-primary);
|
|
10105
10245
|
border-radius: 3px;
|
|
10246
|
+
min-width: 8px;
|
|
10106
10247
|
}
|
|
10107
10248
|
.billing-model-cost {
|
|
10108
10249
|
font-size: 0.8125rem;
|
|
@@ -10603,6 +10744,9 @@ body.setup-mode[data-theme="dark"] {
|
|
|
10603
10744
|
text-align: right;
|
|
10604
10745
|
color: var(--color-text-primary);
|
|
10605
10746
|
}
|
|
10747
|
+
.billing-cell-total {
|
|
10748
|
+
color: var(--color-accent-primary);
|
|
10749
|
+
}
|
|
10606
10750
|
.billing-cell-hit {
|
|
10607
10751
|
color: #3b82f6;
|
|
10608
10752
|
}
|
|
@@ -11180,6 +11324,7 @@ body.setup-mode[data-theme="dark"] {
|
|
|
11180
11324
|
transform: translateX(-100%);
|
|
11181
11325
|
margin-left: 0 !important;
|
|
11182
11326
|
}
|
|
11327
|
+
#sidebar-resize-handle { display: none; }
|
|
11183
11328
|
|
|
11184
11329
|
/* Overlay backdrop */
|
|
11185
11330
|
#sidebar-overlay {
|
|
@@ -11219,7 +11364,9 @@ body.setup-mode[data-theme="dark"] {
|
|
|
11219
11364
|
|
|
11220
11365
|
/* Session info bar: single-line, no hover-expand, font smaller */
|
|
11221
11366
|
#session-info-bar {
|
|
11222
|
-
padding: 0.1875rem 0.
|
|
11367
|
+
padding: 0.1875rem 0.5rem;
|
|
11368
|
+
margin-left: 0.75rem;
|
|
11369
|
+
margin-right: 0.75rem;
|
|
11223
11370
|
font-size: 0.625rem;
|
|
11224
11371
|
overflow: hidden;
|
|
11225
11372
|
white-space: nowrap;
|
|
@@ -11246,9 +11393,14 @@ body.setup-mode[data-theme="dark"] {
|
|
|
11246
11393
|
margin: 0 0.25rem;
|
|
11247
11394
|
}
|
|
11248
11395
|
|
|
11396
|
+
/* Tighten input area outer padding on mobile */
|
|
11397
|
+
#input-area {
|
|
11398
|
+
padding: 0px 12px 12px;
|
|
11399
|
+
}
|
|
11400
|
+
|
|
11249
11401
|
/* Input bar: proportional spacing, touch-friendly */
|
|
11250
11402
|
#input-bar {
|
|
11251
|
-
padding: 0.
|
|
11403
|
+
padding: 0.375rem 0.5rem;
|
|
11252
11404
|
gap: 0.25rem;
|
|
11253
11405
|
}
|
|
11254
11406
|
|
|
@@ -11264,12 +11416,12 @@ body.setup-mode[data-theme="dark"] {
|
|
|
11264
11416
|
/* Textarea: prevent iOS auto-zoom (must be ≥1rem) */
|
|
11265
11417
|
#user-input {
|
|
11266
11418
|
font-size: 1rem;
|
|
11267
|
-
padding: 0.
|
|
11419
|
+
padding: 0.1875rem 0.375rem;
|
|
11268
11420
|
}
|
|
11269
11421
|
|
|
11270
11422
|
/* Send button: bigger tap target */
|
|
11271
11423
|
#btn-send, #btn-interrupt {
|
|
11272
|
-
padding: 0.
|
|
11424
|
+
padding: 0.3125rem 0.75rem;
|
|
11273
11425
|
font-size: 0.875rem;
|
|
11274
11426
|
}
|
|
11275
11427
|
|
|
@@ -11329,7 +11481,7 @@ body.setup-mode[data-theme="dark"] {
|
|
|
11329
11481
|
min-height: 0;
|
|
11330
11482
|
}
|
|
11331
11483
|
#skills-body {
|
|
11332
|
-
padding: 1rem 1rem
|
|
11484
|
+
padding: 1rem 1rem 1rem;
|
|
11333
11485
|
gap: 0.75rem;
|
|
11334
11486
|
overflow-y: auto;
|
|
11335
11487
|
-webkit-overflow-scrolling: touch;
|
|
@@ -11430,9 +11582,11 @@ body.setup-mode[data-theme="dark"] {
|
|
|
11430
11582
|
}
|
|
11431
11583
|
|
|
11432
11584
|
/* ── Billing / Trash / Creator pages ── */
|
|
11433
|
-
#billing-body { padding: 1rem 1rem
|
|
11434
|
-
#trash-body { padding: 1rem 1rem
|
|
11435
|
-
#creator-body { padding: 1rem 1rem
|
|
11585
|
+
#billing-body { padding: 1rem 1rem 1rem; }
|
|
11586
|
+
#trash-body { padding: 1rem 1rem 1rem; }
|
|
11587
|
+
#creator-body { padding: 1rem 1rem 1rem; }
|
|
11588
|
+
|
|
11589
|
+
.billing-heatmap-row { grid-template-columns: 1fr; }
|
|
11436
11590
|
|
|
11437
11591
|
/* ── MCP page ── */
|
|
11438
11592
|
#mcp-panel {
|
data/lib/clacky/web/app.js
CHANGED
|
@@ -513,6 +513,57 @@ if ($("btn-toggle-sidebar")) {
|
|
|
513
513
|
// Tap overlay to close sidebar on mobile
|
|
514
514
|
$("sidebar-overlay").addEventListener("click", _closeSidebar);
|
|
515
515
|
|
|
516
|
+
// ── Sidebar resize ──────────────────────────────────────────────────────
|
|
517
|
+
(function _initSidebarResize() {
|
|
518
|
+
const sidebar = $("sidebar");
|
|
519
|
+
const handle = $("sidebar-resize-handle");
|
|
520
|
+
if (!sidebar || !handle) return;
|
|
521
|
+
|
|
522
|
+
const MIN_W = 12; // rem
|
|
523
|
+
const MAX_W = 32; // rem
|
|
524
|
+
const baseFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
525
|
+
|
|
526
|
+
let startX = 0;
|
|
527
|
+
let startW = 0;
|
|
528
|
+
|
|
529
|
+
// Restore saved width
|
|
530
|
+
const saved = localStorage.getItem("sidebar-width");
|
|
531
|
+
if (saved) {
|
|
532
|
+
const w = parseFloat(saved);
|
|
533
|
+
if (w >= MIN_W && w <= MAX_W) {
|
|
534
|
+
sidebar.style.setProperty("--sidebar-width", w + "rem");
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function _getWidth() {
|
|
539
|
+
return parseFloat(getComputedStyle(sidebar).getPropertyValue("--sidebar-width"));
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
handle.addEventListener("mousedown", (e) => {
|
|
543
|
+
e.preventDefault();
|
|
544
|
+
startX = e.clientX;
|
|
545
|
+
startW = _getWidth();
|
|
546
|
+
handle.classList.add("active");
|
|
547
|
+
document.body.style.cursor = "col-resize";
|
|
548
|
+
document.body.style.userSelect = "none";
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
document.addEventListener("mousemove", (e) => {
|
|
552
|
+
if (!handle.classList.contains("active")) return;
|
|
553
|
+
const dx = (e.clientX - startX) / baseFontSize;
|
|
554
|
+
const newW = Math.min(MAX_W, Math.max(MIN_W, startW + dx));
|
|
555
|
+
sidebar.style.setProperty("--sidebar-width", newW + "rem");
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
document.addEventListener("mouseup", () => {
|
|
559
|
+
if (!handle.classList.contains("active")) return;
|
|
560
|
+
handle.classList.remove("active");
|
|
561
|
+
document.body.style.cursor = "";
|
|
562
|
+
document.body.style.userSelect = "";
|
|
563
|
+
localStorage.setItem("sidebar-width", _getWidth());
|
|
564
|
+
});
|
|
565
|
+
})();
|
|
566
|
+
|
|
516
567
|
// On mobile: start with sidebar hidden
|
|
517
568
|
if (_isMobile()) _closeSidebar();
|
|
518
569
|
|