clauderipple 0.8.10 → 0.9.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 (68) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.es.md +0 -4
  3. package/README.ja.md +0 -4
  4. package/README.ko.md +5 -1
  5. package/README.md +5 -1
  6. package/README.pt-BR.md +0 -4
  7. package/README.zh-CN.md +0 -4
  8. package/dist/app/package.json +1 -1
  9. package/dist/cli/src/codex.js +3 -1
  10. package/dist/cli/src/settings.js +5 -2
  11. package/dist/mod/.claude-plugin/plugin.json +9 -1
  12. package/dist/mod/hooks/cache.ts +60 -0
  13. package/dist/mod/hooks/compact.ts +40 -0
  14. package/dist/mod/hooks/handoff.ts +43 -0
  15. package/dist/mod/hooks/i18n.ts +2716 -0
  16. package/dist/mod/hooks/judge.ts +226 -0
  17. package/dist/mod/hooks/ladder.js +179 -0
  18. package/dist/mod/hooks/ladder.ts +174 -0
  19. package/dist/mod/hooks/modes.ts +93 -0
  20. package/dist/mod/hooks/register.tsx +1458 -256
  21. package/dist/mod/hooks/session.ts +62 -0
  22. package/dist/mod/hooks/settings.js +342 -0
  23. package/dist/mod/hooks/settings.ts +321 -0
  24. package/dist/mod/hooks/stats.ts +59 -0
  25. package/dist/mod/hooks/ui/agents.tsx +138 -0
  26. package/dist/mod/hooks/ui/alerts.tsx +370 -0
  27. package/dist/mod/hooks/ui/art.ts +628 -0
  28. package/dist/mod/hooks/ui/band.tsx +836 -0
  29. package/dist/mod/hooks/ui/kit.tsx +258 -0
  30. package/dist/mod/hooks/ui/log.tsx +109 -0
  31. package/dist/mod/hooks/ui/parts.tsx +217 -0
  32. package/dist/mod/hooks/ui/ripple.ts +614 -0
  33. package/dist/mod/hooks/ui/settings.tsx +376 -0
  34. package/dist/mod/hooks/ui/setup.tsx +148 -0
  35. package/dist/mod/hooks/ui/theme.ts +217 -0
  36. package/dist/mod/hooks/ui/view.ts +371 -0
  37. package/dist/mod/hooks/worker.ts +1 -1
  38. package/dist/mod/hooks/workers.ts +217 -0
  39. package/dist/mod/tests/register.test.tsx +1730 -0
  40. package/dist/mod/tests/ui.test.tsx +853 -0
  41. package/dist/mod/tsconfig.json +2 -1
  42. package/dist/mod/types/index.d.ts +320 -1
  43. package/dist/router/src/admin.js +170 -52
  44. package/dist/router/src/agents.js +24 -5
  45. package/dist/router/src/bootstrap.js +4 -2
  46. package/dist/router/src/config.js +3 -0
  47. package/dist/router/src/health.js +33 -0
  48. package/dist/router/src/index.js +1 -0
  49. package/dist/router/src/ingress/models.js +4 -1
  50. package/dist/router/src/mod-settings.js +119 -0
  51. package/dist/router/src/model-policy.js +6 -0
  52. package/dist/router/src/model-scores.js +159 -0
  53. package/dist/router/src/picker.js +7 -0
  54. package/dist/router/src/providers/chatgpt/index.js +6 -1
  55. package/dist/router/src/providers/chatgpt/translate.js +1 -1
  56. package/dist/router/src/providers/google/index.js +6 -2
  57. package/dist/router/src/providers/openai/index.js +4 -1
  58. package/dist/router/src/providers/openai/translate.js +1 -1
  59. package/dist/router/src/proxy.js +14 -3
  60. package/dist/router/src/version.js +1 -1
  61. package/dist/ui/app.js +908 -209
  62. package/dist/ui/favicon-32.png +0 -0
  63. package/dist/ui/favicon.svg +12 -0
  64. package/dist/ui/i18n.js +919 -233
  65. package/dist/ui/index.html +159 -87
  66. package/dist/ui/style.css +789 -410
  67. package/docs/ARCHITECTURE.md +128 -0
  68. package/package.json +7 -3
@@ -1,126 +1,198 @@
1
1
  <title>ClaudeRipple</title>
2
2
  <meta charset="utf-8" />
3
3
  <meta name="viewport" content="width=device-width, initial-scale=1" />
4
+ <meta name="color-scheme" content="light dark" />
4
5
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; connect-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'; base-uri 'none'; form-action 'none'" />
6
+ <link rel="icon" href="favicon-32.png" sizes="32x32" type="image/png" />
7
+ <link rel="icon" href="favicon.svg" type="image/svg+xml" />
5
8
  <link rel="stylesheet" href="style.css" />
6
9
 
7
- <nav>
8
- <div class="brand"><svg viewBox="0 0 1024 1024" aria-hidden="true"><circle cx="512" cy="512" r="128" fill="#6a56d6"/><circle cx="512" cy="512" r="245" stroke="#8a78e6" stroke-width="56" fill="none" opacity="0.7"/><path d="M512 108C735.124 108 916 288.876 916 512C916 735.124 735.124 916 512 916C288.876 916 108 735.124 108 512" stroke="#8a78e6" stroke-width="56" stroke-linecap="round" fill="none" opacity="0.35"/></svg><span data-i18n="brand">ClaudeRipple</span></div>
9
- <button data-view="health" class="nav-btn" data-i18n="nav.health">Status</button>
10
- <button data-view="slots" class="nav-btn" data-i18n="nav.slots">Model links</button>
11
- <button data-view="providers" class="nav-btn" data-i18n="nav.providers">Providers</button>
12
- <button data-view="clients" class="nav-btn" data-i18n="nav.clients">Clients</button>
13
- <button data-view="logs" class="nav-btn" data-i18n="nav.logs">Logs</button>
14
- <button data-view="about" class="nav-btn" data-i18n="nav.about">About</button>
15
- <div class="nav-spacer"></div>
16
- <select class="lang-toggle" id="lang-toggle" data-i18n-title="lang.label"></select>
10
+ <div class="app">
11
+ <nav class="sidebar">
12
+ <div class="nav-list">
13
+ <button data-view="health" class="nav-btn" type="button"><svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 10.5h3.4l2.2-5.6 3.8 10.4 2.3-4.8H18"/></svg><span data-i18n="nav.health">Status</span></button>
14
+ <button data-view="slots" class="nav-btn" type="button"><svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7h13M13 4l3 3-3 3M17 13H4M7 10l-3 3 3 3"/></svg><span data-i18n="nav.slots">Model mapping</span></button>
15
+ <button data-view="providers" class="nav-btn" type="button"><svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.2 15.5h8.1a3.3 3.3 0 0 0 .4-6.6 4.8 4.8 0 0 0-9.2-.8 3.7 3.7 0 0 0 .7 7.4z"/></svg><span data-i18n="nav.providers">Providers</span></button>
16
+ <button data-view="clients" class="nav-btn" type="button"><svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3.5" y="4" width="13" height="9.5" rx="1.6"/><path d="M1.8 16.4h16.4"/></svg><span data-i18n="nav.clients">Clients</span></button>
17
+ <button data-view="mod" class="nav-btn" type="button"><svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 6.5h8M14.5 6.5h3M2.5 13.5h3M9.5 13.5h8"/><circle cx="12.5" cy="6.5" r="2"/><circle cx="7.5" cy="13.5" r="2"/></svg><span class="nav-long" data-i18n="nav.mod">ClaudeRipple mod</span><span class="nav-short" data-i18n="nav.modShort">Mod</span></button>
18
+ <button data-view="logs" class="nav-btn" type="button"><svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7.5 5.5H17M7.5 10H17M7.5 14.5H17"/><path d="M3.5 5.5h.01M3.5 10h.01M3.5 14.5h.01" stroke-width="2.4"/></svg><span data-i18n="nav.logs">Logs</span></button>
19
+ <button data-view="about" class="nav-btn" type="button"><svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="10" cy="10" r="7.6"/><path d="M10 9.2v4.6"/><path d="M10 6.3h.01" stroke-width="2.2"/></svg><span data-i18n="nav.about">About</span></button>
20
+ </div>
21
+ <div class="sidebar-footer"><select class="lang-select" id="lang-toggle" data-i18n-title="lang.label"></select></div>
17
22
  </nav>
18
23
 
19
24
  <main>
20
25
  <section id="view-health" class="view">
21
- <h1 data-i18n="health.title">Status</h1>
22
- <p class="subtitle" data-i18n="health.subtitle">Check that Claude Desktop and your providers are ready.</p>
23
- <div class="card"><h2 data-i18n="health.desktop">Claude Desktop connection</h2><div class="rows" id="health-desktop"></div></div>
24
- <div class="card"><h2 data-i18n="health.providers">Providers</h2><div class="rows" id="health-providers"></div></div>
25
- <div class="card"><h2 data-i18n="health.requestCount">Request count</h2><div class="rows" id="health-requests"></div></div>
26
- <details class="details"><summary data-i18n="health.details">Details</summary><div class="rows detail-rows" id="health-details"></div></details>
26
+ <header class="page-header"><div><h1 data-i18n="health.title">Status</h1><p class="page-subtitle" data-i18n="health.subtitle"></p></div></header>
27
+ <section class="section">
28
+ <div class="section-head"><h2 class="section-title" data-i18n="health.desktop">Claude Desktop connection</h2></div>
29
+ <div class="group" id="health-desktop"></div>
30
+ </section>
31
+ <section class="section">
32
+ <div class="section-head"><h2 class="section-title" data-i18n="health.providers">Providers</h2></div>
33
+ <div class="group" id="health-providers"></div>
34
+ </section>
35
+ <section class="section">
36
+ <div class="section-head"><h2 class="section-title" data-i18n="health.requestCount">Request count</h2></div>
37
+ <div class="group" id="health-requests"></div>
38
+ </section>
27
39
  </section>
28
40
 
29
41
  <section id="view-slots" class="view">
30
- <h1 data-i18n="slots.title">Model links</h1>
31
- <p class="subtitle" data-i18n="slots.subtitle">Choose what each Claude model should actually use.</p>
32
- <div class="toolbar"><button class="btn secondary" id="slots-add" type="button" data-i18n="slots.add">+ Add link</button><span class="small" id="slots-status"></span></div>
33
- <p class="small" id="slots-summary"></p>
34
- <p class="hint" data-i18n="slots.effortHint">If the effort chosen in the app is unsupported by the model, ClaudeRipple sends the nearest level instead. (For example: xhigh → high)</p>
35
- <div class="card table-card"><table id="slots-table"><thead><tr><th data-i18n="slots.th.claude">When you choose this</th><th data-i18n="slots.th.target">It answers with</th><th data-i18n="slots.th.effort">Thinking depth</th><th></th></tr></thead><tbody></tbody></table></div>
42
+ <header class="page-header">
43
+ <div><h1 data-i18n="slots.title">Model mapping</h1><p class="page-subtitle" data-i18n="slots.subtitle"></p></div>
44
+ <div class="page-actions"><span class="slots-status" id="slots-status" role="status"></span><button class="btn" id="slots-add" type="button" data-i18n="slots.add">+ Add mapping</button></div>
45
+ </header>
46
+ <section class="section">
47
+ <div class="map-head" aria-hidden="true"><span data-i18n="slots.th.claude"></span><span></span><span data-i18n="slots.th.target"></span><span data-i18n="slots.th.effort"></span><span></span></div>
48
+ <div class="group map-list" id="slots-list"></div>
49
+ <p class="group-footer" id="slots-summary"></p>
50
+ <p class="group-footer" data-i18n="slots.effortHint"></p>
51
+ </section>
36
52
  </section>
37
53
 
38
54
  <section id="view-providers" class="view providers-view">
39
- <div class="page-heading">
40
- <div><h1 data-i18n="providers.title">Providers</h1><p class="subtitle" data-i18n="providers.subtitle">Connect an AI account, then choose the models you will use.</p></div>
41
- <button class="btn" id="providers-add" type="button" data-i18n="providers.add">+ Add provider</button>
42
- </div>
55
+ <header class="page-header">
56
+ <div><h1 data-i18n="providers.title">Providers</h1><p class="page-subtitle" data-i18n="providers.subtitle"></p></div>
57
+ <div class="page-actions"><button class="btn" id="providers-add" type="button" data-i18n="providers.add">+ Add provider</button></div>
58
+ </header>
43
59
  <div class="provider-workspace">
44
- <aside class="provider-rail" aria-label="Providers">
45
- <div class="provider-rail-tools"><input id="providers-search" type="search" data-i18n-placeholder="providers.search" placeholder="Search providers…" /><button class="icon-btn" id="providers-refresh" type="button" data-i18n-title="providers.refresh" title="Check connections" aria-label="Check connections">↻</button></div>
46
- <div id="providers-list" class="provider-list" role="listbox"></div>
60
+ <aside class="provider-rail">
61
+ <div class="provider-rail-tools">
62
+ <input id="providers-search" type="search" data-i18n-placeholder="providers.search" placeholder="Search providers…" />
63
+ <button class="icon-btn" id="providers-refresh" type="button" data-i18n-title="providers.refresh" title="Test all"><svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16.2 10a6.2 6.2 0 1 1-1.9-4.5"/><path d="M16.4 3.4v3.8h-3.8"/></svg></button>
64
+ </div>
65
+ <div id="providers-list" class="provider-list group" role="listbox"></div>
47
66
  </aside>
48
67
  <div id="provider-detail" class="provider-detail"></div>
49
68
  </div>
50
69
  </section>
51
70
 
52
71
  <section id="view-clients" class="view">
53
- <h1 data-i18n="clients.title">Clients</h1>
54
- <p class="subtitle" data-i18n="clients.subtitle">Set up the apps that use ClaudeRipple.</p>
55
- <div class="card">
56
- <h2 data-i18n="clients.desktop.title">Claude Desktop</h2>
57
- <p class="hint" data-i18n="clients.desktop.help">Choose whether provider models appear by name in the model picker.</p>
58
- <div class="rows" id="client-picker-rows"></div>
59
- <div class="actions"><button class="btn" id="client-picker-toggle" type="button"></button><span class="small" id="client-picker-msg"></span></div>
60
- <div class="client-models" id="client-picker-models-card" hidden><h3 data-i18n="slots.pickerModels">Models in picker</h3><p class="hint" data-i18n="slots.pickerModelsHelp">Tick only models you want to see in Claude Desktop.</p><div class="model-checklist" id="client-picker-models"></div></div>
61
- <div class="client-divider"></div>
62
- <h3 data-i18n="slots.modelSlots">Claude Code's own model slots</h3>
63
- <p class="hint" data-i18n="slots.modelSlotsHelp"></p>
64
- <div class="slot-rows" id="client-model-slots"></div>
65
- <div class="client-divider"></div>
66
- <h3 data-i18n="agentTitle.title">Subagent name tags</h3>
67
- <p class="hint" data-i18n="agentTitle.help"></p>
68
- <div class="rows" id="client-agent-title-rows"></div>
69
- <div class="actions"><button class="btn" id="client-agent-title-toggle" type="button"></button></div>
70
- <div class="client-divider"></div>
71
- <h3 data-i18n="statusMod.title">Route line and request log</h3>
72
- <p class="hint" data-i18n="statusMod.help"></p>
73
- <div class="rows" id="client-status-mod-rows"></div>
74
- <div class="actions"><button class="btn" id="client-status-mod-toggle" type="button"></button></div>
75
- <div class="client-divider"></div>
76
- <h3 data-i18n="workerTools.title">Worker tools</h3>
77
- <p class="hint" data-i18n="workerTools.help"></p>
78
- <div class="rows" id="client-worker-tools-rows"></div>
79
- <div class="actions"><button class="btn" id="client-worker-tools-toggle" type="button"></button></div>
80
- </div>
81
- <div class="card">
82
- <h2 data-i18n="clients.codex.title">Codex CLI</h2>
83
- <p class="hint" data-i18n="clients.codex.help">Adds ClaudeRipple as a provider to Codex.</p>
84
- <div class="rows" id="client-codex-rows"></div>
85
- <div class="actions"><button class="btn" id="client-codex-toggle" type="button"></button><span class="small" id="client-codex-msg"></span></div>
86
- <p class="hint" data-i18n="clients.codex.note">ChatGPT subscription models are used directly by Codex, so only Claude and Anthropic-compatible providers appear here.</p>
87
- </div>
88
- <div class="card">
89
- <h2 data-i18n="clients.claudeCode.title">Terminal Claude Code</h2>
90
- <p class="hint" data-i18n="clients.claudeCode.help">Terminal Claude uses the same router. Choose with /model &lt;name&gt;.</p>
91
- <div class="rows" id="client-claude-code-rows"></div>
92
- </div>
72
+ <header class="page-header"><div><h1 data-i18n="clients.title">Clients</h1><p class="page-subtitle" data-i18n="clients.subtitle"></p></div></header>
73
+
74
+ <section class="section">
75
+ <div class="section-head"><h2 class="section-title" data-i18n="clients.desktop.title">Claude Desktop</h2><span class="small" id="client-picker-msg" role="status"></span></div>
76
+ <div class="group" id="client-picker-rows">
77
+ <div class="cell">
78
+ <div class="cell-text"><label class="cell-label" for="client-picker-toggle" data-i18n="picker.title">Model picker</label><span class="cell-sub" data-i18n="clients.desktop.help"></span></div>
79
+ <span class="switch"><input type="checkbox" role="switch" id="client-picker-toggle" /><span></span></span>
80
+ </div>
81
+ </div>
82
+ <p class="group-footer" data-i18n="picker.restartHelp"></p>
83
+ </section>
84
+ <section class="section" id="client-picker-models-card" hidden>
85
+ <div class="section-head"><h2 class="section-title" data-i18n="slots.pickerModels">Models in picker</h2></div>
86
+ <div class="group"><div class="model-checklist" id="client-picker-models"></div></div>
87
+ <p class="group-footer" data-i18n="slots.pickerModelsHelp"></p>
88
+ </section>
89
+
90
+ <section class="section">
91
+ <div class="section-head"><h2 class="section-title" data-i18n="slots.modelSlots">Claude Code's own model slots</h2></div>
92
+ <div class="group" id="client-model-slots"></div>
93
+ <p class="group-footer" data-i18n="slots.modelSlotsHelp"></p>
94
+ </section>
95
+
96
+ <section class="section">
97
+ <div class="section-head"><h2 class="section-title" data-i18n="clients.claudeCode.extras">Claude Code add-ons</h2></div>
98
+ <div class="group">
99
+ <div class="cell">
100
+ <div class="cell-text"><label class="cell-label" for="client-status-mod-toggle" data-i18n="statusMod.title">Route line and request log</label></div>
101
+ <span class="switch"><input type="checkbox" role="switch" id="client-status-mod-toggle" /><span></span></span>
102
+ </div>
103
+ <a class="cell link" href="#mod">
104
+ <div class="cell-text"><span class="cell-label" data-i18n="mod.openSettings">ClaudeRipple mod settings</span></div>
105
+ <svg class="chevron" viewBox="0 0 8 13" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1.5 1.5l5 5-5 5"/></svg>
106
+ </a>
107
+ </div>
108
+ <p class="group-footer" data-i18n="statusMod.help"></p>
109
+ <div class="group stacked-group">
110
+ <div class="cell">
111
+ <div class="cell-text"><label class="cell-label" for="client-worker-tools-toggle" data-i18n="workerTools.switch">Limit worker tools</label></div>
112
+ <span class="switch"><input type="checkbox" role="switch" id="client-worker-tools-toggle" /><span></span></span>
113
+ </div>
114
+ </div>
115
+ <p class="group-footer" data-i18n="workerTools.help"></p>
116
+ </section>
117
+
118
+ <section class="section">
119
+ <div class="section-head"><h2 class="section-title" data-i18n="clients.codex.title">Codex</h2><span class="small" id="client-codex-msg" role="status"></span></div>
120
+ <div class="group">
121
+ <div class="cell">
122
+ <div class="cell-text"><label class="cell-label" for="client-codex-toggle" data-i18n="clients.codex.turnOn">Use ClaudeRipple in Codex</label><span class="cell-sub mono" id="client-codex-rows"></span></div>
123
+ <span class="switch"><input type="checkbox" role="switch" id="client-codex-toggle" /><span></span></span>
124
+ </div>
125
+ </div>
126
+ <p class="group-footer" data-i18n="clients.codex.help"></p>
127
+ <p class="group-footer" data-i18n="clients.codex.note"></p>
128
+ </section>
129
+
130
+ <section class="section">
131
+ <div class="section-head"><h2 class="section-title" data-i18n="clients.claudeCode.title">Terminal Claude Code</h2></div>
132
+ <div class="group" id="client-claude-code-rows"></div>
133
+ <p class="group-footer" data-i18n="clients.claudeCode.help"></p>
134
+ </section>
135
+ </section>
136
+
137
+ <section id="view-mod" class="view">
138
+ <header class="page-header">
139
+ <div><h1 data-i18n="nav.mod">ClaudeRipple mod</h1><p class="page-subtitle" data-i18n="mod.subtitle"></p></div>
140
+ <div class="page-actions"><span class="slots-status" id="mod-status" role="status"></span></div>
141
+ </header>
142
+ <section class="section" id="mod-notice" hidden></section>
143
+ <div id="mod-content"></div>
93
144
  </section>
94
145
 
95
146
  <section id="view-logs" class="view">
96
- <h1 data-i18n="logs.title">Logs</h1>
97
- <div class="logs-tabs" role="tablist">
98
- <button class="logs-tab active" id="logs-requests-tab" type="button" role="tab" aria-selected="true" data-i18n="logs.requests">Requests</button>
99
- <button class="logs-tab" id="logs-raw-tab" type="button" role="tab" aria-selected="false" data-i18n="logs.raw">Raw logs</button>
100
- </div>
101
- <div id="logs-requests-panel" role="tabpanel">
102
- <div class="logs-summary-title"><strong data-i18n="logs.lastHour">Last hour</strong><span class="small" data-i18n="logs.polling">Updates every 3s</span></div>
103
- <div class="logs-summary" id="logs-summary"></div>
104
- <div class="toolbar logs-controls">
105
- <label class="logs-filter"><span data-i18n="logs.provider">Provider</span><select id="logs-provider"><option value="" data-i18n="logs.all">All</option></select></label>
147
+ <header class="page-header">
148
+ <div><h1 data-i18n="logs.title">Logs</h1></div>
149
+ <div class="logs-tabs" role="tablist">
150
+ <button class="logs-tab active" id="logs-requests-tab" type="button" role="tab" aria-selected="true" data-i18n="logs.requests">Requests</button>
151
+ <button class="logs-tab" id="logs-raw-tab" type="button" role="tab" aria-selected="false" data-i18n="logs.raw">Raw logs</button>
106
152
  </div>
107
- <div class="card table-card logs-table-card"><table id="requests-table"><thead><tr><th data-i18n="logs.th.time">Time</th><th data-i18n="logs.th.models">Called → answered</th><th data-i18n="logs.th.effort">effort</th><th data-i18n="logs.th.input">Input</th><th data-i18n="logs.th.output">Output</th><th data-i18n="logs.th.duration">Duration</th><th data-i18n="logs.th.status">Status</th></tr></thead><tbody></tbody></table></div>
108
- <div class="empty-card" id="requests-empty" hidden data-i18n="logs.empty">No requests yet.</div>
153
+ </header>
154
+ <div id="logs-requests-panel" role="tabpanel">
155
+ <section class="section">
156
+ <div class="logs-header-row"><div><h2 class="section-title" data-i18n="logs.lastHour">Last hour</h2><span class="small" data-i18n="logs.polling">Updates every 3s</span></div></div>
157
+ <div class="group logs-summary" id="logs-summary"></div>
158
+ </section>
159
+ <section class="section">
160
+ <div class="logs-header-row">
161
+ <h2 class="section-title" data-i18n="logs.requests">Requests</h2>
162
+ <label class="logs-filter"><span data-i18n="logs.provider">Provider</span><select id="logs-provider"><option value="" data-i18n="logs.all">All</option></select></label>
163
+ </div>
164
+ <div class="group logs-table-card"><table id="requests-table"><thead><tr><th data-i18n="logs.th.time">Time</th><th data-i18n="logs.th.models">Model</th><th data-i18n="logs.th.effort">effort</th><th data-i18n="logs.th.input">Input</th><th data-i18n="logs.th.output">Output</th><th data-i18n="logs.th.duration">Duration</th><th data-i18n="logs.th.status">Status</th></tr></thead><tbody></tbody></table></div>
165
+ <div class="group empty-card" id="requests-empty" hidden data-i18n="logs.empty">No requests yet.</div>
166
+ </section>
109
167
  </div>
110
168
  <div id="logs-raw-panel" role="tabpanel" hidden>
111
- <div class="toolbar"><label class="check"><input type="checkbox" id="logs-autoscroll" checked /><span data-i18n="logs.autoscroll">Auto-scroll</span></label><span class="small" data-i18n="logs.rawPolling">Updates every 3s · last 200 lines</span></div>
112
- <pre id="logbox"></pre>
169
+ <div class="raw-toolbar"><span class="small" data-i18n="logs.rawPolling">Updates every 3s · last 200 lines</span><label class="check"><input type="checkbox" id="logs-autoscroll" checked /><span data-i18n="logs.autoscroll">Auto-scroll</span></label></div>
170
+ <div class="group"><pre id="logbox"></pre></div>
113
171
  </div>
114
172
  </section>
115
173
 
116
174
  <section id="view-about" class="view">
117
- <h1 data-i18n="about.title">About</h1>
118
- <div class="card"><p data-i18n="about.body">ClaudeRipple is an independent open-source project, not affiliated with Anthropic or OpenAI. Claude and Claude Code are trademarks of Anthropic, PBC.</p><p class="small" data-i18n="about.license">License: GPL-3.0</p></div>
175
+ <header class="page-header"><div><h1 data-i18n="about.title">About</h1></div></header>
176
+ <section class="section">
177
+ <div class="group" id="health-details"></div>
178
+ </section>
179
+ <section class="section narrow-only">
180
+ <div class="group">
181
+ <div class="cell">
182
+ <div class="cell-text"><span class="cell-label" data-i18n="lang.label">Language</span></div>
183
+ <div class="cell-value"><select class="lang-select" id="lang-about" data-i18n-title="lang.label"></select></div>
184
+ </div>
185
+ </div>
186
+ </section>
187
+ <section class="section">
188
+ <div class="group about-text"><p data-i18n="about.body">ClaudeRipple is an independent open-source project, not affiliated with Anthropic or OpenAI. Claude and Claude Code are trademarks of Anthropic, PBC.</p><p class="small" data-i18n="about.license">License: GPL-3.0</p></div>
189
+ </section>
119
190
  </section>
120
191
  </main>
192
+ </div>
121
193
 
122
- <div class="toast" id="toast" role="status"></div>
123
- <div class="modal-backdrop" id="modal-backdrop" hidden><section class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title"><button class="icon-btn modal-close" id="modal-close" type="button" aria-label="Close">×</button><div id="modal-content"></div></section></div>
194
+ <div class="toast" id="toast" role="status" aria-live="polite"></div>
195
+ <div class="modal-backdrop" id="modal-backdrop" hidden><section class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title"><button class="icon-btn modal-close" id="modal-close" type="button" data-i18n-title="common.close" aria-label="Close"><svg viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M1.5 1.5l7 7M8.5 1.5l-7 7"/></svg></button><div id="modal-content"></div></section></div>
124
196
 
125
197
  <script src="i18n.js"></script>
126
198
  <script src="presets-fallback.js"></script>