@lopecode/channel 0.1.3 → 0.1.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.
- package/README.md +205 -0
- package/claude-code-pairing-module.js +292 -139
- package/lopecode-channel.ts +224 -11
- package/package.json +3 -2
- package/sync-module.ts +190 -0
- package/inject-module.js +0 -131
|
@@ -23,15 +23,23 @@ const _cc_messages = function _cc_messages(Inputs){return(
|
|
|
23
23
|
)};
|
|
24
24
|
|
|
25
25
|
const _cc_watches = function _cc_watches(Inputs){return(
|
|
26
|
-
Inputs.input([
|
|
26
|
+
Inputs.input([
|
|
27
|
+
{ name: "hash", module: null },
|
|
28
|
+
{ name: "currentModules", module: null }
|
|
29
|
+
])
|
|
30
|
+
)};
|
|
31
|
+
|
|
32
|
+
const _cc_module = function _cc_module(thisModule){return(
|
|
33
|
+
thisModule()
|
|
27
34
|
)};
|
|
28
35
|
|
|
29
|
-
const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages, viewof_cc_watches, summarizeJS, observe, realize, runtime, invalidation){return(
|
|
36
|
+
const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages, viewof_cc_watches, summarizeJS, observe, realize, compile, createModule, deleteModule, lookupVariable, exportToHTML, cc_module, runtime, invalidation){return(
|
|
30
37
|
(function() {
|
|
31
38
|
var port = cc_config.port, host = cc_config.host;
|
|
32
39
|
var ws = null;
|
|
33
40
|
var paired = false;
|
|
34
|
-
|
|
41
|
+
// Cache the observer factory (used by lopepage to render cells)
|
|
42
|
+
var ojs_observer = window.__ojs_observer || null;
|
|
35
43
|
|
|
36
44
|
function serializeValue(value, maxLen) {
|
|
37
45
|
maxLen = maxLen || 500;
|
|
@@ -79,22 +87,21 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
79
87
|
var name = cmd.params.name;
|
|
80
88
|
var moduleName = cmd.params.module;
|
|
81
89
|
var targetModule = findModule(runtime, moduleName);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return { ok: false, error: "Variable not found: " + name };
|
|
90
|
+
if (!targetModule) return { ok: false, error: "Module not found: " + (moduleName || "default") };
|
|
91
|
+
return lookupVariable(name, targetModule).then(function(v) {
|
|
92
|
+
if (!v) return { ok: false, error: "Variable not found: " + name };
|
|
93
|
+
return {
|
|
94
|
+
ok: true,
|
|
95
|
+
result: {
|
|
96
|
+
name: v._name,
|
|
97
|
+
hasValue: v._value !== undefined,
|
|
98
|
+
hasError: v._error !== undefined,
|
|
99
|
+
value: serializeValue(v._value),
|
|
100
|
+
error: v._error ? v._error.message : undefined,
|
|
101
|
+
reachable: v._reachable
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
});
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
case "define-variable": {
|
|
@@ -102,61 +109,95 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
102
109
|
var definition = cmd.params.definition;
|
|
103
110
|
var inputs = cmd.params.inputs || [];
|
|
104
111
|
var moduleName = cmd.params.module;
|
|
105
|
-
var mod = runtime
|
|
106
|
-
if (!mod && runtime.mains) {
|
|
107
|
-
for (var entry of runtime.mains) {
|
|
108
|
-
if (!FRAMEWORK_MODULES.has(entry[0])) { mod = entry[1]; break; }
|
|
109
|
-
}
|
|
110
|
-
}
|
|
112
|
+
var mod = findModule(runtime, moduleName);
|
|
111
113
|
if (!mod) return { ok: false, error: "Module not found: " + (moduleName || "default") };
|
|
112
114
|
|
|
113
115
|
return realize([definition], runtime).then(function(results) {
|
|
114
116
|
var fn = results[0];
|
|
115
117
|
if (typeof fn !== "function") return { ok: false, error: "Definition must evaluate to a function" };
|
|
116
118
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (v._name === name && v._module === mod) { existingVar = v; break; }
|
|
120
|
-
}
|
|
121
|
-
|
|
119
|
+
// Use module._scope to check for existing variable
|
|
120
|
+
var existingVar = mod._scope.get(name);
|
|
122
121
|
if (existingVar) {
|
|
123
122
|
existingVar.define(name, inputs, fn);
|
|
124
123
|
} else {
|
|
125
|
-
|
|
126
|
-
for (var v of runtime._variables) {
|
|
127
|
-
if (v._name === "__ojs_observer" && typeof v._value === "function") { obsFactory = v._value; break; }
|
|
128
|
-
}
|
|
129
|
-
mod.variable(obsFactory ? obsFactory(name) : {}).define(name, inputs, fn);
|
|
124
|
+
mod.variable(ojs_observer ? ojs_observer(name) : {}).define(name, inputs, fn);
|
|
130
125
|
}
|
|
126
|
+
// Auto-watch the defined variable so the result arrives reactively
|
|
127
|
+
watchVariable(runtime, name, moduleName || null);
|
|
131
128
|
return { ok: true, result: { success: true, name: name, module: moduleName || "default" } };
|
|
132
129
|
}).catch(function(e) {
|
|
133
130
|
return { ok: false, error: "define failed: " + e.message };
|
|
134
131
|
});
|
|
135
132
|
}
|
|
136
133
|
|
|
134
|
+
case "define-cell": {
|
|
135
|
+
var source = cmd.params.source;
|
|
136
|
+
var moduleName = cmd.params.module;
|
|
137
|
+
var mod = findModule(runtime, moduleName);
|
|
138
|
+
if (!mod) return { ok: false, error: "Module not found: " + (moduleName || "default") };
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
var compiled = compile(source);
|
|
142
|
+
if (!compiled || compiled.length === 0) return { ok: false, error: "Compilation returned no definitions" };
|
|
143
|
+
|
|
144
|
+
var definitions = [];
|
|
145
|
+
for (var ci = 0; ci < compiled.length; ci++) {
|
|
146
|
+
definitions.push(compiled[ci]._definition);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return realize(definitions, runtime).then(function(fns) {
|
|
150
|
+
var defined = [];
|
|
151
|
+
for (var di = 0; di < compiled.length; di++) {
|
|
152
|
+
var cellDef = compiled[di];
|
|
153
|
+
var fn = fns[di];
|
|
154
|
+
var cellName = cellDef._name;
|
|
155
|
+
var cellInputs = cellDef._inputs || [];
|
|
156
|
+
|
|
157
|
+
var existingVar = mod._scope.get(cellName);
|
|
158
|
+
if (existingVar) {
|
|
159
|
+
existingVar.define(cellName, cellInputs, fn);
|
|
160
|
+
} else {
|
|
161
|
+
mod.variable(ojs_observer ? ojs_observer(cellName) : {}).define(cellName, cellInputs, fn);
|
|
162
|
+
}
|
|
163
|
+
defined.push(cellName);
|
|
164
|
+
|
|
165
|
+
// Auto-watch non-internal variables
|
|
166
|
+
if (cellName && !cellName.startsWith("module ")) {
|
|
167
|
+
watchVariable(runtime, cellName, moduleName || null);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { ok: true, result: { success: true, defined: defined, module: moduleName || "default" } };
|
|
171
|
+
}).catch(function(e) {
|
|
172
|
+
return { ok: false, error: "define-cell realize failed: " + e.message };
|
|
173
|
+
});
|
|
174
|
+
} catch (e) {
|
|
175
|
+
return { ok: false, error: "define-cell compile failed: " + e.message };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
137
179
|
case "delete-variable": {
|
|
138
180
|
var name = cmd.params.name;
|
|
139
181
|
var moduleName = cmd.params.module;
|
|
140
182
|
var targetModule = findModule(runtime, moduleName);
|
|
141
183
|
if (!targetModule) return { ok: false, error: "Module not found: " + (moduleName || "main") };
|
|
142
|
-
|
|
143
|
-
if (v
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
return { ok: false, error: "Variable not found: " + name + " in module " + (moduleName || "main") };
|
|
184
|
+
return lookupVariable(name, targetModule).then(function(v) {
|
|
185
|
+
if (!v) return { ok: false, error: "Variable not found: " + name + " in module " + (moduleName || "main") };
|
|
186
|
+
v.delete();
|
|
187
|
+
return { ok: true, result: { success: true, name: name, module: targetModule._name || "main" } };
|
|
188
|
+
});
|
|
149
189
|
}
|
|
150
190
|
|
|
151
191
|
case "list-variables": {
|
|
152
192
|
var moduleName = cmd.params.module;
|
|
153
193
|
var targetModule = findModule(runtime, moduleName);
|
|
194
|
+
if (!targetModule) return { ok: false, error: "Module not found: " + (moduleName || "default") };
|
|
195
|
+
// Use module._scope instead of scanning runtime._variables
|
|
154
196
|
var variables = [];
|
|
155
|
-
for (var
|
|
156
|
-
|
|
157
|
-
if (targetModule && v._module !== targetModule) continue;
|
|
197
|
+
for (var entry of targetModule._scope) {
|
|
198
|
+
var v = entry[1];
|
|
158
199
|
variables.push({
|
|
159
|
-
name:
|
|
200
|
+
name: entry[0],
|
|
160
201
|
module: (v._module && v._module._name) || "main",
|
|
161
202
|
hasValue: v._value !== undefined,
|
|
162
203
|
hasError: v._error !== undefined,
|
|
@@ -167,12 +208,56 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
167
208
|
return { ok: true, result: variables };
|
|
168
209
|
}
|
|
169
210
|
|
|
211
|
+
case "list-cells": {
|
|
212
|
+
var moduleName = cmd.params.module;
|
|
213
|
+
var targetModule = findModule(runtime, moduleName);
|
|
214
|
+
if (!targetModule) return { ok: false, error: "Module not found: " + (moduleName || "default") };
|
|
215
|
+
var cells = [];
|
|
216
|
+
for (var entry of targetModule._scope) {
|
|
217
|
+
var v = entry[1];
|
|
218
|
+
var defStr = "";
|
|
219
|
+
try { defStr = String(v._definition).slice(0, 300); } catch(e) {}
|
|
220
|
+
cells.push({
|
|
221
|
+
name: entry[0],
|
|
222
|
+
inputs: (v._inputs || []).map(function(inp) { return inp._name || "?"; }),
|
|
223
|
+
definition: defStr,
|
|
224
|
+
hasValue: v._value !== undefined,
|
|
225
|
+
hasError: v._error !== undefined,
|
|
226
|
+
error: v._error ? (v._error.message || String(v._error)) : undefined
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
cells.sort(function(a, b) { return a.name.localeCompare(b.name); });
|
|
230
|
+
return { ok: true, result: cells };
|
|
231
|
+
}
|
|
232
|
+
|
|
170
233
|
case "run-tests": {
|
|
171
234
|
var filter = cmd.params.filter;
|
|
172
235
|
var timeout = cmd.params.timeout || 30000;
|
|
173
236
|
return runTests(runtime, filter, timeout);
|
|
174
237
|
}
|
|
175
238
|
|
|
239
|
+
case "create-module": {
|
|
240
|
+
var moduleName = cmd.params.name;
|
|
241
|
+
if (!moduleName) return { ok: false, error: "Module name is required" };
|
|
242
|
+
try {
|
|
243
|
+
createModule(moduleName, runtime);
|
|
244
|
+
return { ok: true, result: { success: true, name: moduleName } };
|
|
245
|
+
} catch (e) {
|
|
246
|
+
return { ok: false, error: e.message };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
case "delete-module": {
|
|
251
|
+
var moduleName = cmd.params.name;
|
|
252
|
+
if (!moduleName) return { ok: false, error: "Module name is required" };
|
|
253
|
+
try {
|
|
254
|
+
deleteModule(moduleName, runtime);
|
|
255
|
+
return { ok: true, result: { success: true, name: moduleName } };
|
|
256
|
+
} catch (e) {
|
|
257
|
+
return { ok: false, error: e.message };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
176
261
|
case "eval": {
|
|
177
262
|
var code = cmd.params.code;
|
|
178
263
|
try {
|
|
@@ -184,7 +269,7 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
184
269
|
}
|
|
185
270
|
|
|
186
271
|
case "fork": {
|
|
187
|
-
return handleFork(
|
|
272
|
+
return handleFork();
|
|
188
273
|
}
|
|
189
274
|
|
|
190
275
|
case "watch": {
|
|
@@ -302,17 +387,14 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
302
387
|
var key = (moduleName || "main") + ":" + name;
|
|
303
388
|
if (watchers.has(key)) return { ok: true, result: { already_watching: true, key: key } };
|
|
304
389
|
|
|
305
|
-
|
|
306
|
-
var
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
break;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
390
|
+
// Use findModule for named modules, cc_module (thisModule) for unqualified lookups
|
|
391
|
+
var targetModule = moduleName ? findModule(runtime, moduleName) : cc_module;
|
|
392
|
+
if (!targetModule) return Promise.resolve({ ok: false, error: "Module not found: " + (moduleName || "default") });
|
|
393
|
+
|
|
394
|
+
return lookupVariable(name, targetModule).then(function(targetVar) {
|
|
313
395
|
if (!targetVar) return { ok: false, error: "Variable not found: " + name };
|
|
314
396
|
|
|
315
|
-
var
|
|
397
|
+
var resolvedModule = (targetVar._module && targetVar._module._name) || "main";
|
|
316
398
|
var debounceTimer = null;
|
|
317
399
|
var latestValue = undefined;
|
|
318
400
|
var latestError = undefined;
|
|
@@ -328,19 +410,19 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
328
410
|
var watches = viewof_cc_watches.value.slice();
|
|
329
411
|
var found = false;
|
|
330
412
|
for (var i = 0; i < watches.length; i++) {
|
|
331
|
-
if (watches[i].name === name && watches[i].module ===
|
|
332
|
-
watches[i] = { name: name, module:
|
|
413
|
+
if (watches[i].name === name && (watches[i].module === resolvedModule || watches[i].module == null)) {
|
|
414
|
+
watches[i] = { name: name, module: resolvedModule, value: serialized.slice(0, 200), updated: now };
|
|
333
415
|
found = true;
|
|
334
416
|
break;
|
|
335
417
|
}
|
|
336
418
|
}
|
|
337
|
-
if (!found) watches.push({ name: name, module:
|
|
419
|
+
if (!found) watches.push({ name: name, module: resolvedModule, value: serialized.slice(0, 200), updated: now });
|
|
338
420
|
viewof_cc_watches.value = watches;
|
|
339
421
|
viewof_cc_watches.dispatchEvent(new Event("input"));
|
|
340
422
|
|
|
341
423
|
// Send over WebSocket if connected
|
|
342
424
|
if (!paired || !ws) return;
|
|
343
|
-
var msg = { type: "variable-update", name: name, module:
|
|
425
|
+
var msg = { type: "variable-update", name: name, module: resolvedModule };
|
|
344
426
|
if (latestError) { msg.error = latestError.message || String(latestError); }
|
|
345
427
|
else { msg.value = serialized; }
|
|
346
428
|
ws.send(JSON.stringify(msg));
|
|
@@ -370,7 +452,7 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
370
452
|
cancel();
|
|
371
453
|
// Remove from watches table
|
|
372
454
|
var watches = viewof_cc_watches.value.filter(function(w) {
|
|
373
|
-
return !(w.name === name && w.module ===
|
|
455
|
+
return !(w.name === name && w.module === resolvedModule);
|
|
374
456
|
});
|
|
375
457
|
viewof_cc_watches.value = watches;
|
|
376
458
|
viewof_cc_watches.dispatchEvent(new Event("input"));
|
|
@@ -379,6 +461,7 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
379
461
|
});
|
|
380
462
|
|
|
381
463
|
return { ok: true, result: { watching: true, key: key } };
|
|
464
|
+
}); // close lookupVariable.then
|
|
382
465
|
}
|
|
383
466
|
|
|
384
467
|
function unwatchVariable(name, moduleName) {
|
|
@@ -397,30 +480,30 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
397
480
|
return { ok: true, result: { unwatched_all: true, count: keys.length } };
|
|
398
481
|
}
|
|
399
482
|
|
|
400
|
-
function handleFork(
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
});
|
|
410
|
-
return;
|
|
411
|
-
} catch (e) {
|
|
412
|
-
resolve({ ok: false, error: "Export failed: " + e.message });
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
483
|
+
function handleFork() {
|
|
484
|
+
if (typeof exportToHTML !== "function") {
|
|
485
|
+
return { ok: false, error: "exportToHTML not available. Does this notebook include @tomlarkworthy/exporter-2?" };
|
|
486
|
+
}
|
|
487
|
+
return Promise.resolve(exportToHTML({ mains: runtime.mains })).then(function(result) {
|
|
488
|
+
// exportToHTML returns { source: string, report: object }
|
|
489
|
+
var html = typeof result === "string" ? result : result.source;
|
|
490
|
+
if (!html || typeof html !== "string") {
|
|
491
|
+
return { ok: false, error: "Export returned no HTML source" };
|
|
416
492
|
}
|
|
417
|
-
|
|
493
|
+
return { ok: true, result: { html: html } };
|
|
494
|
+
}).catch(function(e) {
|
|
495
|
+
return { ok: false, error: "Export failed: " + e.message };
|
|
418
496
|
});
|
|
419
497
|
}
|
|
420
498
|
|
|
421
499
|
function connect(token) {
|
|
422
500
|
if (ws) { ws.close(); ws = null; }
|
|
423
501
|
|
|
502
|
+
// Persist token for reconnection across reloads
|
|
503
|
+
if (token) {
|
|
504
|
+
try { sessionStorage.setItem("lopecode_cc_token", token); } catch(e) {}
|
|
505
|
+
}
|
|
506
|
+
|
|
424
507
|
// Parse port from token format LOPE-PORT-XXXX
|
|
425
508
|
var connectPort = port;
|
|
426
509
|
if (token) {
|
|
@@ -472,11 +555,13 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
472
555
|
hash: location.hash
|
|
473
556
|
}));
|
|
474
557
|
|
|
475
|
-
//
|
|
558
|
+
// Set up watches from cc_watches (initialized with defaults via dependency resolution)
|
|
476
559
|
var runtime2 = window.__ojs_runtime;
|
|
477
560
|
if (runtime2) {
|
|
478
|
-
|
|
479
|
-
|
|
561
|
+
var initialWatches = viewof_cc_watches.value || [];
|
|
562
|
+
for (var i = 0; i < initialWatches.length; i++) {
|
|
563
|
+
watchVariable(runtime2, initialWatches[i].name, initialWatches[i].module);
|
|
564
|
+
}
|
|
480
565
|
}
|
|
481
566
|
break;
|
|
482
567
|
|
|
@@ -496,6 +581,17 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
496
581
|
cc_messages.dispatchEvent(new Event("input"));
|
|
497
582
|
break;
|
|
498
583
|
|
|
584
|
+
case "tool-activity":
|
|
585
|
+
var msgs2 = cc_messages.value.concat([{
|
|
586
|
+
role: "tool",
|
|
587
|
+
tool_name: msg.tool_name,
|
|
588
|
+
content: msg.summary,
|
|
589
|
+
timestamp: msg.timestamp || Date.now()
|
|
590
|
+
}]);
|
|
591
|
+
cc_messages.value = msgs2;
|
|
592
|
+
cc_messages.dispatchEvent(new Event("input"));
|
|
593
|
+
break;
|
|
594
|
+
|
|
499
595
|
case "command":
|
|
500
596
|
Promise.resolve(handleCommand(msg)).then(function(result) {
|
|
501
597
|
ws.send(JSON.stringify({
|
|
@@ -527,13 +623,23 @@ const _cc_ws = function _cc_ws(cc_config, cc_notebook_id, cc_status, cc_messages
|
|
|
527
623
|
ws.onerror = function() {};
|
|
528
624
|
}
|
|
529
625
|
|
|
530
|
-
// Auto-connect
|
|
531
|
-
// Hash format: #view=R100(...)&cc=LOPE-PORT-XXXX
|
|
626
|
+
// Auto-connect: check hash param first, then sessionStorage fallback
|
|
532
627
|
(function autoConnect() {
|
|
628
|
+
var token = null;
|
|
629
|
+
|
|
630
|
+
// 1. Check &cc=TOKEN in hash
|
|
533
631
|
var hash = location.hash || "";
|
|
534
632
|
var match = hash.match(/[&?]cc=(LOPE-[A-Z0-9-]+)/);
|
|
535
633
|
if (match) {
|
|
536
|
-
|
|
634
|
+
token = match[1];
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// 2. Fallback to sessionStorage (survives reloads even if hash is mangled)
|
|
638
|
+
if (!token) {
|
|
639
|
+
try { token = sessionStorage.getItem("lopecode_cc_token"); } catch(e) {}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (token) {
|
|
537
643
|
// Small delay to let the WebSocket server be ready
|
|
538
644
|
setTimeout(function() { connect(token); }, 500);
|
|
539
645
|
}
|
|
@@ -555,38 +661,31 @@ const _cc_watch_table = function _cc_watch_table(cc_watches, Inputs){return(
|
|
|
555
661
|
})
|
|
556
662
|
)};
|
|
557
663
|
|
|
558
|
-
const _cc_change_forwarder = function _cc_change_forwarder(cc_ws, invalidation){return(
|
|
664
|
+
const _cc_change_forwarder = function _cc_change_forwarder(cc_ws, history, invalidation){return(
|
|
559
665
|
(function() {
|
|
560
666
|
var highWaterMark = 0;
|
|
561
667
|
var initializing = true;
|
|
562
668
|
|
|
563
669
|
var interval = setInterval(function() {
|
|
564
670
|
if (!cc_ws.paired || !cc_ws.ws) return;
|
|
565
|
-
|
|
566
|
-
if (!runtime) return;
|
|
671
|
+
if (!history || !Array.isArray(history)) return;
|
|
567
672
|
|
|
568
|
-
|
|
569
|
-
if (v._name === "history" && v._value && Array.isArray(v._value)) {
|
|
570
|
-
var history = v._value;
|
|
571
|
-
var total = history.length;
|
|
673
|
+
var total = history.length;
|
|
572
674
|
|
|
573
|
-
|
|
574
|
-
|
|
675
|
+
if (initializing) { highWaterMark = total; initializing = false; return; }
|
|
676
|
+
if (total <= highWaterMark) return;
|
|
575
677
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
break;
|
|
588
|
-
}
|
|
589
|
-
}
|
|
678
|
+
var newEntries = history.slice(highWaterMark).map(function(e) {
|
|
679
|
+
return {
|
|
680
|
+
t: e.t, op: e.op, module: e.module, _name: e._name,
|
|
681
|
+
_inputs: e._inputs,
|
|
682
|
+
_definition: typeof e._definition === "function"
|
|
683
|
+
? e._definition.toString().slice(0, 500)
|
|
684
|
+
: String(e._definition || "").slice(0, 500)
|
|
685
|
+
};
|
|
686
|
+
});
|
|
687
|
+
highWaterMark = total;
|
|
688
|
+
cc_ws.ws.send(JSON.stringify({ type: "cell-change", changes: newEntries }));
|
|
590
689
|
}, 1000);
|
|
591
690
|
|
|
592
691
|
invalidation.then(function() { clearInterval(interval); });
|
|
@@ -603,39 +702,41 @@ const _cc_chat = function _cc_chat(cc_messages, cc_status, cc_ws, md, htl, Input
|
|
|
603
702
|
disconnected: "#ef4444"
|
|
604
703
|
};
|
|
605
704
|
|
|
606
|
-
function
|
|
705
|
+
function renderConnect() {
|
|
706
|
+
var row = document.createElement("div");
|
|
707
|
+
row.style.cssText = "display:flex;gap:8px;justify-content:center;align-items:center;padding:16px;";
|
|
708
|
+
|
|
607
709
|
var tokenInput = document.createElement("input");
|
|
608
710
|
tokenInput.type = "text";
|
|
609
711
|
tokenInput.placeholder = "LOPE-XXXX";
|
|
610
|
-
tokenInput.style.cssText = "font-family:monospace;font-size:16px;padding:8px 12px;border:2px solid
|
|
712
|
+
tokenInput.style.cssText = "font-family:var(--monospace, monospace);font-size:16px;padding:8px 12px;border:2px solid var(--theme-foreground-faint);border-radius:6px;width:140px;text-transform:uppercase;letter-spacing:2px;background:var(--theme-background-a);color:var(--theme-foreground);";
|
|
611
713
|
|
|
612
714
|
var connectBtn = document.createElement("button");
|
|
613
715
|
connectBtn.textContent = "Connect";
|
|
614
|
-
connectBtn.style.cssText = "padding:8px 20px;background
|
|
716
|
+
connectBtn.style.cssText = "padding:8px 20px;background:var(--theme-foreground);color:var(--theme-background-a);border:none;border-radius:6px;cursor:pointer;font-size:14px;font-weight:500;";
|
|
615
717
|
connectBtn.onclick = function() {
|
|
616
718
|
var token = tokenInput.value.trim();
|
|
617
719
|
if (token) cc_ws.connect(token);
|
|
618
720
|
};
|
|
619
|
-
|
|
620
721
|
tokenInput.addEventListener("keydown", function(e) {
|
|
621
722
|
if (e.key === "Enter") connectBtn.click();
|
|
622
723
|
});
|
|
623
724
|
|
|
725
|
+
row.append(tokenInput, connectBtn);
|
|
726
|
+
|
|
727
|
+
var guide = document.createElement("details");
|
|
728
|
+
guide.style.cssText = "padding:0 16px 16px;font-size:13px;color:var(--theme-foreground);";
|
|
729
|
+
guide.innerHTML = '<summary style="cursor:pointer;font-weight:600;font-size:14px;">Setup guide</summary>' +
|
|
730
|
+
'<ol style="margin:8px 0 0;padding-left:20px;line-height:1.8;">' +
|
|
731
|
+
'<li>Install <a href="https://bun.sh" target="_blank">Bun</a> if needed, then:<br>' +
|
|
732
|
+
'<code style="background:var(--theme-background-alt);padding:2px 6px;border-radius:3px;font-size:12px;">bun install -g @lopecode/channel</code><br>' +
|
|
733
|
+
'<code style="background:var(--theme-background-alt);padding:2px 6px;border-radius:3px;font-size:12px;">claude mcp add lopecode bunx @lopecode/channel</code></li>' +
|
|
734
|
+
'<li>Start Claude Code:<br><code style="background:var(--theme-background-alt);padding:2px 6px;border-radius:3px;font-size:12px;">claude --dangerously-load-development-channels server:lopecode</code></li>' +
|
|
735
|
+
'<li>Ask Claude for a pairing token, then paste it above</li>' +
|
|
736
|
+
'</ol>';
|
|
737
|
+
|
|
624
738
|
var container = document.createElement("div");
|
|
625
|
-
container.
|
|
626
|
-
container.innerHTML = '<div style="padding:24px;max-width:400px;margin:0 auto;text-align:center;">' +
|
|
627
|
-
'<div style="font-size:24px;margin-bottom:8px;">Claude Channel</div>' +
|
|
628
|
-
'<p style="color:#6b7280;margin-bottom:20px;font-size:14px;">Connect to Claude Code to chat with Claude from this notebook.</p>' +
|
|
629
|
-
'<div style="text-align:left;background:#f3f4f6;padding:16px;border-radius:8px;margin-bottom:20px;font-size:13px;">' +
|
|
630
|
-
'<div style="font-weight:600;margin-bottom:8px;">Setup:</div>' +
|
|
631
|
-
'<ol style="margin:0;padding-left:20px;line-height:1.8;">' +
|
|
632
|
-
'<li>Start Claude with the channel flag:<br><code style="background:#e5e7eb;padding:2px 6px;border-radius:3px;font-size:12px;">claude --dangerously-load-development-channels server:lopecode</code></li>' +
|
|
633
|
-
'<li>Copy the pairing token from the terminal</li>' +
|
|
634
|
-
'<li>Paste it below and click Connect</li>' +
|
|
635
|
-
'</ol></div>' +
|
|
636
|
-
'<div style="display:flex;gap:8px;justify-content:center;align-items:center;" class="cc-token-row"></div>' +
|
|
637
|
-
'</div>';
|
|
638
|
-
container.querySelector(".cc-token-row").append(tokenInput, connectBtn);
|
|
739
|
+
container.append(row, guide);
|
|
639
740
|
return container;
|
|
640
741
|
}
|
|
641
742
|
|
|
@@ -648,11 +749,11 @@ const _cc_chat = function _cc_chat(cc_messages, cc_status, cc_ws, md, htl, Input
|
|
|
648
749
|
textarea.className = "cc-input";
|
|
649
750
|
textarea.placeholder = "Message Claude...";
|
|
650
751
|
textarea.rows = 2;
|
|
651
|
-
textarea.style.cssText = "width:100%;box-sizing:border-box;resize:none;border:1px solid
|
|
752
|
+
textarea.style.cssText = "width:100%;box-sizing:border-box;resize:none;border:1px solid var(--theme-foreground-faint);border-radius:8px;padding:10px 12px;font-family:inherit;font-size:14px;outline:none;background:var(--theme-background-a);color:var(--theme-foreground);";
|
|
652
753
|
|
|
653
754
|
var sendBtn = document.createElement("button");
|
|
654
755
|
sendBtn.textContent = "Send";
|
|
655
|
-
sendBtn.style.cssText = "padding:8px 16px;background
|
|
756
|
+
sendBtn.style.cssText = "padding:8px 16px;background:var(--theme-foreground);color:var(--theme-background-a);border:none;border-radius:6px;cursor:pointer;font-size:13px;font-weight:500;margin-left:auto;";
|
|
656
757
|
|
|
657
758
|
function sendMessage() {
|
|
658
759
|
var text = textarea.value.trim();
|
|
@@ -670,7 +771,7 @@ const _cc_chat = function _cc_chat(cc_messages, cc_status, cc_ws, md, htl, Input
|
|
|
670
771
|
});
|
|
671
772
|
|
|
672
773
|
var inputRow = document.createElement("div");
|
|
673
|
-
inputRow.style.cssText = "display:flex;gap:8px;padding:12px;align-items:flex-end;border-top:1px solid
|
|
774
|
+
inputRow.style.cssText = "display:flex;gap:8px;padding:12px;align-items:flex-end;border-top:1px solid var(--theme-foreground-faint);";
|
|
674
775
|
inputRow.append(textarea, sendBtn);
|
|
675
776
|
|
|
676
777
|
var container = document.createElement("div");
|
|
@@ -680,15 +781,47 @@ const _cc_chat = function _cc_chat(cc_messages, cc_status, cc_ws, md, htl, Input
|
|
|
680
781
|
|
|
681
782
|
function updateMessages() {
|
|
682
783
|
messagesDiv.innerHTML = "";
|
|
683
|
-
|
|
684
|
-
|
|
784
|
+
var msgs = cc_messages.value;
|
|
785
|
+
var i = 0;
|
|
786
|
+
while (i < msgs.length) {
|
|
787
|
+
var msg = msgs[i];
|
|
788
|
+
|
|
789
|
+
// Group consecutive tool messages into a collapsible block
|
|
790
|
+
if (msg.role === "tool") {
|
|
791
|
+
var toolGroup = [];
|
|
792
|
+
while (i < msgs.length && msgs[i].role === "tool") {
|
|
793
|
+
toolGroup.push(msgs[i]);
|
|
794
|
+
i++;
|
|
795
|
+
}
|
|
796
|
+
var details = document.createElement("details");
|
|
797
|
+
details.style.cssText = "max-width:90%;align-self:flex-start;font-size:12px;opacity:0.85;margin:2px 0;";
|
|
798
|
+
var summary = document.createElement("summary");
|
|
799
|
+
summary.style.cssText = "cursor:pointer;padding:4px 10px;border-radius:8px;" +
|
|
800
|
+
"background:var(--theme-background-alt, #f0f0f0);color:var(--theme-foreground, #333);" +
|
|
801
|
+
"font-family:var(--monospace, monospace);border-left:2px solid var(--theme-foreground-faint, #ccc);list-style:inside;font-size:12px;";
|
|
802
|
+
summary.textContent = toolGroup.length === 1
|
|
803
|
+
? "\u{1F527} " + toolGroup[0].content
|
|
804
|
+
: "\u{1F527} " + toolGroup.length + " tool calls \u2014 " + toolGroup[toolGroup.length - 1].content;
|
|
805
|
+
details.appendChild(summary);
|
|
806
|
+
var list = document.createElement("div");
|
|
807
|
+
list.style.cssText = "padding:4px 10px 4px 20px;font-family:var(--monospace, monospace);color:var(--theme-foreground, #333);line-height:1.6;font-size:11px;";
|
|
808
|
+
for (var j = 0; j < toolGroup.length; j++) {
|
|
809
|
+
var line = document.createElement("div");
|
|
810
|
+
line.textContent = toolGroup[j].content;
|
|
811
|
+
list.appendChild(line);
|
|
812
|
+
}
|
|
813
|
+
details.appendChild(list);
|
|
814
|
+
messagesDiv.appendChild(details);
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
|
|
685
818
|
var bubble = document.createElement("div");
|
|
686
819
|
bubble.className = "cc-msg cc-msg-" + msg.role;
|
|
687
820
|
var isUser = msg.role === "user";
|
|
688
821
|
bubble.style.cssText = "max-width:80%;padding:10px 14px;border-radius:12px;font-size:14px;line-height:1.5;" +
|
|
689
822
|
(isUser
|
|
690
|
-
? "align-self:flex-end;background
|
|
691
|
-
: "align-self:flex-start;background
|
|
823
|
+
? "align-self:flex-end;background:var(--theme-foreground);color:var(--theme-background-a);border-bottom-right-radius:4px;"
|
|
824
|
+
: "align-self:flex-start;background:var(--theme-background-b);color:var(--theme-foreground);border-bottom-left-radius:4px;");
|
|
692
825
|
|
|
693
826
|
if (isUser) {
|
|
694
827
|
bubble.textContent = msg.content;
|
|
@@ -700,6 +833,7 @@ const _cc_chat = function _cc_chat(cc_messages, cc_status, cc_ws, md, htl, Input
|
|
|
700
833
|
} catch(e) { bubble.textContent = msg.content; }
|
|
701
834
|
}
|
|
702
835
|
messagesDiv.appendChild(bubble);
|
|
836
|
+
i++;
|
|
703
837
|
}
|
|
704
838
|
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
|
705
839
|
}
|
|
@@ -709,13 +843,16 @@ const _cc_chat = function _cc_chat(cc_messages, cc_status, cc_ws, md, htl, Input
|
|
|
709
843
|
return container;
|
|
710
844
|
}
|
|
711
845
|
|
|
846
|
+
var isConnected = (cc_status.value === "connected");
|
|
847
|
+
|
|
712
848
|
var wrapper = document.createElement("div");
|
|
713
849
|
wrapper.className = "cc-chat";
|
|
714
|
-
wrapper.style.cssText = "border:1px solid
|
|
850
|
+
wrapper.style.cssText = "border:1px solid var(--theme-foreground-faint);border-radius:12px;overflow:hidden;display:flex;flex-direction:column;background:var(--theme-background-a);font-family:inherit;" +
|
|
851
|
+
(isConnected ? "height:400px;" : "");
|
|
715
852
|
|
|
716
853
|
var statusBar = document.createElement("div");
|
|
717
854
|
statusBar.className = "cc-status-bar";
|
|
718
|
-
statusBar.style.cssText = "display:flex;align-items:center;gap:6px;padding:8px 12px;border-bottom:1px solid
|
|
855
|
+
statusBar.style.cssText = "display:flex;align-items:center;gap:6px;padding:8px 12px;border-bottom:1px solid var(--theme-foreground-faint);font-size:12px;color:var(--theme-foreground-faint);background:var(--theme-background-b);";
|
|
719
856
|
|
|
720
857
|
var statusDot = document.createElement("span");
|
|
721
858
|
statusDot.style.cssText = "width:8px;height:8px;border-radius:50%;display:inline-block;";
|
|
@@ -735,18 +872,21 @@ const _cc_chat = function _cc_chat(cc_messages, cc_status, cc_ws, md, htl, Input
|
|
|
735
872
|
|
|
736
873
|
function render() {
|
|
737
874
|
var status = cc_status.value || "disconnected";
|
|
875
|
+
var connected = (status === "connected");
|
|
738
876
|
statusDot.style.background = statusColors[status] || "#ef4444";
|
|
739
|
-
statusText.textContent =
|
|
877
|
+
statusText.textContent = connected ? "Connected to Claude Code"
|
|
740
878
|
: (status === "connecting" || status === "pairing") ? "Connecting..."
|
|
741
879
|
: "Not connected";
|
|
742
880
|
|
|
881
|
+
wrapper.style.height = connected ? "400px" : "";
|
|
882
|
+
|
|
743
883
|
body.innerHTML = "";
|
|
744
|
-
if (
|
|
884
|
+
if (connected) {
|
|
745
885
|
chatView = renderChat();
|
|
746
886
|
body.appendChild(chatView);
|
|
747
887
|
} else {
|
|
748
888
|
chatView = null;
|
|
749
|
-
body.appendChild(
|
|
889
|
+
body.appendChild(renderConnect());
|
|
750
890
|
}
|
|
751
891
|
}
|
|
752
892
|
|
|
@@ -788,18 +928,31 @@ export default function define(runtime, observer) {
|
|
|
788
928
|
$def("_cc_messages", "cc_messages", ["Inputs"], _cc_messages);
|
|
789
929
|
$def("_cc_watches", "viewof cc_watches", ["Inputs"], _cc_watches);
|
|
790
930
|
main.variable().define("cc_watches", ["Generators", "viewof cc_watches"], (G, v) => G.input(v));
|
|
791
|
-
$def("
|
|
792
|
-
|
|
931
|
+
$def("_cc_module", "viewof cc_module", ["thisModule"], _cc_module);
|
|
932
|
+
main.variable().define("cc_module", ["Generators", "viewof cc_module"], (G, v) => G.input(v));
|
|
933
|
+
$def("_cc_ws", "cc_ws", ["cc_config","cc_notebook_id","cc_status","cc_messages","viewof cc_watches","summarizeJS","observe","realize","compile","createModule","deleteModule","lookupVariable","exportToHTML","cc_module","runtime","invalidation"], _cc_ws);
|
|
934
|
+
$def("_cc_change_forwarder", "cc_change_forwarder", ["cc_ws","history","invalidation"], _cc_change_forwarder);
|
|
793
935
|
|
|
794
936
|
// Imports
|
|
795
937
|
main.define("module @tomlarkworthy/module-map", async () => runtime.module((await import("/@tomlarkworthy/module-map.js?v=4")).default));
|
|
796
938
|
main.define("currentModules", ["module @tomlarkworthy/module-map", "@variable"], (_, v) => v.import("currentModules", _));
|
|
797
939
|
main.define("moduleMap", ["module @tomlarkworthy/module-map", "@variable"], (_, v) => v.import("moduleMap", _));
|
|
940
|
+
main.define("module @tomlarkworthy/exporter-2", async () => runtime.module((await import("/@tomlarkworthy/exporter-2.js?v=4")).default));
|
|
941
|
+
main.define("exportToHTML", ["module @tomlarkworthy/exporter-2", "@variable"], (_, v) => v.import("exportToHTML", _));
|
|
942
|
+
main.define("module @tomlarkworthy/observablejs-toolchain", async () => runtime.module((await import("/@tomlarkworthy/observablejs-toolchain.js?v=4")).default));
|
|
943
|
+
main.define("compile", ["module @tomlarkworthy/observablejs-toolchain", "@variable"], (_, v) => v.import("compile", _));
|
|
944
|
+
main.define("module @tomlarkworthy/local-change-history", async () => runtime.module((await import("/@tomlarkworthy/local-change-history.js?v=4")).default));
|
|
945
|
+
main.define("viewof history", ["module @tomlarkworthy/local-change-history", "@variable"], (_, v) => v.import("viewof history", _));
|
|
946
|
+
main.define("history", ["Generators", "viewof history"], (G, v) => G.input(v));
|
|
798
947
|
main.define("module @tomlarkworthy/runtime-sdk", async () => runtime.module((await import("/@tomlarkworthy/runtime-sdk.js?v=4")).default));
|
|
799
948
|
main.define("viewof runtime_variables", ["module @tomlarkworthy/runtime-sdk", "@variable"], (_, v) => v.import("viewof runtime_variables", _));
|
|
800
949
|
main.define("runtime_variables", ["module @tomlarkworthy/runtime-sdk", "@variable"], (_, v) => v.import("runtime_variables", _));
|
|
801
950
|
main.define("observe", ["module @tomlarkworthy/runtime-sdk", "@variable"], (_, v) => v.import("observe", _));
|
|
802
951
|
main.define("realize", ["module @tomlarkworthy/runtime-sdk", "@variable"], (_, v) => v.import("realize", _));
|
|
952
|
+
main.define("createModule", ["module @tomlarkworthy/runtime-sdk", "@variable"], (_, v) => v.import("createModule", _));
|
|
953
|
+
main.define("deleteModule", ["module @tomlarkworthy/runtime-sdk", "@variable"], (_, v) => v.import("deleteModule", _));
|
|
954
|
+
main.define("lookupVariable", ["module @tomlarkworthy/runtime-sdk", "@variable"], (_, v) => v.import("lookupVariable", _));
|
|
955
|
+
main.define("thisModule", ["module @tomlarkworthy/runtime-sdk", "@variable"], (_, v) => v.import("thisModule", _));
|
|
803
956
|
main.define("runtime", ["module @tomlarkworthy/runtime-sdk", "@variable"], (_, v) => v.import("runtime", _));
|
|
804
957
|
main.define("module d/57d79353bac56631@44", async () => runtime.module((await import("/d/57d79353bac56631@44.js?v=4")).default));
|
|
805
958
|
main.define("hash", ["module d/57d79353bac56631@44", "@variable"], (_, v) => v.import("hash", _));
|