@portel/photon 1.14.0 → 1.16.1

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 (125) hide show
  1. package/dist/auto-ui/beam/photon-management.d.ts +1 -1
  2. package/dist/auto-ui/beam/photon-management.d.ts.map +1 -1
  3. package/dist/auto-ui/beam/photon-management.js +5 -1
  4. package/dist/auto-ui/beam/photon-management.js.map +1 -1
  5. package/dist/auto-ui/beam/routes/api-config.d.ts.map +1 -1
  6. package/dist/auto-ui/beam/routes/api-config.js +31 -9
  7. package/dist/auto-ui/beam/routes/api-config.js.map +1 -1
  8. package/dist/auto-ui/beam/routes/api-marketplace.d.ts.map +1 -1
  9. package/dist/auto-ui/beam/routes/api-marketplace.js +3 -0
  10. package/dist/auto-ui/beam/routes/api-marketplace.js.map +1 -1
  11. package/dist/auto-ui/beam.d.ts.map +1 -1
  12. package/dist/auto-ui/beam.js +205 -56
  13. package/dist/auto-ui/beam.js.map +1 -1
  14. package/dist/auto-ui/bridge/index.d.ts.map +1 -1
  15. package/dist/auto-ui/bridge/index.js +578 -0
  16. package/dist/auto-ui/bridge/index.js.map +1 -1
  17. package/dist/auto-ui/bridge/renderers.d.ts.map +1 -1
  18. package/dist/auto-ui/bridge/renderers.js +7 -3
  19. package/dist/auto-ui/bridge/renderers.js.map +1 -1
  20. package/dist/auto-ui/bridge/types.d.ts +6 -0
  21. package/dist/auto-ui/bridge/types.d.ts.map +1 -1
  22. package/dist/auto-ui/frontend/pure-view.html +289 -0
  23. package/dist/auto-ui/photon-bridge.d.ts +11 -0
  24. package/dist/auto-ui/photon-bridge.d.ts.map +1 -1
  25. package/dist/auto-ui/photon-bridge.js +75 -1
  26. package/dist/auto-ui/photon-bridge.js.map +1 -1
  27. package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
  28. package/dist/auto-ui/streamable-http-transport.js +29 -3
  29. package/dist/auto-ui/streamable-http-transport.js.map +1 -1
  30. package/dist/beam-form.bundle.js +5707 -0
  31. package/dist/beam-form.bundle.js.map +7 -0
  32. package/dist/beam.bundle.js +2863 -895
  33. package/dist/beam.bundle.js.map +4 -4
  34. package/dist/claude-code-plugin.js +11 -3
  35. package/dist/claude-code-plugin.js.map +1 -1
  36. package/dist/cli/commands/build.js +1 -1
  37. package/dist/cli/commands/build.js.map +1 -1
  38. package/dist/cli/commands/doctor.d.ts.map +1 -1
  39. package/dist/cli/commands/doctor.js +7 -5
  40. package/dist/cli/commands/doctor.js.map +1 -1
  41. package/dist/cli/commands/info.d.ts.map +1 -1
  42. package/dist/cli/commands/info.js +18 -4
  43. package/dist/cli/commands/info.js.map +1 -1
  44. package/dist/cli/commands/mcp.js +2 -2
  45. package/dist/cli/commands/mcp.js.map +1 -1
  46. package/dist/cli/commands/update.d.ts.map +1 -1
  47. package/dist/cli/commands/update.js +6 -2
  48. package/dist/cli/commands/update.js.map +1 -1
  49. package/dist/cli-alias.d.ts.map +1 -1
  50. package/dist/cli-alias.js +3 -2
  51. package/dist/cli-alias.js.map +1 -1
  52. package/dist/daemon/client.d.ts +5 -0
  53. package/dist/daemon/client.d.ts.map +1 -1
  54. package/dist/daemon/client.js +50 -0
  55. package/dist/daemon/client.js.map +1 -1
  56. package/dist/daemon/manager.d.ts +15 -0
  57. package/dist/daemon/manager.d.ts.map +1 -1
  58. package/dist/daemon/manager.js +142 -11
  59. package/dist/daemon/manager.js.map +1 -1
  60. package/dist/daemon/worker-manager.js +1 -1
  61. package/dist/daemon/worker-manager.js.map +1 -1
  62. package/dist/deploy/cloudflare.d.ts.map +1 -1
  63. package/dist/deploy/cloudflare.js +12 -10
  64. package/dist/deploy/cloudflare.js.map +1 -1
  65. package/dist/loader.d.ts.map +1 -1
  66. package/dist/loader.js +37 -2
  67. package/dist/loader.js.map +1 -1
  68. package/dist/marketplace-manager.d.ts +9 -0
  69. package/dist/marketplace-manager.d.ts.map +1 -1
  70. package/dist/marketplace-manager.js +115 -42
  71. package/dist/marketplace-manager.js.map +1 -1
  72. package/dist/meta.d.ts +51 -0
  73. package/dist/meta.d.ts.map +1 -0
  74. package/dist/meta.js +320 -0
  75. package/dist/meta.js.map +1 -0
  76. package/dist/photon-cli-runner.d.ts.map +1 -1
  77. package/dist/photon-cli-runner.js +30 -5
  78. package/dist/photon-cli-runner.js.map +1 -1
  79. package/dist/photon-doc-extractor.d.ts +1 -0
  80. package/dist/photon-doc-extractor.d.ts.map +1 -1
  81. package/dist/photon-doc-extractor.js +33 -21
  82. package/dist/photon-doc-extractor.js.map +1 -1
  83. package/dist/photons/builder-compass.photon.d.ts +167 -0
  84. package/dist/photons/builder-compass.photon.d.ts.map +1 -0
  85. package/dist/photons/builder-compass.photon.js +816 -0
  86. package/dist/photons/builder-compass.photon.js.map +1 -0
  87. package/dist/photons/builder-compass.photon.ts +1129 -0
  88. package/dist/photons/docs/ui/docs.html +441 -0
  89. package/dist/photons/docs.photon.d.ts +237 -0
  90. package/dist/photons/docs.photon.d.ts.map +1 -0
  91. package/dist/photons/docs.photon.js +483 -0
  92. package/dist/photons/docs.photon.js.map +1 -0
  93. package/dist/photons/docs.photon.ts +536 -0
  94. package/dist/photons/maker.photon.d.ts.map +1 -1
  95. package/dist/photons/maker.photon.js +19 -2
  96. package/dist/photons/maker.photon.js.map +1 -1
  97. package/dist/photons/maker.photon.ts +18 -2
  98. package/dist/photons/slides.photon.d.ts +212 -0
  99. package/dist/photons/slides.photon.d.ts.map +1 -0
  100. package/dist/photons/slides.photon.js +355 -0
  101. package/dist/photons/slides.photon.js.map +1 -0
  102. package/dist/photons/slides.photon.ts +370 -0
  103. package/dist/photons/spreadsheet/ui/spreadsheet.html +779 -0
  104. package/dist/photons/spreadsheet.photon.d.ts +554 -0
  105. package/dist/photons/spreadsheet.photon.d.ts.map +1 -0
  106. package/dist/photons/spreadsheet.photon.js +1050 -0
  107. package/dist/photons/spreadsheet.photon.js.map +1 -0
  108. package/dist/photons/spreadsheet.photon.ts +1239 -0
  109. package/dist/photons/ui/builder-compass.html +1199 -0
  110. package/dist/photons/ui/builder-compass.photon.html +380 -0
  111. package/dist/server.d.ts.map +1 -1
  112. package/dist/server.js +33 -59
  113. package/dist/server.js.map +1 -1
  114. package/dist/shared/error-handler.d.ts +8 -0
  115. package/dist/shared/error-handler.d.ts.map +1 -1
  116. package/dist/shared/error-handler.js +50 -0
  117. package/dist/shared/error-handler.js.map +1 -1
  118. package/dist/shared-utils.d.ts +16 -2
  119. package/dist/shared-utils.d.ts.map +1 -1
  120. package/dist/shared-utils.js +37 -3
  121. package/dist/shared-utils.js.map +1 -1
  122. package/dist/template-manager.d.ts.map +1 -1
  123. package/dist/template-manager.js +2 -1
  124. package/dist/template-manager.js.map +1 -1
  125. package/package.json +8 -3
@@ -0,0 +1,779 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="mcp:ui-size" content="minWidth=800;minHeight=400;maxHeight=900">
7
+ <title>Spreadsheet</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ /* Default: dark theme */
16
+ :root {
17
+ --primary: #2ea043;
18
+ --primary-hover: #3fb950;
19
+ --bg-primary: #0d1117;
20
+ --bg-secondary: #161b22;
21
+ --bg-tertiary: #21262d;
22
+ --border: #30363d;
23
+ --border-dark: #484f58;
24
+ --text-primary: #c9d1d9;
25
+ --text-secondary: #8b949e;
26
+ --text-muted: #6e7681;
27
+ --accent: #58a6ff;
28
+ --error: #f85149;
29
+ --formula-bar-bg: #161b22;
30
+ --header-bg: #161b22;
31
+ --grid-line: #30363d;
32
+ --selection: #58a6ff;
33
+ --selection-bg: rgba(88, 166, 255, 0.15);
34
+ --cell-active: #0d1117;
35
+ --cell-empty: rgba(255, 255, 255, 0.03);
36
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
37
+ --shadow-md: 0 3px 6px rgba(0, 0, 0, 0.4);
38
+ }
39
+
40
+ /* Light theme override via .light class on :root */
41
+ :root.light {
42
+ --primary: #217346;
43
+ --primary-hover: #1e623c;
44
+ --bg-primary: #ffffff;
45
+ --bg-secondary: #f8f9fa;
46
+ --bg-tertiary: #f3f2f1;
47
+ --border: #e1e4e8;
48
+ --border-dark: #d0d7de;
49
+ --text-primary: #24292f;
50
+ --text-secondary: #57606a;
51
+ --text-muted: #8c959f;
52
+ --accent: #0969da;
53
+ --error: #cf222e;
54
+ --formula-bar-bg: #f6f8fa;
55
+ --header-bg: #f6f8fa;
56
+ --grid-line: #d0d7de;
57
+ --selection: #0969da;
58
+ --selection-bg: rgba(9, 105, 218, 0.1);
59
+ --cell-active: #ffffff;
60
+ --cell-empty: rgba(0, 0, 0, 0.03);
61
+ --shadow-sm: 0 1px 2px rgba(31, 35, 40, 0.04);
62
+ --shadow-md: 0 3px 6px rgba(31, 35, 40, 0.08);
63
+ }
64
+
65
+ /* Slim scrollbars */
66
+ ::-webkit-scrollbar {
67
+ width: 6px;
68
+ height: 6px;
69
+ }
70
+ ::-webkit-scrollbar-track {
71
+ background: transparent;
72
+ }
73
+ ::-webkit-scrollbar-thumb {
74
+ background: var(--border);
75
+ border-radius: 3px;
76
+ }
77
+ ::-webkit-scrollbar-thumb:hover {
78
+ background: var(--border-dark);
79
+ }
80
+
81
+ body {
82
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
83
+ background: var(--bg-primary);
84
+ color: var(--text-primary);
85
+ height: 100vh;
86
+ overflow: hidden;
87
+ display: flex;
88
+ flex-direction: column;
89
+ transition: background 0.2s, color 0.2s;
90
+ }
91
+
92
+ /* Toolbar */
93
+ .toolbar {
94
+ background: var(--bg-primary);
95
+ border-bottom: 1px solid var(--border);
96
+ padding: 8px 16px;
97
+ display: flex;
98
+ align-items: center;
99
+ gap: 12px;
100
+ box-shadow: var(--shadow-sm);
101
+ z-index: 100;
102
+ }
103
+
104
+ .toolbar-section {
105
+ display: flex;
106
+ align-items: center;
107
+ gap: 6px;
108
+ padding-right: 12px;
109
+ border-right: 1px solid var(--border);
110
+ }
111
+
112
+ .toolbar-section:last-child {
113
+ border-right: none;
114
+ padding-right: 0;
115
+ }
116
+
117
+ .btn {
118
+ background: var(--bg-secondary);
119
+ border: 1px solid var(--border);
120
+ border-radius: 6px;
121
+ padding: 6px 12px;
122
+ font-size: 13px;
123
+ cursor: pointer;
124
+ transition: all 0.15s;
125
+ color: var(--text-primary);
126
+ font-weight: 500;
127
+ display: flex;
128
+ align-items: center;
129
+ gap: 6px;
130
+ }
131
+
132
+ .btn:hover {
133
+ background: var(--bg-tertiary);
134
+ border-color: var(--border-dark);
135
+ }
136
+
137
+ .btn-primary {
138
+ background: var(--primary);
139
+ color: white;
140
+ border-color: var(--primary);
141
+ }
142
+
143
+ .btn-primary:hover {
144
+ background: var(--primary-hover);
145
+ border-color: var(--primary-hover);
146
+ }
147
+
148
+ .btn-icon {
149
+ padding: 6px 8px;
150
+ font-size: 14px;
151
+ }
152
+
153
+ .btn.active {
154
+ background: var(--selection-bg);
155
+ border-color: var(--selection);
156
+ color: var(--selection);
157
+ }
158
+
159
+ /* Tabs */
160
+ .tabs-container {
161
+ background: var(--bg-secondary);
162
+ border-bottom: 1px solid var(--border);
163
+ display: flex;
164
+ align-items: center;
165
+ padding: 0 12px;
166
+ overflow-x: auto;
167
+ gap: 2px;
168
+ height: 32px;
169
+ }
170
+
171
+ .tab {
172
+ padding: 6px 16px;
173
+ font-size: 12px;
174
+ color: var(--text-secondary);
175
+ cursor: pointer;
176
+ border: 1px solid transparent;
177
+ border-bottom: none;
178
+ border-radius: 6px 6px 0 0;
179
+ white-space: nowrap;
180
+ display: flex;
181
+ align-items: center;
182
+ gap: 6px;
183
+ transition: all 0.15s;
184
+ background: transparent;
185
+ }
186
+
187
+ .tab:hover {
188
+ background: var(--bg-tertiary);
189
+ color: var(--text-primary);
190
+ }
191
+
192
+ .tab.active {
193
+ background: var(--bg-primary);
194
+ color: var(--accent);
195
+ border-color: var(--border);
196
+ font-weight: 600;
197
+ position: relative;
198
+ }
199
+
200
+ .tab.active::after {
201
+ content: '';
202
+ position: absolute;
203
+ bottom: -1px;
204
+ left: 0;
205
+ right: 0;
206
+ height: 1px;
207
+ background: var(--bg-primary);
208
+ z-index: 2;
209
+ }
210
+
211
+ .tab-close {
212
+ opacity: 0;
213
+ font-size: 14px;
214
+ line-height: 1;
215
+ }
216
+
217
+ .tab:hover .tab-close {
218
+ opacity: 0.6;
219
+ }
220
+
221
+ .tab-close:hover {
222
+ opacity: 1;
223
+ color: var(--error);
224
+ }
225
+
226
+ .btn-new-tab {
227
+ padding: 4px 8px;
228
+ font-size: 16px;
229
+ color: var(--text-muted);
230
+ cursor: pointer;
231
+ border-radius: 4px;
232
+ }
233
+
234
+ .btn-new-tab:hover {
235
+ background: var(--bg-tertiary);
236
+ color: var(--text-primary);
237
+ }
238
+
239
+ /* Sidebar table list */
240
+ .table-list {
241
+ display: flex;
242
+ flex-direction: column;
243
+ gap: 2px;
244
+ }
245
+
246
+ .table-item {
247
+ padding: 6px 12px;
248
+ border-radius: 6px;
249
+ cursor: pointer;
250
+ font-size: 12px;
251
+ display: flex;
252
+ align-items: center;
253
+ justify-content: space-between;
254
+ color: var(--text-secondary);
255
+ }
256
+
257
+ .table-item:hover {
258
+ background: var(--bg-tertiary);
259
+ color: var(--text-primary);
260
+ }
261
+
262
+ .table-item.active {
263
+ background: var(--selection-bg);
264
+ color: var(--selection);
265
+ font-weight: 500;
266
+ }
267
+
268
+ .table-info {
269
+ font-size: 10px;
270
+ opacity: 0.5;
271
+ }
272
+
273
+ /* Formula Bar */
274
+ .formula-container {
275
+ background: var(--formula-bar-bg);
276
+ border-bottom: 1px solid var(--border);
277
+ padding: 8px 16px;
278
+ display: flex;
279
+ align-items: center;
280
+ gap: 12px;
281
+ }
282
+
283
+ .cell-reference {
284
+ background: var(--bg-primary);
285
+ border: 1px solid var(--border);
286
+ border-radius: 4px;
287
+ padding: 6px 10px;
288
+ font-size: 13px;
289
+ font-weight: 600;
290
+ color: var(--text-primary);
291
+ min-width: 80px;
292
+ text-align: center;
293
+ font-family: 'SF Mono', Monaco, monospace;
294
+ }
295
+
296
+ .formula-input-wrapper {
297
+ flex: 1;
298
+ display: flex;
299
+ align-items: center;
300
+ background: var(--bg-primary);
301
+ border: 1px solid var(--border);
302
+ border-radius: 4px;
303
+ padding: 0 12px;
304
+ transition: border-color 0.15s;
305
+ }
306
+
307
+ .formula-input-wrapper:focus-within {
308
+ border-color: var(--accent);
309
+ box-shadow: 0 0 0 3px var(--selection-bg);
310
+ }
311
+
312
+ #formulaInput {
313
+ flex: 1;
314
+ border: none;
315
+ outline: none;
316
+ padding: 6px 0;
317
+ font-size: 14px;
318
+ font-family: 'SF Mono', Monaco, monospace;
319
+ background: transparent;
320
+ color: var(--text-primary);
321
+ }
322
+
323
+ /* Main Content */
324
+ .main-content {
325
+ flex: 1;
326
+ display: flex;
327
+ overflow: hidden;
328
+ min-height: 0;
329
+ }
330
+
331
+ /* Sidebar for Views */
332
+ .sidebar {
333
+ width: 240px;
334
+ background: var(--bg-secondary);
335
+ border-right: 1px solid var(--border);
336
+ display: flex;
337
+ flex-direction: column;
338
+ transition: width 0.2s;
339
+ }
340
+
341
+ .sidebar.collapsed {
342
+ width: 0;
343
+ overflow: hidden;
344
+ }
345
+
346
+ .sidebar-header {
347
+ padding: 12px 16px;
348
+ border-bottom: 1px solid var(--border);
349
+ display: flex;
350
+ justify-content: space-between;
351
+ align-items: center;
352
+ }
353
+
354
+ .sidebar-title {
355
+ font-size: 13px;
356
+ font-weight: 600;
357
+ color: var(--text-primary);
358
+ }
359
+
360
+ .sidebar-section {
361
+ padding: 12px;
362
+ border-bottom: 1px solid var(--border);
363
+ }
364
+
365
+ .sidebar-section-title {
366
+ font-size: 11px;
367
+ font-weight: 600;
368
+ color: var(--text-secondary);
369
+ text-transform: uppercase;
370
+ letter-spacing: 0.5px;
371
+ margin-bottom: 8px;
372
+ }
373
+
374
+ /* Sheet */
375
+ .sheet-wrapper {
376
+ position: relative;
377
+ flex: 1;
378
+ display: flex;
379
+ flex-direction: column;
380
+ }
381
+
382
+ .sheet-container {
383
+ flex: 1;
384
+ overflow: auto;
385
+ position: relative;
386
+ background: var(--bg-secondary);
387
+ }
388
+
389
+ /* Grid */
390
+ .grid-wrapper {
391
+ position: relative;
392
+ min-width: fit-content;
393
+ min-height: fit-content;
394
+ background: var(--bg-primary);
395
+ }
396
+
397
+ table {
398
+ border-collapse: separate;
399
+ border-spacing: 0;
400
+ font-size: 13px;
401
+ table-layout: fixed;
402
+ }
403
+
404
+ th, td {
405
+ border-right: 1px solid var(--grid-line);
406
+ border-bottom: 1px solid var(--grid-line);
407
+ padding: 0;
408
+ position: relative;
409
+ }
410
+
411
+ th {
412
+ background: var(--header-bg);
413
+ font-weight: 600;
414
+ color: var(--text-secondary);
415
+ text-align: center;
416
+ position: sticky;
417
+ z-index: 10;
418
+ }
419
+
420
+ th.corner {
421
+ position: sticky;
422
+ top: 0;
423
+ left: 0;
424
+ z-index: 20;
425
+ width: 50px;
426
+ height: 25px;
427
+ background: var(--header-bg);
428
+ border-right: 1px solid var(--border-dark);
429
+ border-bottom: 1px solid var(--border-dark);
430
+ }
431
+
432
+ th.col-header {
433
+ top: 0;
434
+ height: 25px;
435
+ min-width: 100px;
436
+ border-bottom: 1px solid var(--border-dark);
437
+ }
438
+
439
+ th.row-header {
440
+ left: 0;
441
+ width: 50px;
442
+ height: 24px;
443
+ border-right: 1px solid var(--border-dark);
444
+ }
445
+
446
+ td {
447
+ background: var(--bg-primary);
448
+ height: 24px;
449
+ min-width: 100px;
450
+ max-width: 300px;
451
+ overflow: hidden;
452
+ text-overflow: ellipsis;
453
+ white-space: nowrap;
454
+ }
455
+
456
+ .cell-content {
457
+ padding: 0 6px;
458
+ height: 100%;
459
+ display: flex;
460
+ align-items: center;
461
+ overflow: hidden;
462
+ text-overflow: ellipsis;
463
+ color: var(--text-primary);
464
+ }
465
+
466
+ /* Status Bar */
467
+ .status-bar {
468
+ background: var(--bg-secondary);
469
+ border-top: 1px solid var(--border);
470
+ padding: 6px 16px;
471
+ font-size: 12px;
472
+ color: var(--text-secondary);
473
+ display: flex;
474
+ justify-content: space-between;
475
+ align-items: center;
476
+ }
477
+
478
+ .context-menu {
479
+ position: fixed;
480
+ background: var(--bg-primary);
481
+ border: 1px solid var(--border);
482
+ border-radius: 8px;
483
+ box-shadow: var(--shadow-md);
484
+ z-index: 300;
485
+ min-width: 180px;
486
+ padding: 4px 0;
487
+ display: none;
488
+ }
489
+
490
+ .context-menu.visible {
491
+ display: block;
492
+ }
493
+
494
+ .context-menu-item {
495
+ padding: 8px 16px;
496
+ cursor: pointer;
497
+ font-size: 13px;
498
+ color: var(--text-primary);
499
+ display: flex;
500
+ align-items: center;
501
+ gap: 8px;
502
+ }
503
+
504
+ .context-menu-item:hover {
505
+ background: var(--selection-bg);
506
+ }
507
+
508
+ .context-menu-sep {
509
+ height: 1px;
510
+ background: var(--border);
511
+ margin: 4px 0;
512
+ }
513
+
514
+ /* Sidebar toggle */
515
+ .sidebar-toggle {
516
+ position: absolute;
517
+ left: 0;
518
+ top: 50%;
519
+ transform: translateY(-50%);
520
+ background: var(--bg-primary);
521
+ border: 1px solid var(--border);
522
+ border-left: none;
523
+ border-radius: 0 4px 4px 0;
524
+ padding: 8px 4px;
525
+ cursor: pointer;
526
+ z-index: 50;
527
+ font-size: 12px;
528
+ color: var(--text-secondary);
529
+ }
530
+ </style>
531
+ </head>
532
+ <body>
533
+ <!-- Toolbar -->
534
+ <div class="toolbar">
535
+ <div class="toolbar-section">
536
+ <button class="btn btn-primary" onclick="spreadsheet.newFile()">
537
+ <span>+</span> New
538
+ </button>
539
+ <button class="btn" onclick="spreadsheet.openImportModal()">
540
+ Import CSV
541
+ </button>
542
+ <button class="btn" onclick="spreadsheet.exportCSV()">
543
+ Export CSV
544
+ </button>
545
+ </div>
546
+ <div class="toolbar-section">
547
+ <button class="btn btn-icon" onclick="spreadsheet.addRow()" title="Add Row">Add Row</button>
548
+ <button class="btn btn-icon" onclick="spreadsheet.addColumn()" title="Add Column">Add Col</button>
549
+ </div>
550
+ </div>
551
+
552
+ <!-- Formula Bar -->
553
+ <div class="formula-container">
554
+ <div class="cell-reference" id="cellRef">A1</div>
555
+ <div class="formula-input-wrapper">
556
+ <input type="text" id="formulaInput" placeholder="Enter formula (start with =) or value..." autocomplete="off">
557
+ </div>
558
+ </div>
559
+
560
+ <!-- Context Menu -->
561
+ <div class="context-menu" id="contextMenu">
562
+ <div class="context-menu-item" data-action="insert-row-above">Insert row above</div>
563
+ <div class="context-menu-item" data-action="insert-row-below">Insert row below</div>
564
+ <div class="context-menu-sep"></div>
565
+ <div class="context-menu-item" data-action="insert-col-left">Insert column left</div>
566
+ <div class="context-menu-item" data-action="insert-col-right">Insert column right</div>
567
+ <div class="context-menu-sep"></div>
568
+ <div class="context-menu-item" data-action="delete-row" style="color: var(--error)">Delete row</div>
569
+ <div class="context-menu-item" data-action="delete-col" style="color: var(--error)">Delete column</div>
570
+ <div class="context-menu-sep"></div>
571
+ <div class="context-menu-item" data-action="fill">Fill range...</div>
572
+ <div class="context-menu-item" data-action="clear">Clear cell</div>
573
+ <div class="context-menu-sep"></div>
574
+ <div class="context-menu-item" data-action="rename">Rename column</div>
575
+ </div>
576
+
577
+ <!-- Main Content -->
578
+ <div class="main-content">
579
+ <!-- Sidebar -->
580
+ <div class="sidebar" id="sidebar">
581
+ <div class="sidebar-header">
582
+ <span class="sidebar-title">Workbook</span>
583
+ <button class="btn-icon" onclick="spreadsheet.toggleSidebar()">←</button>
584
+ </div>
585
+
586
+ <div class="sidebar-section">
587
+ <div class="sidebar-section-title">Sheets</div>
588
+ <div class="table-list" id="sheetList">
589
+ <div class="table-item active">
590
+ <span>📄 default.csv</span>
591
+ </div>
592
+ </div>
593
+ <button class="btn" style="width: 100%; margin-top: 8px; justify-content: center; font-size: 11px;" onclick="spreadsheet.newSheet()">
594
+ + New Sheet
595
+ </button>
596
+ </div>
597
+
598
+ <div class="sidebar-section">
599
+ <div class="sidebar-section-title">Views</div>
600
+ <div class="view-list" id="viewList">
601
+ <div class="view-item active" onclick="spreadsheet.switchView('grid')">
602
+ <span>⊞</span> Grid view
603
+ </div>
604
+ </div>
605
+ </div>
606
+ </div>
607
+
608
+ <!-- Sheet Wrapper -->
609
+ <div class="sheet-wrapper">
610
+ <button class="sidebar-toggle" onclick="spreadsheet.toggleSidebar()" id="sidebarToggle">▶</button>
611
+
612
+ <!-- Tabs Bar -->
613
+ <div class="tabs-container" id="tabBar">
614
+ <div class="tab active" onclick="spreadsheet.switchSheet('default')">
615
+ <span>default</span>
616
+ </div>
617
+ <div class="btn-new-tab" onclick="spreadsheet.newSheet()" title="New Sheet">+</div>
618
+ </div>
619
+
620
+ <div class="sheet-container" id="sheetContainer">
621
+ <div class="grid-wrapper" id="gridWrapper">
622
+ <table id="spreadsheet">
623
+ <thead id="tableHead"></thead>
624
+ <tbody id="tableBody"></tbody>
625
+ </table>
626
+ </div>
627
+ </div>
628
+ </div>
629
+ </div>
630
+
631
+ <!-- Status Bar -->
632
+ <div class="status-bar">
633
+ <div class="status-left">
634
+ <span class="status-item" id="cellCount">Ready</span>
635
+ <span class="status-item" id="recordCount"></span>
636
+ </div>
637
+ <div class="status-right">
638
+ <span class="status-item">Spreadsheet</span>
639
+ </div>
640
+ </div>
641
+
642
+ <script>
643
+ class SpreadsheetEngine {
644
+ constructor(rows = 20, cols = 10) {
645
+ this.data = [];
646
+ this.formulas = [];
647
+ this.headers = [];
648
+ this.rowCount = rows;
649
+ this.colCount = cols;
650
+ this.selectedCell = { row: 0, col: 0 };
651
+ this.currentTable = 'default';
652
+ this.tables = [];
653
+ this.openTabs = ['default'];
654
+ this.sidebarVisible = false;
655
+ this.init();
656
+ }
657
+
658
+ init() {
659
+ this.generateHeaders();
660
+ this.initializeData();
661
+ this.render();
662
+ this.renderTabs();
663
+ document.getElementById('sidebar')?.classList.add('collapsed');
664
+ }
665
+
666
+ generateHeaders() {
667
+ this.headers = [];
668
+ for (let i = 0; i < this.colCount; i++) {
669
+ let name = '';
670
+ let num = i + 1;
671
+ while (num > 0) {
672
+ num--;
673
+ name = String.fromCharCode(65 + (num % 26)) + name;
674
+ num = Math.floor(num / 26);
675
+ }
676
+ this.headers.push(name);
677
+ }
678
+ }
679
+
680
+ initializeData() {
681
+ this.data = Array.from({length: this.rowCount}, () => new Array(this.colCount).fill(''));
682
+ this.formulas = Array.from({length: this.rowCount}, () => new Array(this.colCount).fill(''));
683
+ }
684
+
685
+ toggleSidebar() {
686
+ this.sidebarVisible = !this.sidebarVisible;
687
+ document.getElementById('sidebar').classList.toggle('collapsed', !this.sidebarVisible);
688
+ document.getElementById('sidebarToggle').textContent = this.sidebarVisible ? '◀' : '▶';
689
+ }
690
+
691
+ newSheet() {
692
+ const name = prompt('Enter sheet name:');
693
+ if (!name) return;
694
+ this.switchSheet(name);
695
+ }
696
+
697
+ switchSheet(name) {
698
+ this.currentTable = name;
699
+ if (!this.openTabs.includes(name)) {
700
+ this.openTabs.push(name);
701
+ }
702
+ this.renderTabs();
703
+ if (window.loadDataFromPhoton) window.loadDataFromPhoton();
704
+ }
705
+
706
+ closeTab(name, e) {
707
+ if (e) e.stopPropagation();
708
+ if (this.openTabs.length <= 1) return;
709
+ this.openTabs = this.openTabs.filter(t => t !== name);
710
+ if (this.currentTable === name) {
711
+ this.switchSheet(this.openTabs[0]);
712
+ } else {
713
+ this.renderTabs();
714
+ }
715
+ }
716
+
717
+ renderTabs() {
718
+ const tabBar = document.getElementById('tabBar');
719
+ if (!tabBar) return;
720
+ let html = this.openTabs.map(tab => `
721
+ <div class="tab ${tab === this.currentTable ? 'active' : ''}" onclick="spreadsheet.switchSheet('${tab}')">
722
+ <span>${tab}</span>
723
+ <span class="tab-close" onclick="spreadsheet.closeTab('${tab}', event)">×</span>
724
+ </div>
725
+ `).join('');
726
+ html += '<div class="btn-new-tab" onclick="spreadsheet.newSheet()" title="New Sheet">+</div>';
727
+ tabBar.innerHTML = html;
728
+ }
729
+
730
+ renderSheetList(tables) {
731
+ this.tables = tables;
732
+ const list = document.getElementById('sheetList');
733
+ if (!list) return;
734
+ list.innerHTML = tables.map(table => `
735
+ <div class="table-item ${table.name === this.currentTable ? 'active' : ''}" onclick="spreadsheet.switchSheet('${table.name}')">
736
+ <span>📄 ${table.name}</span>
737
+ <span class="table-info">${(table.size / 1024).toFixed(1)} KB</span>
738
+ </div>
739
+ `).join('');
740
+ }
741
+
742
+ render() {
743
+ const thead = document.getElementById('tableHead');
744
+ const tbody = document.getElementById('tableBody');
745
+
746
+ thead.innerHTML = '<tr><th class="corner"></th>' +
747
+ this.headers.map(h => `<th class="col-header">${h}</th>`).join('') + '</tr>';
748
+
749
+ tbody.innerHTML = this.data.map((row, r) => `
750
+ <tr>
751
+ <th class="row-header">${r + 1}</th>
752
+ ${row.map((cell, c) => `
753
+ <td data-row="${r}" data-col="${c}">
754
+ <div class="cell-content">${cell}</div>
755
+ </td>
756
+ `).join('')}
757
+ </tr>
758
+ `).join('');
759
+ }
760
+
761
+ // Stubs for bridge overrides
762
+ addRow() {}
763
+ addColumn() {}
764
+ newFile() {}
765
+ openImportModal() {}
766
+ exportCSV() {}
767
+ }
768
+
769
+ const spreadsheet = new SpreadsheetEngine();
770
+
771
+ function setupPhotonBridge() {
772
+ if (!window.photon) { setTimeout(setupPhotonBridge, 100); return; }
773
+ if (window.initMCPBridge) window.initMCPBridge(spreadsheet);
774
+ }
775
+ document.addEventListener('DOMContentLoaded', setupPhotonBridge);
776
+ </script>
777
+ <script src="mcp-bridge.js"></script>
778
+ </body>
779
+ </html>