claudeck 1.0.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.
- package/LICENSE +21 -0
- package/README.md +233 -0
- package/cli.js +2 -0
- package/config/agent-chains.json +16 -0
- package/config/agent-dags.json +16 -0
- package/config/agents.json +46 -0
- package/config/bot-prompt.json +3 -0
- package/config/folders.json +66 -0
- package/config/prompts.json +92 -0
- package/config/repos.json +86 -0
- package/config/telegram-config.json +17 -0
- package/config/workflows.json +90 -0
- package/db.js +1198 -0
- package/package.json +55 -0
- package/plugins/claude-editor/client.css +171 -0
- package/plugins/claude-editor/client.js +183 -0
- package/plugins/event-stream/client.css +207 -0
- package/plugins/event-stream/client.js +271 -0
- package/plugins/linear/client.css +345 -0
- package/plugins/linear/client.js +380 -0
- package/plugins/linear/config.json +5 -0
- package/plugins/linear/server.js +312 -0
- package/plugins/repos/client.css +549 -0
- package/plugins/repos/client.js +663 -0
- package/plugins/repos/server.js +232 -0
- package/plugins/sudoku/client.css +196 -0
- package/plugins/sudoku/client.js +329 -0
- package/plugins/tasks/client.css +414 -0
- package/plugins/tasks/client.js +394 -0
- package/plugins/tasks/server.js +116 -0
- package/plugins/tic-tac-toe/client.css +167 -0
- package/plugins/tic-tac-toe/client.js +241 -0
- package/public/css/core/components.css +232 -0
- package/public/css/core/layout.css +330 -0
- package/public/css/core/print.css +18 -0
- package/public/css/core/reset.css +36 -0
- package/public/css/core/responsive.css +378 -0
- package/public/css/core/theme.css +116 -0
- package/public/css/core/variables.css +93 -0
- package/public/css/features/agent-monitor.css +297 -0
- package/public/css/features/agent-sidebar.css +525 -0
- package/public/css/features/agents.css +996 -0
- package/public/css/features/analytics.css +181 -0
- package/public/css/features/background-sessions.css +321 -0
- package/public/css/features/cost-dashboard.css +168 -0
- package/public/css/features/home.css +313 -0
- package/public/css/features/retro-terminal.css +88 -0
- package/public/css/features/telegram.css +127 -0
- package/public/css/features/tour.css +148 -0
- package/public/css/features/voice-input.css +60 -0
- package/public/css/features/welcome.css +241 -0
- package/public/css/panels/assistant-bot.css +442 -0
- package/public/css/panels/dev-docs.css +292 -0
- package/public/css/panels/file-explorer.css +322 -0
- package/public/css/panels/git-panel.css +221 -0
- package/public/css/panels/mcp-manager.css +199 -0
- package/public/css/panels/tips-feed.css +353 -0
- package/public/css/ui/commands.css +273 -0
- package/public/css/ui/context-gauge.css +76 -0
- package/public/css/ui/file-picker.css +69 -0
- package/public/css/ui/image-attachments.css +106 -0
- package/public/css/ui/messages.css +884 -0
- package/public/css/ui/modals.css +122 -0
- package/public/css/ui/parallel.css +217 -0
- package/public/css/ui/permissions.css +110 -0
- package/public/css/ui/right-panel.css +481 -0
- package/public/css/ui/sessions.css +689 -0
- package/public/css/ui/status-bar.css +425 -0
- package/public/css/ui/toolbox.css +206 -0
- package/public/data/tips.json +218 -0
- package/public/icons/favicon.png +0 -0
- package/public/icons/icon-192.png +0 -0
- package/public/icons/icon-512.png +0 -0
- package/public/icons/whaly.png +0 -0
- package/public/index.html +1140 -0
- package/public/js/core/api.js +591 -0
- package/public/js/core/constants.js +3 -0
- package/public/js/core/dom.js +270 -0
- package/public/js/core/events.js +10 -0
- package/public/js/core/plugin-loader.js +153 -0
- package/public/js/core/store.js +39 -0
- package/public/js/core/utils.js +25 -0
- package/public/js/core/ws.js +64 -0
- package/public/js/features/agent-monitor.js +222 -0
- package/public/js/features/agents.js +1209 -0
- package/public/js/features/analytics.js +397 -0
- package/public/js/features/attachments.js +251 -0
- package/public/js/features/background-sessions.js +475 -0
- package/public/js/features/chat.js +589 -0
- package/public/js/features/cost-dashboard.js +152 -0
- package/public/js/features/dag-editor.js +399 -0
- package/public/js/features/easter-egg.js +46 -0
- package/public/js/features/home.js +270 -0
- package/public/js/features/projects.js +372 -0
- package/public/js/features/prompts.js +228 -0
- package/public/js/features/sessions.js +332 -0
- package/public/js/features/telegram.js +131 -0
- package/public/js/features/tour.js +210 -0
- package/public/js/features/voice-input.js +185 -0
- package/public/js/features/welcome.js +43 -0
- package/public/js/features/workflows.js +277 -0
- package/public/js/main.js +51 -0
- package/public/js/panels/assistant-bot.js +445 -0
- package/public/js/panels/dev-docs.js +380 -0
- package/public/js/panels/file-explorer.js +486 -0
- package/public/js/panels/git-panel.js +285 -0
- package/public/js/panels/mcp-manager.js +311 -0
- package/public/js/panels/tips-feed.js +303 -0
- package/public/js/ui/commands.js +114 -0
- package/public/js/ui/context-gauge.js +100 -0
- package/public/js/ui/diff.js +124 -0
- package/public/js/ui/disabled-tools.js +36 -0
- package/public/js/ui/export.js +74 -0
- package/public/js/ui/formatting.js +206 -0
- package/public/js/ui/header-dropdowns.js +72 -0
- package/public/js/ui/input-meta.js +71 -0
- package/public/js/ui/max-turns.js +21 -0
- package/public/js/ui/messages.js +387 -0
- package/public/js/ui/model-selector.js +20 -0
- package/public/js/ui/notifications.js +232 -0
- package/public/js/ui/parallel.js +176 -0
- package/public/js/ui/permissions.js +168 -0
- package/public/js/ui/right-panel.js +173 -0
- package/public/js/ui/shortcuts.js +143 -0
- package/public/js/ui/sidebar-toggle.js +29 -0
- package/public/js/ui/status-bar.js +172 -0
- package/public/js/ui/tab-sdk.js +623 -0
- package/public/js/ui/theme.js +38 -0
- package/public/manifest.json +13 -0
- package/public/offline.html +190 -0
- package/public/style.css +42 -0
- package/public/sw.js +91 -0
- package/server/agent-loop.js +385 -0
- package/server/dag-executor.js +265 -0
- package/server/orchestrator.js +514 -0
- package/server/paths.js +61 -0
- package/server/plugin-mount.js +56 -0
- package/server/push-sender.js +31 -0
- package/server/routes/agents.js +294 -0
- package/server/routes/bot.js +45 -0
- package/server/routes/exec.js +35 -0
- package/server/routes/files.js +218 -0
- package/server/routes/mcp.js +82 -0
- package/server/routes/messages.js +36 -0
- package/server/routes/notifications.js +37 -0
- package/server/routes/projects.js +207 -0
- package/server/routes/prompts.js +53 -0
- package/server/routes/sessions.js +103 -0
- package/server/routes/stats.js +143 -0
- package/server/routes/telegram.js +71 -0
- package/server/routes/tips.js +135 -0
- package/server/routes/workflows.js +81 -0
- package/server/summarizer.js +55 -0
- package/server/telegram-poller.js +205 -0
- package/server/telegram-sender.js +304 -0
- package/server/ws-handler.js +926 -0
- package/server.js +179 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// Guided tour — uses Driver.js to walk users through Claudeck features
|
|
2
|
+
const TOUR_KEY = 'claudeck-tour-completed';
|
|
3
|
+
|
|
4
|
+
function buildSteps() {
|
|
5
|
+
const hasSpeechApi = !document.body.classList.contains('no-speech-api');
|
|
6
|
+
const steps = [
|
|
7
|
+
// ── Sidebar & Navigation ──────────────────────────
|
|
8
|
+
{
|
|
9
|
+
element: '#home-btn',
|
|
10
|
+
popover: {
|
|
11
|
+
title: 'Home Dashboard',
|
|
12
|
+
description: 'Your activity hub — see AI usage heatmaps, session stats, streaks, and analytics at a glance.',
|
|
13
|
+
side: 'right',
|
|
14
|
+
align: 'center',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
element: '#project-select',
|
|
19
|
+
popover: {
|
|
20
|
+
title: 'Project Selector',
|
|
21
|
+
description: 'Switch between projects. Each project has its own sessions, system prompt, and MCP config.',
|
|
22
|
+
side: 'right',
|
|
23
|
+
align: 'start',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
element: '#session-search',
|
|
28
|
+
popover: {
|
|
29
|
+
title: 'Session Search',
|
|
30
|
+
description: 'Quickly find any session by name. Pro tip: use <kbd>Cmd+K</kbd> from anywhere.',
|
|
31
|
+
side: 'right',
|
|
32
|
+
align: 'start',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
element: '#new-session-btn',
|
|
37
|
+
popover: {
|
|
38
|
+
title: 'New Chat',
|
|
39
|
+
description: 'Start a fresh conversation with Claude. Shortcut: <kbd>Cmd+N</kbd>',
|
|
40
|
+
side: 'right',
|
|
41
|
+
align: 'center',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
element: '.mode-toggle .toggle-switch',
|
|
46
|
+
popover: {
|
|
47
|
+
title: 'Parallel Mode',
|
|
48
|
+
description: 'Split into a 2×2 grid and run 4 conversations simultaneously.',
|
|
49
|
+
side: 'right',
|
|
50
|
+
align: 'center',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
element: '#session-list',
|
|
55
|
+
popover: {
|
|
56
|
+
title: 'Session History',
|
|
57
|
+
description: 'All your chat sessions in one place. Click to switch, right-click to rename or delete.',
|
|
58
|
+
side: 'right',
|
|
59
|
+
align: 'start',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
element: '#theme-toggle-btn',
|
|
64
|
+
popover: {
|
|
65
|
+
title: 'Theme Toggle',
|
|
66
|
+
description: 'Switch between dark terminal mode and a warm light theme.',
|
|
67
|
+
side: 'right',
|
|
68
|
+
align: 'center',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
// ── Header Controls ───────────────────────────────
|
|
73
|
+
{
|
|
74
|
+
element: '.header-dropdown:first-of-type .header-dropdown-trigger',
|
|
75
|
+
popover: {
|
|
76
|
+
title: 'Session Settings',
|
|
77
|
+
description: 'Configure approval mode (Bypass, Confirm Writes, Plan), choose your AI model, and set max conversation turns.',
|
|
78
|
+
side: 'bottom',
|
|
79
|
+
align: 'end',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
element: '.header-dropdown:nth-of-type(2) .header-dropdown-trigger',
|
|
84
|
+
popover: {
|
|
85
|
+
title: 'Tools & Integrations',
|
|
86
|
+
description: 'Access MCP servers, browser notifications, Telegram alerts, and developer docs.',
|
|
87
|
+
side: 'bottom',
|
|
88
|
+
align: 'end',
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
element: '#tips-feed-toggle-btn',
|
|
93
|
+
popover: {
|
|
94
|
+
title: 'Tips Feed',
|
|
95
|
+
description: 'Contextual tips and learning resources to help you get more out of Claudeck. <kbd>Cmd+Shift+T</kbd>',
|
|
96
|
+
side: 'bottom',
|
|
97
|
+
align: 'center',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
element: '#right-panel-toggle-btn',
|
|
102
|
+
popover: {
|
|
103
|
+
title: 'Right Panel',
|
|
104
|
+
description: 'Toggle the file explorer, git panel, and plugin tabs. <kbd>Cmd+B</kbd>',
|
|
105
|
+
side: 'bottom',
|
|
106
|
+
align: 'center',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
// ── Chat Area ─────────────────────────────────────
|
|
111
|
+
{
|
|
112
|
+
element: '#agent-btn',
|
|
113
|
+
popover: {
|
|
114
|
+
title: 'Agents & Workflows',
|
|
115
|
+
description: 'Open the agent sidebar to run autonomous agents, sequential chains, DAG pipelines, or multi-step workflows.',
|
|
116
|
+
side: 'top',
|
|
117
|
+
align: 'center',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
element: '#attach-btn',
|
|
122
|
+
popover: {
|
|
123
|
+
title: 'Attach Files',
|
|
124
|
+
description: 'Browse your project files and attach them as context for Claude.',
|
|
125
|
+
side: 'top',
|
|
126
|
+
align: 'center',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
...(hasSpeechApi ? [{
|
|
130
|
+
element: '#mic-btn',
|
|
131
|
+
popover: {
|
|
132
|
+
title: 'Voice Input',
|
|
133
|
+
description: 'Speak your message — uses Web Speech API for real-time speech-to-text.',
|
|
134
|
+
side: 'top',
|
|
135
|
+
align: 'center',
|
|
136
|
+
},
|
|
137
|
+
}] : []),
|
|
138
|
+
{
|
|
139
|
+
element: '#toolbox-btn',
|
|
140
|
+
popover: {
|
|
141
|
+
title: 'Prompt Templates',
|
|
142
|
+
description: 'Access your saved prompts. Create reusable templates with {{variable}} placeholders.',
|
|
143
|
+
side: 'top',
|
|
144
|
+
align: 'center',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
element: '#message-input',
|
|
149
|
+
popover: {
|
|
150
|
+
title: 'Chat Input',
|
|
151
|
+
description: 'Type your message here. Start with <kbd>/</kbd> for slash commands like /clear, /export, /costs, and more. <kbd>Shift+Enter</kbd> for a new line.',
|
|
152
|
+
side: 'top',
|
|
153
|
+
align: 'center',
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
element: '#send-btn',
|
|
158
|
+
popover: {
|
|
159
|
+
title: 'Send Message',
|
|
160
|
+
description: 'Send your message to Claude, or press <kbd>Enter</kbd>. While streaming, this becomes a stop button.',
|
|
161
|
+
side: 'top',
|
|
162
|
+
align: 'center',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
// ── Status Bar ────────────────────────────────────
|
|
167
|
+
{
|
|
168
|
+
element: '.status-bar',
|
|
169
|
+
popover: {
|
|
170
|
+
title: 'Status Bar',
|
|
171
|
+
description: 'Real-time info at a glance — connection status, git branch, active project, streaming tokens, context usage, and costs.',
|
|
172
|
+
side: 'top',
|
|
173
|
+
align: 'center',
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
return steps;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Start the guided tour.
|
|
183
|
+
*/
|
|
184
|
+
export function startTour() {
|
|
185
|
+
if (typeof window.driver === 'undefined') {
|
|
186
|
+
console.warn('Driver.js not loaded');
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const driverObj = window.driver.js.driver({
|
|
191
|
+
showProgress: true,
|
|
192
|
+
animate: true,
|
|
193
|
+
overlayColor: 'rgba(0, 0, 0, 0.35)',
|
|
194
|
+
stagePadding: 6,
|
|
195
|
+
stageRadius: 8,
|
|
196
|
+
smoothScroll: true,
|
|
197
|
+
popoverClass: 'claudeck-tour',
|
|
198
|
+
allowClose: true,
|
|
199
|
+
doneBtnText: 'Finish',
|
|
200
|
+
nextBtnText: 'Next →',
|
|
201
|
+
prevBtnText: '← Back',
|
|
202
|
+
showButtons: ['next', 'previous', 'close'],
|
|
203
|
+
steps: buildSteps(),
|
|
204
|
+
onDestroyed: () => {
|
|
205
|
+
localStorage.setItem(TOUR_KEY, '1');
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
driverObj.drive();
|
|
210
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Voice Input — Speech-to-Text using Web Speech API
|
|
2
|
+
import { $ } from '../core/dom.js';
|
|
3
|
+
import { getState } from '../core/store.js';
|
|
4
|
+
import { addStatus } from '../ui/messages.js';
|
|
5
|
+
import { getPane } from '../ui/parallel.js';
|
|
6
|
+
|
|
7
|
+
const isEdge = /Edg\//.test(navigator.userAgent);
|
|
8
|
+
const SpeechRecognition = !isEdge && (window.SpeechRecognition || window.webkitSpeechRecognition);
|
|
9
|
+
|
|
10
|
+
if (!SpeechRecognition) {
|
|
11
|
+
document.body.classList.add('no-speech-api');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (SpeechRecognition) {
|
|
15
|
+
let recognition = null;
|
|
16
|
+
let isRecording = false;
|
|
17
|
+
let shouldRestart = false;
|
|
18
|
+
let interimText = '';
|
|
19
|
+
let preVoiceText = '';
|
|
20
|
+
let finalizedText = '';
|
|
21
|
+
|
|
22
|
+
function hideButton() {
|
|
23
|
+
document.body.classList.add('no-speech-api');
|
|
24
|
+
stopRecording();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Recording indicator element (created once, reused)
|
|
28
|
+
const indicator = document.createElement('div');
|
|
29
|
+
indicator.className = 'voice-recording-indicator hidden';
|
|
30
|
+
indicator.innerHTML = '<span class="voice-recording-dot"></span><span>Listening...</span>';
|
|
31
|
+
|
|
32
|
+
// Insert indicator before the input bar
|
|
33
|
+
const inputBar = $.messageInput.closest('.input-bar');
|
|
34
|
+
inputBar.parentNode.insertBefore(indicator, inputBar);
|
|
35
|
+
|
|
36
|
+
function updateTextarea() {
|
|
37
|
+
$.messageInput.value = preVoiceText + finalizedText + interimText;
|
|
38
|
+
// Trigger auto-resize
|
|
39
|
+
$.messageInput.style.height = 'auto';
|
|
40
|
+
$.messageInput.style.height = Math.min($.messageInput.scrollHeight, 200) + 'px';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function startRecording() {
|
|
44
|
+
if (isRecording) return;
|
|
45
|
+
if (getState('parallelMode')) {
|
|
46
|
+
addStatus('Voice input is not available in parallel mode', true, getPane(null));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
recognition = new SpeechRecognition();
|
|
51
|
+
recognition.lang = 'en-US';
|
|
52
|
+
recognition.continuous = true;
|
|
53
|
+
recognition.interimResults = true;
|
|
54
|
+
|
|
55
|
+
// Snapshot existing textarea content
|
|
56
|
+
const current = $.messageInput.value;
|
|
57
|
+
preVoiceText = current && !/\s$/.test(current) ? current + ' ' : current;
|
|
58
|
+
finalizedText = '';
|
|
59
|
+
interimText = '';
|
|
60
|
+
|
|
61
|
+
recognition.onresult = (event) => {
|
|
62
|
+
let finalChunk = '';
|
|
63
|
+
let interimChunk = '';
|
|
64
|
+
|
|
65
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
66
|
+
const transcript = event.results[i][0].transcript;
|
|
67
|
+
if (event.results[i].isFinal) {
|
|
68
|
+
finalChunk += transcript;
|
|
69
|
+
} else {
|
|
70
|
+
interimChunk += transcript;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (finalChunk) finalizedText += finalChunk;
|
|
75
|
+
interimText = interimChunk;
|
|
76
|
+
updateTextarea();
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
recognition.onend = () => {
|
|
80
|
+
if (shouldRestart && isRecording) {
|
|
81
|
+
// Chrome cuts off after ~60s of silence — restart
|
|
82
|
+
try { recognition.start(); } catch (_) { stopRecording(); }
|
|
83
|
+
} else {
|
|
84
|
+
cleanUp();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
recognition.onerror = (event) => {
|
|
89
|
+
const pane = getPane(null);
|
|
90
|
+
switch (event.error) {
|
|
91
|
+
case 'service-not-allowed':
|
|
92
|
+
case 'language-not-supported':
|
|
93
|
+
// API exists but browser doesn't actually support it (e.g. Edge)
|
|
94
|
+
hideButton();
|
|
95
|
+
break;
|
|
96
|
+
case 'not-allowed':
|
|
97
|
+
addStatus('Microphone access denied. Check browser permissions.', true, pane);
|
|
98
|
+
stopRecording();
|
|
99
|
+
break;
|
|
100
|
+
case 'no-speech':
|
|
101
|
+
// Silence — let onend restart if needed
|
|
102
|
+
break;
|
|
103
|
+
case 'network':
|
|
104
|
+
addStatus('Speech recognition network error', true, pane);
|
|
105
|
+
stopRecording();
|
|
106
|
+
break;
|
|
107
|
+
default:
|
|
108
|
+
addStatus('Speech recognition error: ' + event.error, true, pane);
|
|
109
|
+
stopRecording();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
isRecording = true;
|
|
114
|
+
shouldRestart = true;
|
|
115
|
+
$.micBtn.classList.add('recording');
|
|
116
|
+
indicator.classList.remove('hidden');
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
recognition.start();
|
|
120
|
+
} catch (_) {
|
|
121
|
+
// Constructor exists but service unavailable — hide permanently
|
|
122
|
+
hideButton();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function stopRecording() {
|
|
127
|
+
shouldRestart = false;
|
|
128
|
+
isRecording = false;
|
|
129
|
+
if (recognition) {
|
|
130
|
+
try { recognition.stop(); } catch (_) {}
|
|
131
|
+
}
|
|
132
|
+
cleanUp();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function cleanUp() {
|
|
136
|
+
$.micBtn.classList.remove('recording');
|
|
137
|
+
indicator.classList.add('hidden');
|
|
138
|
+
// Commit any remaining interim text
|
|
139
|
+
if (interimText) {
|
|
140
|
+
finalizedText += interimText;
|
|
141
|
+
interimText = '';
|
|
142
|
+
updateTextarea();
|
|
143
|
+
}
|
|
144
|
+
recognition = null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Toggle on mic button click
|
|
148
|
+
$.micBtn.addEventListener('click', () => {
|
|
149
|
+
if (isRecording) {
|
|
150
|
+
stopRecording();
|
|
151
|
+
} else {
|
|
152
|
+
startRecording();
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Stop recording on send (capture phase so we commit text before sendMessage reads it)
|
|
157
|
+
$.sendBtn.addEventListener('click', () => {
|
|
158
|
+
if (isRecording) stopRecording();
|
|
159
|
+
}, true);
|
|
160
|
+
|
|
161
|
+
// Stop on Enter key (before chat.js handles it)
|
|
162
|
+
$.messageInput.addEventListener('keydown', (e) => {
|
|
163
|
+
if (e.key === 'Enter' && !e.shiftKey && isRecording) {
|
|
164
|
+
stopRecording();
|
|
165
|
+
}
|
|
166
|
+
}, true);
|
|
167
|
+
|
|
168
|
+
// Stop on session switch
|
|
169
|
+
$.newSessionBtn.addEventListener('click', () => {
|
|
170
|
+
if (isRecording) stopRecording();
|
|
171
|
+
});
|
|
172
|
+
$.sessionList.addEventListener('click', () => {
|
|
173
|
+
if (isRecording) stopRecording();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Stop when parallel mode is toggled on
|
|
177
|
+
$.toggleParallelBtn.addEventListener('change', () => {
|
|
178
|
+
if (isRecording && $.toggleParallelBtn.checked) stopRecording();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Stop when tab is hidden
|
|
182
|
+
document.addEventListener('visibilitychange', () => {
|
|
183
|
+
if (document.hidden && isRecording) stopRecording();
|
|
184
|
+
});
|
|
185
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Welcome overlay — shown once on first visit
|
|
2
|
+
import { startTour } from './tour.js';
|
|
3
|
+
|
|
4
|
+
const STORAGE_KEY = 'claudeck-welcome-seen';
|
|
5
|
+
|
|
6
|
+
function init() {
|
|
7
|
+
if (localStorage.getItem(STORAGE_KEY)) return;
|
|
8
|
+
|
|
9
|
+
const overlay = document.getElementById('welcome-overlay');
|
|
10
|
+
if (!overlay) return;
|
|
11
|
+
|
|
12
|
+
overlay.classList.remove('hidden');
|
|
13
|
+
|
|
14
|
+
const getStartedBtn = document.getElementById('welcome-get-started');
|
|
15
|
+
const tourBtn = document.getElementById('welcome-take-tour');
|
|
16
|
+
|
|
17
|
+
getStartedBtn.addEventListener('click', () => dismiss(overlay));
|
|
18
|
+
|
|
19
|
+
tourBtn.addEventListener('click', () => {
|
|
20
|
+
dismiss(overlay, () => startTour());
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Also dismiss with Escape or Enter
|
|
24
|
+
function onKey(e) {
|
|
25
|
+
if (e.key === 'Escape' || e.key === 'Enter') {
|
|
26
|
+
dismiss(overlay);
|
|
27
|
+
document.removeEventListener('keydown', onKey);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
document.addEventListener('keydown', onKey);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function dismiss(overlay, onDone) {
|
|
34
|
+
localStorage.setItem(STORAGE_KEY, '1');
|
|
35
|
+
overlay.classList.add('hiding');
|
|
36
|
+
overlay.addEventListener('transitionend', () => {
|
|
37
|
+
overlay.classList.add('hidden');
|
|
38
|
+
overlay.remove();
|
|
39
|
+
if (onDone) onDone();
|
|
40
|
+
}, { once: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
init();
|