openclacky 1.3.2 → 1.3.4

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -0
  3. data/Dockerfile +3 -0
  4. data/README.md +1 -1
  5. data/README_JA.md +237 -0
  6. data/docs/rich_ui_guide.md +277 -0
  7. data/docs/rich_ui_refactor_plan.md +396 -0
  8. data/lib/clacky/agent/llm_caller.rb +10 -4
  9. data/lib/clacky/agent/session_serializer.rb +52 -7
  10. data/lib/clacky/agent/time_machine.rb +247 -26
  11. data/lib/clacky/agent.rb +15 -3
  12. data/lib/clacky/api_extension.rb +262 -0
  13. data/lib/clacky/api_extension_loader.rb +156 -0
  14. data/lib/clacky/cli.rb +93 -3
  15. data/lib/clacky/client.rb +38 -13
  16. data/lib/clacky/default_agents/_panels/git/panel.js +201 -0
  17. data/lib/clacky/default_agents/_panels/time_machine/panel.js +640 -0
  18. data/lib/clacky/default_agents/coding/profile.yml +3 -0
  19. data/lib/clacky/default_agents/coding/webui/.gitkeep +0 -0
  20. data/lib/clacky/default_skills/cron-task-creator/SKILL.md +1 -1
  21. data/lib/clacky/default_skills/extend-openclacky/SKILL.md +6 -4
  22. data/lib/clacky/default_skills/media-gen/SKILL.md +37 -10
  23. data/lib/clacky/idle_compression_timer.rb +3 -1
  24. data/lib/clacky/locales/en.rb +26 -0
  25. data/lib/clacky/locales/i18n.rb +26 -0
  26. data/lib/clacky/locales/zh.rb +26 -0
  27. data/lib/clacky/media/openai_compat.rb +64 -1
  28. data/lib/clacky/message_history.rb +9 -0
  29. data/lib/clacky/rich_ui/components/base_component.rb +50 -0
  30. data/lib/clacky/rich_ui/components/dialogs/approval_dialog.rb +142 -0
  31. data/lib/clacky/rich_ui/components/dialogs/config_menu_dialog.rb +106 -0
  32. data/lib/clacky/rich_ui/components/dialogs/form_dialog.rb +128 -0
  33. data/lib/clacky/rich_ui/components/sidebar.rb +119 -0
  34. data/lib/clacky/rich_ui/components/sidebar_panels.rb +134 -0
  35. data/lib/clacky/rich_ui/components/status_view.rb +58 -0
  36. data/lib/clacky/rich_ui/components/thinking_live_view.rb +79 -0
  37. data/lib/clacky/rich_ui/entry_tracker.rb +56 -0
  38. data/lib/clacky/rich_ui/layout_adapter.rb +16 -0
  39. data/lib/clacky/rich_ui/progress_handle_adapter.rb +24 -0
  40. data/lib/clacky/rich_ui/rich_ui_controller.rb +868 -0
  41. data/lib/clacky/rich_ui/shell/rich_agent_shell.rb +184 -0
  42. data/lib/clacky/rich_ui/view_renderer.rb +291 -0
  43. data/lib/clacky/rich_ui.rb +57 -0
  44. data/lib/clacky/rich_ui_controller.rb +3 -1549
  45. data/lib/clacky/server/api_extension_dispatcher.rb +120 -0
  46. data/lib/clacky/server/channel/channel_manager.rb +26 -0
  47. data/lib/clacky/server/git_panel.rb +115 -0
  48. data/lib/clacky/server/http_server.rb +547 -15
  49. data/lib/clacky/server/server_master.rb +6 -4
  50. data/lib/clacky/server/session_registry.rb +1 -1
  51. data/lib/clacky/shell_hook_loader.rb +1 -1
  52. data/lib/clacky/tools/edit.rb +14 -2
  53. data/lib/clacky/ui2/ui_controller.rb +7 -0
  54. data/lib/clacky/version.rb +1 -1
  55. data/lib/clacky/web/app.css +511 -101
  56. data/lib/clacky/web/app.js +95 -14
  57. data/lib/clacky/web/components/code-editor.js +197 -0
  58. data/lib/clacky/web/{notify.js → components/notify.js} +1 -1
  59. data/lib/clacky/web/{onboard.js → components/onboard.js} +18 -2
  60. data/lib/clacky/web/core/aside.js +117 -0
  61. data/lib/clacky/web/core/ext.js +387 -0
  62. data/lib/clacky/web/features/backup/store.js +92 -0
  63. data/lib/clacky/web/features/backup/view.js +94 -0
  64. data/lib/clacky/web/features/billing/store.js +163 -0
  65. data/lib/clacky/web/{billing.js → features/billing/view.js} +132 -240
  66. data/lib/clacky/web/features/brand/store.js +110 -0
  67. data/lib/clacky/web/{brand.js → features/brand/view.js} +49 -199
  68. data/lib/clacky/web/features/channels/store.js +103 -0
  69. data/lib/clacky/web/{channels.js → features/channels/view.js} +50 -127
  70. data/lib/clacky/web/features/creator/store.js +81 -0
  71. data/lib/clacky/web/{creator.js → features/creator/view.js} +53 -102
  72. data/lib/clacky/web/features/mcp/store.js +158 -0
  73. data/lib/clacky/web/{mcp.js → features/mcp/view.js} +57 -134
  74. data/lib/clacky/web/features/model-tester/store.js +77 -0
  75. data/lib/clacky/web/features/model-tester/view.js +7 -0
  76. data/lib/clacky/web/features/profile/store.js +170 -0
  77. data/lib/clacky/web/{profile.js → features/profile/view.js} +94 -144
  78. data/lib/clacky/web/features/share/store.js +145 -0
  79. data/lib/clacky/web/{share.js → features/share/view.js} +66 -202
  80. data/lib/clacky/web/features/skills/store.js +331 -0
  81. data/lib/clacky/web/features/skills/view.js +581 -0
  82. data/lib/clacky/web/features/tasks/store.js +135 -0
  83. data/lib/clacky/web/features/tasks/view.js +241 -0
  84. data/lib/clacky/web/features/trash/store.js +242 -0
  85. data/lib/clacky/web/{trash.js → features/trash/view.js} +102 -293
  86. data/lib/clacky/web/features/version/store.js +165 -0
  87. data/lib/clacky/web/features/version/view.js +323 -0
  88. data/lib/clacky/web/features/workspace/store.js +99 -0
  89. data/lib/clacky/web/features/workspace/view.js +305 -0
  90. data/lib/clacky/web/i18n.js +68 -6
  91. data/lib/clacky/web/index.html +113 -62
  92. data/lib/clacky/web/sessions.js +493 -39
  93. data/lib/clacky/web/settings.js +143 -49
  94. data/lib/clacky/web/skills.js +3 -863
  95. data/lib/clacky/web/vendor/codemirror/codemirror.min.js +29 -0
  96. data/lib/clacky/web/ws-dispatcher.js +7 -3
  97. data/lib/clacky.rb +17 -1
  98. metadata +81 -20
  99. data/lib/clacky/web/backup.js +0 -119
  100. data/lib/clacky/web/model-tester.js +0 -66
  101. data/lib/clacky/web/tasks.js +0 -373
  102. data/lib/clacky/web/version.js +0 -449
  103. data/lib/clacky/web/workspace.js +0 -316
  104. /data/lib/clacky/web/{notify.mp3 → assets/notify.mp3} +0 -0
  105. /data/lib/clacky/web/{datepicker.js → components/datepicker.js} +0 -0
  106. /data/lib/clacky/web/{sidebar.js → components/sidebar.js} +0 -0
  107. /data/lib/clacky/web/{marked.min.js → vendor/marked/marked.min.js} +0 -0
@@ -0,0 +1,145 @@
1
+ // ── Share · store — brand source, scorecard state, frequency cap, telemetry
2
+ //
3
+ // Owns the share feature's data: the brand identity (hydrated from
4
+ // /api/brand/status), the live scorecard stats + selected period, the
5
+ // success-count / cooldown frequency cap (localStorage), and the telemetry
6
+ // beacon. It never renders.
7
+ //
8
+ // White-label safety lives here: a branded build NEVER falls back to
9
+ // openclacky.com — the homepage/QR url is simply null when unset.
10
+ //
11
+ // `Share` stays the single public facade.
12
+ //
13
+ // Depends on: Clacky.ext.
14
+ // ───────────────────────────────────────────────────────────────────────────
15
+
16
+ const ShareStore = (() => {
17
+ const DEFAULT_NAME = "OpenClacky";
18
+ const DEFAULT_HOMEPAGE = "https://www.openclacky.com/";
19
+
20
+ const COUNT_KEY = "clacky-share-success-count";
21
+ const COOLDOWN_KEY = "clacky-share-cooldown-until";
22
+ const COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000;
23
+ const PROMPT_AT = [1, 5];
24
+
25
+ let _brand = { name: DEFAULT_NAME, homepageUrl: DEFAULT_HOMEPAGE, logoUrl: null };
26
+ let _scorecard = null;
27
+ let _scorePeriod = null;
28
+
29
+ const _listeners = {};
30
+
31
+ function _on(event, handler) {
32
+ (_listeners[event] ||= []).push(handler);
33
+ return () => {
34
+ const list = _listeners[event];
35
+ const i = list ? list.indexOf(handler) : -1;
36
+ if (i >= 0) list.splice(i, 1);
37
+ };
38
+ }
39
+
40
+ function _emit(event, payload) {
41
+ (_listeners[event] || []).forEach((h) => h(payload));
42
+ if (window.Clacky && Clacky.ext) Clacky.ext.emit(event, payload);
43
+ }
44
+
45
+ const state = {
46
+ get brand() { return _brand; },
47
+ get scorecard() { return _scorecard; },
48
+ get scorePeriod() { return _scorePeriod; },
49
+ set scorePeriod(p) { _scorePeriod = p; },
50
+ shareUrl() { return _brand.homepageUrl; },
51
+
52
+ /** Stats for the selected period, falling back to flat fields. */
53
+ curStats() {
54
+ if (_scorecard && _scorecard.periods && _scorePeriod && _scorecard.periods[_scorePeriod]) {
55
+ return _scorecard.periods[_scorePeriod];
56
+ }
57
+ return _scorecard;
58
+ },
59
+ };
60
+
61
+ async function _hydrateBrand() {
62
+ try {
63
+ const res = await fetch("/api/brand/status");
64
+ if (!res.ok) return;
65
+ const data = await res.json();
66
+ if (data && data.branded) {
67
+ _brand = {
68
+ name: (data.product_name || "").trim() || DEFAULT_NAME,
69
+ homepageUrl: (data.homepage_url || "").trim() || null,
70
+ logoUrl: (data.logo_url || "").trim() || null
71
+ };
72
+ _emit("share:brandChanged", _brand);
73
+ }
74
+ } catch (_e) { /* keep defaults */ }
75
+ }
76
+
77
+ function _successCount() {
78
+ return parseInt(localStorage.getItem(COUNT_KEY) || "0", 10) || 0;
79
+ }
80
+ function _bumpSuccessCount() {
81
+ const n = _successCount() + 1;
82
+ localStorage.setItem(COUNT_KEY, String(n));
83
+ return n;
84
+ }
85
+ function _inCooldown() {
86
+ const until = parseInt(localStorage.getItem(COOLDOWN_KEY) || "0", 10) || 0;
87
+ return Date.now() < until;
88
+ }
89
+ function _startCooldown() {
90
+ localStorage.setItem(COOLDOWN_KEY, String(Date.now() + COOLDOWN_MS));
91
+ }
92
+
93
+ const Share = {
94
+ on: _on,
95
+ state,
96
+ PROMPT_AT,
97
+
98
+ hydrateBrand: _hydrateBrand,
99
+
100
+ /** Enter scorecard mode with live stats; picks the default period. */
101
+ setScorecard(stats) {
102
+ _scorecard = stats || null;
103
+ _scorePeriod = _scorecard
104
+ ? (_scorecard.defaultPeriod && _scorecard.periods && _scorecard.periods[_scorecard.defaultPeriod]
105
+ ? _scorecard.defaultPeriod
106
+ : (_scorecard.periods ? Object.keys(_scorecard.periods)[0] : null))
107
+ : null;
108
+ _emit("share:scorecardChanged", { scorecard: _scorecard, period: _scorePeriod });
109
+ },
110
+
111
+ /** Merge a late-arriving period's stats and notify the view. */
112
+ addScorecardPeriod(key, stats) {
113
+ if (!_scorecard) return false;
114
+ if (!_scorecard.periods) _scorecard.periods = {};
115
+ _scorecard.periods[key] = stats;
116
+ _emit("share:scorecardPeriodAdded", { key, stats });
117
+ return true;
118
+ },
119
+
120
+ clearScorecard() {
121
+ _scorecard = null;
122
+ _scorePeriod = null;
123
+ _emit("share:scorecardChanged", { scorecard: null, period: null });
124
+ },
125
+
126
+ /** Bump success count and decide whether to auto-prompt; starts cooldown. */
127
+ consumeSuccess() {
128
+ const n = _bumpSuccessCount();
129
+ if (_inCooldown()) return { prompt: false };
130
+ if (!PROMPT_AT.includes(n)) return { prompt: false };
131
+ _startCooldown();
132
+ return { prompt: true, count: n };
133
+ },
134
+
135
+ telemetry(event, extra) {
136
+ if (typeof fetch === "undefined") return;
137
+ const body = JSON.stringify({ event, extra: extra || {} });
138
+ fetch("/api/telemetry", { method: "POST", headers: { "Content-Type": "application/json" }, body }).catch(() => {});
139
+ },
140
+ };
141
+
142
+ return Share;
143
+ })();
144
+
145
+ const Share = ShareStore;