@veolab/discoverylab 1.6.3 → 1.6.6

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 (29) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/assets/applab-bundle-icon.png +0 -0
  4. package/assets/icons/icons8-claude-150.png +0 -0
  5. package/assets/icons/icons8-claude-500.png +0 -0
  6. package/dist/{chunk-HFN6BTVO.js → chunk-JAA53ES7.js} +1 -1
  7. package/dist/{chunk-XKX6NBHF.js → chunk-TWRWARU4.js} +52 -2
  8. package/dist/{chunk-IVX2OSOJ.js → chunk-XDUFCPOC.js} +198 -69
  9. package/dist/{chunk-5AISGCS4.js → chunk-YFF3M76J.js} +162 -33
  10. package/dist/cli.js +29 -18
  11. package/dist/export/infographic-template.html +392 -128
  12. package/dist/index.d.ts +30 -6
  13. package/dist/index.html +75 -21
  14. package/dist/index.js +4 -4
  15. package/dist/{infographic-GQAHEOAA.js → infographic-OSDIJM5M.js} +119 -17
  16. package/dist/mcpb/node_modules/@anthropic-ai/sdk/src/lib/.keep +4 -0
  17. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/better_sqlite3.node.d +1 -0
  18. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/better_sqlite3/src/better_sqlite3.o.d +133 -0
  19. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/deps/locate_sqlite3.stamp.d +1 -0
  20. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/sqlite3/gen/sqlite3/sqlite3.o.d +4 -0
  21. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/test_extension/deps/test_extension.o.d +7 -0
  22. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/sqlite3.a.d +1 -0
  23. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/test_extension.node.d +1 -0
  24. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/ba23eeee118cd63e16015df367567cb043fed872.intermediate.d +1 -0
  25. package/dist/{server-W3JQ5RG7.js → server-GXNAKM4H.js} +1 -1
  26. package/dist/{server-OVOACIOJ.js → server-WN6DCCUA.js} +1 -1
  27. package/dist/{setup-F7MGEFIM.js → setup-SMN7FJNZ.js} +2 -2
  28. package/dist/{tools-VYFNRUS4.js → tools-6BTUMR3G.js} +3 -3
  29. package/package.json +8 -2
@@ -6,128 +6,379 @@
6
6
  <title>__TITLE__</title>
7
7
  <style>
8
8
  * { margin: 0; padding: 0; box-sizing: border-box; }
9
- :root { --accent: #6366f1; --bg: #0f0f17; --bg2: #16161f; --bg3: #1e1e2a; --text: #e4e4e7; --muted: #71717a; --ok: #22c55e; --warn: #eab308; }
10
- body { background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; min-height: 100vh; overflow-x: hidden; }
11
-
12
- /* Header */
13
- .header { display: flex; align-items: center; justify-content: space-between; padding: 14px 24px; border-bottom: 1px solid rgba(255,255,255,0.05); }
14
- .header-left { display: flex; align-items: center; gap: 12px; }
15
- .header-title { font-size: 16px; font-weight: 700; }
16
- .header-platform { font-size: 10px; padding: 2px 8px; background: rgba(99,102,241,0.12); color: var(--accent); border-radius: 10px; }
17
- .header-date { font-size: 11px; color: var(--muted); }
18
-
19
- /* Step chips */
20
- .chips { display: flex; gap: 4px; padding: 10px 24px; overflow-x: auto; scrollbar-width: none; }
9
+ :root {
10
+ --accent: #6366f1;
11
+ --bg: #0f0f17;
12
+ --bg2: #16161f;
13
+ --bg3: #1e1e2a;
14
+ --text: #e4e4e7;
15
+ --muted: #71717a;
16
+ --ok: #22c55e;
17
+ --warn: #eab308;
18
+ --header-h: 56px;
19
+ --chips-h: 48px;
20
+ --glass-bg: linear-gradient(180deg, rgba(22,22,31,0.82), rgba(22,22,31,0.62));
21
+ --glass-border: rgba(255,255,255,0.09);
22
+ }
23
+ body {
24
+ background: radial-gradient(circle at top, rgba(99,102,241,0.16), transparent 24%), var(--bg);
25
+ color: var(--text);
26
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
27
+ min-height: 100vh;
28
+ overflow-y: auto;
29
+ overflow-x: hidden;
30
+ }
31
+ .viewer-shell {
32
+ display: grid;
33
+ min-height: 100vh;
34
+ grid-template-rows: auto auto 1fr auto;
35
+ }
36
+
37
+ /* ── Header ── */
38
+ .header {
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: space-between;
42
+ gap: 12px;
43
+ min-height: var(--header-h);
44
+ padding: 10px 22px;
45
+ border-bottom: 1px solid var(--glass-border);
46
+ position: sticky;
47
+ top: 0;
48
+ z-index: 30;
49
+ background: var(--glass-bg);
50
+ backdrop-filter: blur(20px) saturate(1.35);
51
+ -webkit-backdrop-filter: blur(20px) saturate(1.35);
52
+ }
53
+ .header-left, .header-right {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 10px;
57
+ flex-wrap: wrap;
58
+ }
59
+ .header-title { font-size: 15px; font-weight: 700; }
60
+ .header-platform {
61
+ font-size: 10px;
62
+ padding: 2px 8px;
63
+ background: rgba(99,102,241,0.12);
64
+ color: #aeb2ff;
65
+ border-radius: 10px;
66
+ }
67
+ .header-date, .header-step { font-size: 11px; color: var(--muted); }
68
+
69
+ /* ── Chips ── */
70
+ .chips {
71
+ display: flex;
72
+ align-items: center;
73
+ gap: 6px;
74
+ min-height: var(--chips-h);
75
+ padding: 6px 20px 8px;
76
+ overflow-x: auto;
77
+ scrollbar-width: none;
78
+ position: sticky;
79
+ top: var(--header-h);
80
+ z-index: 25;
81
+ background: var(--glass-bg);
82
+ backdrop-filter: blur(20px) saturate(1.35);
83
+ -webkit-backdrop-filter: blur(20px) saturate(1.35);
84
+ border-bottom: 1px solid rgba(255,255,255,0.04);
85
+ }
21
86
  .chips::-webkit-scrollbar { display: none; }
22
- .chip { font-size: 10px; padding: 4px 12px; border-radius: 16px; background: var(--bg2); border: 1px solid rgba(255,255,255,0.06); cursor: pointer; white-space: nowrap; transition: all 0.25s; color: var(--muted); font-weight: 500; }
23
- .chip.active { background: var(--accent); color: #fff; border-color: var(--accent); }
24
- .chip:hover:not(.active) { border-color: rgba(255,255,255,0.15); }
25
-
26
- /* Main layout */
27
- .main { display: grid; grid-template-columns: 1fr 300px; height: calc(100vh - 100px); overflow: hidden; }
28
- @media (max-width: 768px) { .main { grid-template-columns: 1fr; } .panel { border-left: none; border-top: 1px solid rgba(255,255,255,0.05); max-height: 200px; } }
29
-
30
- /* Viewer */
31
- .viewer { display: flex; align-items: center; justify-content: center; position: relative; padding: 20px; }
32
- .device-area { position: relative; display: flex; align-items: center; }
33
- .phone { width: 320px; background: #000; border-radius: 28px; overflow: hidden; box-shadow: 0 16px 48px rgba(0,0,0,0.5); border: 1.5px solid rgba(255,255,255,0.06); position: relative; }
34
- .phone img { width: 100%; display: block; transition: opacity 0.35s ease; }
35
- .phone img.out { opacity: 0; position: absolute; top: 0; left: 0; }
36
- .phone img.in { opacity: 1; position: relative; }
87
+ .chip {
88
+ white-space: nowrap;
89
+ font-size: 10px;
90
+ padding: 5px 12px;
91
+ border-radius: 999px;
92
+ background: var(--bg2);
93
+ border: 1px solid rgba(255,255,255,0.06);
94
+ color: var(--muted);
95
+ cursor: pointer;
96
+ transition: all 120ms ease;
97
+ font-weight: 500;
98
+ flex-shrink: 0;
99
+ }
100
+ .chip.active { background: var(--accent); color: #fff; border-color: transparent; }
37
101
 
38
- /* Side annotations (appear when paused) */
39
- .side-annots { position: absolute; top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: 8px; opacity: 0; transition: opacity 0.35s; pointer-events: none; width: 160px; }
102
+ /* ── Main Layout ── */
103
+ .main {
104
+ display: grid;
105
+ grid-template-columns: minmax(0, 1fr) 340px;
106
+ min-height: 0;
107
+ align-items: start;
108
+ }
109
+
110
+ /* ── Viewer (device area) ── */
111
+ .viewer {
112
+ display: flex;
113
+ align-items: flex-start;
114
+ justify-content: center;
115
+ position: relative;
116
+ padding: 24px 28px 200px;
117
+ min-height: calc(100vh - var(--header-h) - var(--chips-h));
118
+ }
119
+ .device-wrapper {
120
+ display: flex;
121
+ align-items: flex-start;
122
+ justify-content: center;
123
+ gap: 12px;
124
+ width: 100%;
125
+ max-width: 740px;
126
+ margin: 0 auto;
127
+ }
128
+
129
+ /* ── Side Annotations ── */
130
+ .side-annots {
131
+ display: flex;
132
+ flex-direction: column;
133
+ gap: 8px;
134
+ width: 160px;
135
+ flex-shrink: 0;
136
+ opacity: 0;
137
+ transition: opacity 200ms ease;
138
+ pointer-events: none;
139
+ padding-top: 20px;
140
+ }
40
141
  .side-annots.show { opacity: 1; pointer-events: auto; }
41
- .side-annots.left { right: calc(100% + 16px); }
42
- .side-annots.right { left: calc(100% + 16px); }
43
- .side-annot { padding: 8px 10px; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); border-radius: 8px; position: relative; }
44
- .side-annot::before { content: ''; position: absolute; top: 50%; width: 12px; height: 1px; background: rgba(255,255,255,0.15); }
45
- .side-annots.left .side-annot::before { right: -13px; }
46
- .side-annots.right .side-annot::before { left: -13px; }
142
+ .side-annot {
143
+ padding: 8px 10px;
144
+ background: rgba(255,255,255,0.04);
145
+ border: 1px solid rgba(255,255,255,0.08);
146
+ border-radius: 10px;
147
+ }
47
148
  .side-annot-title { font-size: 10px; font-weight: 600; color: var(--text); display: flex; align-items: center; gap: 5px; }
48
149
  .side-annot-dot { width: 5px; height: 5px; border-radius: 50%; flex-shrink: 0; }
49
- .side-annot-desc { font-size: 9px; color: var(--muted); margin-top: 2px; line-height: 1.4; }
50
-
51
- /* Controls */
52
- .controls { position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 10px; background: rgba(0,0,0,0.6); backdrop-filter: blur(8px); padding: 6px 14px; border-radius: 20px; }
53
- .play-btn { width: 28px; height: 28px; border-radius: 50%; background: transparent; border: 1.5px solid rgba(255,255,255,0.3); color: var(--text); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; }
54
- .play-btn:hover { border-color: var(--accent); color: var(--accent); }
55
- .dots { display: flex; gap: 5px; }
56
- .dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,0.15); cursor: pointer; transition: all 0.25s; }
57
- .dot.active { background: var(--accent); width: 16px; border-radius: 3px; }
58
- .status-text { font-size: 9px; color: rgba(255,255,255,0.35); }
59
-
60
- /* Panel */
61
- .panel { padding: 16px; border-left: 1px solid rgba(255,255,255,0.05); overflow-y: auto; background: var(--bg2); display: flex; flex-direction: column; gap: 16px; }
62
- .panel-section { }
63
- .panel-label { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; color: var(--muted); margin-bottom: 6px; }
64
- .step-number { font-size: 11px; color: var(--accent); font-weight: 700; margin-bottom: 2px; }
65
- .step-title { font-size: 14px; font-weight: 600; line-height: 1.3; }
66
- .step-desc { font-size: 12px; color: var(--muted); line-height: 1.5; margin-top: 4px; }
67
-
68
- /* Annotation cards in panel */
69
- .annot-cards { display: flex; flex-direction: column; gap: 6px; }
70
- .annot-card { padding: 8px 10px; background: var(--bg3); border-radius: 8px; border-left: 3px solid var(--accent); cursor: pointer; transition: all 0.2s; }
150
+ .side-annot-desc { font-size: 9px; line-height: 1.4; color: var(--muted); margin-top: 2px; }
151
+
152
+ /* ── Device frame ── */
153
+ .device-center {
154
+ display: flex;
155
+ flex-direction: column;
156
+ align-items: center;
157
+ gap: 0;
158
+ flex: 1;
159
+ min-width: 0;
160
+ max-width: 380px;
161
+ }
162
+ .phone {
163
+ width: 100%;
164
+ max-width: 340px;
165
+ background: #000;
166
+ border-radius: 32px;
167
+ overflow: hidden;
168
+ box-shadow: 0 20px 60px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.06);
169
+ position: relative;
170
+ }
171
+ .phone img {
172
+ width: 100%;
173
+ display: block;
174
+ aspect-ratio: 9 / 19.5;
175
+ object-fit: cover;
176
+ object-position: top center;
177
+ background: #000;
178
+ transition: opacity 0.35s ease;
179
+ }
180
+ .phone img.out { opacity: 0; position: absolute; top: 0; left: 0; }
181
+ .phone img.in { opacity: 1; position: relative; }
182
+
183
+ /* ── Floating Play/Pause (Liquid Glass) ── */
184
+ .fab-controls {
185
+ position: fixed;
186
+ left: 16px;
187
+ bottom: 220px;
188
+ z-index: 40;
189
+ display: flex;
190
+ flex-direction: column;
191
+ align-items: center;
192
+ gap: 8px;
193
+ padding: 10px 8px;
194
+ border-radius: 20px;
195
+ background: linear-gradient(180deg, rgba(255,255,255,0.12), rgba(255,255,255,0.04));
196
+ border: 1px solid rgba(255,255,255,0.14);
197
+ backdrop-filter: blur(24px) saturate(1.4);
198
+ -webkit-backdrop-filter: blur(24px) saturate(1.4);
199
+ box-shadow: 0 8px 32px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.1);
200
+ }
201
+ .fab-btn {
202
+ width: 36px;
203
+ height: 36px;
204
+ border-radius: 50%;
205
+ background: rgba(99,102,241,0.2);
206
+ border: 1.5px solid rgba(255,255,255,0.18);
207
+ color: var(--text);
208
+ cursor: pointer;
209
+ display: flex;
210
+ align-items: center;
211
+ justify-content: center;
212
+ transition: all 0.2s;
213
+ }
214
+ .fab-btn:hover { background: var(--accent); border-color: var(--accent); }
215
+ .fab-dots {
216
+ display: flex;
217
+ flex-direction: column;
218
+ gap: 4px;
219
+ align-items: center;
220
+ }
221
+ .fab-dot {
222
+ width: 6px;
223
+ height: 6px;
224
+ border-radius: 50%;
225
+ background: rgba(255,255,255,0.18);
226
+ cursor: pointer;
227
+ transition: all 0.25s;
228
+ }
229
+ .fab-dot.active { background: var(--accent); height: 14px; border-radius: 3px; }
230
+ .fab-status {
231
+ font-size: 8px;
232
+ color: rgba(255,255,255,0.45);
233
+ text-transform: uppercase;
234
+ letter-spacing: 0.06em;
235
+ writing-mode: vertical-lr;
236
+ text-orientation: mixed;
237
+ transform: rotate(180deg);
238
+ }
239
+
240
+ /* ── Right Panel ── */
241
+ .panel {
242
+ padding: 18px 16px 200px;
243
+ border-left: 1px solid rgba(255,255,255,0.05);
244
+ overflow-y: auto;
245
+ background: var(--bg2);
246
+ display: flex;
247
+ flex-direction: column;
248
+ gap: 16px;
249
+ min-height: calc(100vh - var(--header-h) - var(--chips-h));
250
+ position: sticky;
251
+ top: calc(var(--header-h) + var(--chips-h));
252
+ max-height: calc(100vh - var(--header-h) - var(--chips-h));
253
+ }
254
+ .panel-section { display: grid; gap: 6px; }
255
+ .panel-label {
256
+ font-size: 9px;
257
+ font-weight: 700;
258
+ text-transform: uppercase;
259
+ letter-spacing: 0.08em;
260
+ color: var(--muted);
261
+ }
262
+ .project-summary, .step-desc { font-size: 12px; color: var(--muted); line-height: 1.55; }
263
+ .step-number { font-size: 11px; color: #aeb2ff; font-weight: 700; }
264
+ .step-title { font-size: 15px; font-weight: 700; line-height: 1.25; }
265
+ .annot-cards { display: flex; flex-direction: column; gap: 8px; }
266
+ .annot-card {
267
+ padding: 8px 10px;
268
+ background: var(--bg3);
269
+ border-radius: 10px;
270
+ border-left: 3px solid var(--accent);
271
+ transition: all 0.2s;
272
+ }
71
273
  .annot-card:hover { background: rgba(99,102,241,0.08); }
72
274
  .annot-card-title { font-size: 11px; font-weight: 600; display: flex; align-items: center; gap: 6px; }
73
275
  .annot-card-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
74
276
  .annot-card-desc { font-size: 10px; color: var(--muted); margin-top: 2px; line-height: 1.4; }
75
-
76
- .baseline-badge { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; padding: 3px 10px; border-radius: 12px; background: rgba(113,113,122,0.1); color: var(--muted); }
77
- .baseline-badge.ok { background: rgba(34,197,94,0.1); color: var(--ok); }
78
- .baseline-badge.warn { background: rgba(234,179,8,0.1); color: var(--warn); }
79
- .bl-dot { width: 5px; height: 5px; border-radius: 50%; }
80
-
277
+ .baseline-badge {
278
+ display: inline-flex;
279
+ align-items: center;
280
+ gap: 6px;
281
+ width: fit-content;
282
+ font-size: 11px;
283
+ padding: 4px 10px;
284
+ border-radius: 999px;
285
+ background: rgba(113,113,122,0.1);
286
+ color: var(--muted);
287
+ }
288
+ .baseline-badge.ok { background: rgba(34,197,94,0.12); color: var(--ok); }
289
+ .baseline-badge.warn { background: rgba(234,179,8,0.12); color: var(--warn); }
290
+ .bl-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
81
291
  .footer { text-align: center; padding: 6px; font-size: 8px; color: rgba(255,255,255,0.12); }
82
292
 
83
- /* Transitions */
84
- @keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
85
- .fade-in { animation: fadeIn 0.3s ease forwards; }
293
+ /* ── Responsive ── */
294
+ @media (max-width: 980px) {
295
+ .main { grid-template-columns: 1fr; }
296
+ .panel {
297
+ border-left: none;
298
+ border-top: 1px solid rgba(255,255,255,0.05);
299
+ position: static;
300
+ max-height: none;
301
+ min-height: auto;
302
+ padding-bottom: 200px;
303
+ }
304
+ .viewer { padding: 20px 16px 40px; }
305
+ .side-annots { width: 130px; }
306
+ .fab-controls { left: 12px; bottom: 200px; }
307
+ }
308
+ @media (max-width: 720px) {
309
+ .viewer { padding: 16px 12px 40px; }
310
+ .phone { max-width: 280px; border-radius: 26px; }
311
+ .side-annots { display: none; }
312
+ .chips { top: var(--header-h); }
313
+ .panel { min-height: auto; padding-bottom: 200px; }
314
+ .device-wrapper { max-width: 320px; }
315
+ .fab-controls { left: 8px; bottom: 180px; padding: 8px 6px; border-radius: 16px; }
316
+ .fab-btn { width: 32px; height: 32px; }
317
+ }
318
+ @media (max-width: 480px) {
319
+ .header { padding: 8px 14px; }
320
+ .header-title { font-size: 13px; }
321
+ .chips { padding: 6px 14px; }
322
+ .phone { max-width: 240px; border-radius: 22px; }
323
+ }
86
324
  </style>
87
325
  </head>
88
326
  <body>
89
- <div class="header">
90
- <div class="header-left">
91
- <div class="header-title" id="title"></div>
92
- <div class="header-platform" id="platform"></div>
93
- </div>
94
- <div class="header-date" id="date"></div>
95
- </div>
96
- <div class="chips" id="chips"></div>
97
- <div class="main">
98
- <div class="viewer">
99
- <div class="device-area" id="deviceArea" style="position: relative;">
100
- <div class="side-annots left" id="annotsLeft"></div>
101
- <div class="phone" id="phone"></div>
102
- <div class="side-annots right" id="annotsRight"></div>
327
+ <div class="viewer-shell">
328
+ <div class="header">
329
+ <div class="header-left">
330
+ <div class="header-title" id="title"></div>
331
+ <div class="header-platform" id="platform"></div>
103
332
  </div>
104
- <div class="controls">
105
- <button class="play-btn" id="playBtn">
106
- <svg id="playIco" width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><polygon points="6 3 20 12 6 21"/></svg>
107
- <svg id="pauseIco" width="12" height="12" viewBox="0 0 24 24" fill="currentColor" style="display:none"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>
108
- </button>
109
- <div class="dots" id="dots"></div>
110
- <div class="status-text" id="statusText">playing</div>
333
+ <div class="header-right">
334
+ <div class="header-step" id="headerStep"></div>
335
+ <div class="header-date" id="date"></div>
111
336
  </div>
112
337
  </div>
113
- <div class="panel">
114
- <div class="panel-section">
115
- <div class="panel-label">Step</div>
116
- <div class="step-number" id="stepNum"></div>
117
- <div class="step-title" id="stepTitle"></div>
118
- <div class="step-desc" id="stepDesc"></div>
119
- </div>
120
- <div class="panel-section">
121
- <div class="panel-label">Annotations</div>
122
- <div class="annot-cards" id="annotCards"></div>
338
+ <div class="chips" id="chips"></div>
339
+ <div class="main">
340
+ <div class="viewer">
341
+ <div class="device-wrapper">
342
+ <div class="side-annots left" id="annotsLeft"></div>
343
+ <div class="device-center">
344
+ <div class="phone" id="phone"></div>
345
+ </div>
346
+ <div class="side-annots right" id="annotsRight"></div>
347
+ </div>
123
348
  </div>
124
- <div class="panel-section">
125
- <div class="panel-label">Baseline</div>
126
- <div class="baseline-badge" id="blBadge"><div class="bl-dot"></div><span></span></div>
349
+ <div class="panel">
350
+ <div class="panel-section">
351
+ <div class="panel-label">Project</div>
352
+ <div class="project-summary" id="projectSummary"></div>
353
+ </div>
354
+ <div class="panel-section">
355
+ <div class="panel-label">Step</div>
356
+ <div class="step-number" id="stepNum"></div>
357
+ <div class="step-title" id="stepTitle"></div>
358
+ <div class="step-desc" id="stepDesc"></div>
359
+ </div>
360
+ <div class="panel-section">
361
+ <div class="panel-label">Annotations</div>
362
+ <div class="annot-cards" id="annotCards"></div>
363
+ </div>
364
+ <div class="panel-section">
365
+ <div class="panel-label">Baseline</div>
366
+ <div class="baseline-badge" id="blBadge"><div class="bl-dot"></div><span></span></div>
367
+ </div>
127
368
  </div>
128
369
  </div>
370
+ <div class="footer">Generated by DiscoveryLab</div>
371
+ </div>
372
+
373
+ <!-- Floating Liquid Glass Controls -->
374
+ <div class="fab-controls" id="fabControls">
375
+ <button class="fab-btn" id="playBtn" title="Play / Pause">
376
+ <svg id="playIco" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style="display:none"><polygon points="7 3 21 12 7 21"/></svg>
377
+ <svg id="pauseIco" width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>
378
+ </button>
379
+ <div class="fab-dots" id="dots"></div>
380
+ <div class="fab-status" id="statusText">playing</div>
129
381
  </div>
130
- <div class="footer">Generated by DiscoveryLab</div>
131
382
 
132
383
  <script>
133
384
  const D = window.FLOW_DATA || { name: 'Flow', frames: [] };
@@ -137,8 +388,8 @@ let cur = 0, playing = true, timer = null;
137
388
  document.getElementById('title').textContent = D.name || 'App Flow';
138
389
  document.getElementById('platform').textContent = D.platform || '';
139
390
  document.getElementById('date').textContent = D.recorded_at ? new Date(D.recorded_at).toLocaleDateString() : '';
391
+ document.getElementById('projectSummary').textContent = D.overview || 'Captured flow ready for review inside Claude Desktop.';
140
392
 
141
- // Create images
142
393
  const phone = document.getElementById('phone');
143
394
  F.forEach((f, i) => {
144
395
  const img = document.createElement('img');
@@ -148,57 +399,63 @@ F.forEach((f, i) => {
148
399
  phone.appendChild(img);
149
400
  });
150
401
 
151
- // Chips
152
402
  const chips = document.getElementById('chips');
153
403
  F.forEach((f, i) => {
154
404
  const c = document.createElement('div');
155
- c.className = `chip ${i===0?'active':''}`;
156
- c.textContent = `${i+1}. ${f.step_name}`;
405
+ c.className = 'chip' + (i === 0 ? ' active' : '');
406
+ c.textContent = (i + 1) + '. ' + f.step_name;
157
407
  c.onclick = () => { go(i); stop(); };
158
408
  chips.appendChild(c);
159
409
  });
160
410
 
161
- // Dots
162
411
  const dots = document.getElementById('dots');
163
412
  F.forEach((_, i) => {
164
413
  const d = document.createElement('div');
165
- d.className = `dot ${i===0?'active':''}`;
414
+ d.className = 'fab-dot' + (i === 0 ? ' active' : '');
166
415
  d.onclick = () => { go(i); stop(); };
167
416
  dots.appendChild(d);
168
417
  });
169
418
 
170
419
  function go(i) {
171
420
  cur = i;
172
- phone.querySelectorAll('img').forEach((img, j) => { img.className = j===i?'in':'out'; });
173
- chips.querySelectorAll('.chip').forEach((c, j) => c.classList.toggle('active', j===i));
174
- dots.querySelectorAll('.dot').forEach((d, j) => d.classList.toggle('active', j===i));
421
+ phone.querySelectorAll('img').forEach((img, j) => { img.className = j === i ? 'in' : 'out'; });
422
+ chips.querySelectorAll('.chip').forEach((c, j) => c.classList.toggle('active', j === i));
423
+ dots.querySelectorAll('.fab-dot').forEach((d, j) => d.classList.toggle('active', j === i));
424
+ // Auto-scroll active chip into view
425
+ const activeChip = chips.querySelector('.chip.active');
426
+ if (activeChip) activeChip.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
175
427
  updatePanel();
176
428
  updateAnnotations();
177
429
  }
178
430
 
179
431
  function updatePanel() {
180
432
  const f = F[cur]; if (!f) return;
181
- document.getElementById('stepNum').textContent = `${cur+1} of ${F.length}`;
433
+ document.getElementById('stepNum').textContent = (cur + 1) + ' of ' + F.length;
182
434
  document.getElementById('stepTitle').textContent = f.step_name;
183
435
  document.getElementById('stepDesc').textContent = f.description;
436
+ document.getElementById('headerStep').textContent = 'Step ' + (cur + 1) + ' / ' + F.length;
184
437
 
185
- // Annotation cards
186
438
  const cards = document.getElementById('annotCards');
187
439
  cards.innerHTML = '';
188
440
  (f.hotspots || []).forEach(h => {
189
441
  const card = document.createElement('div');
190
- card.className = 'annot-card fade-in';
442
+ card.className = 'annot-card';
191
443
  card.style.borderLeftColor = h.color;
192
- card.innerHTML = `<div class="annot-card-title"><div class="annot-card-dot" style="background:${h.color}"></div>${h.title}</div><div class="annot-card-desc">${h.description}</div>`;
444
+ card.innerHTML = '<div class="annot-card-title"><div class="annot-card-dot" style="background:' + h.color + '"></div>' + escHtml(h.title) + '</div><div class="annot-card-desc">' + escHtml(h.description) + '</div>';
193
445
  cards.appendChild(card);
194
446
  });
195
447
 
196
- // Baseline
197
448
  const bl = document.getElementById('blBadge');
198
449
  const s = f.baseline_status || 'not_validated';
199
- bl.className = `baseline-badge ${s==='ok'?'ok':s==='changed'?'warn':''}`;
200
- bl.querySelector('.bl-dot').style.background = s==='ok'?'var(--ok)':s==='changed'?'var(--warn)':'var(--muted)';
201
- bl.querySelector('span').textContent = s==='ok'?'Validated':s==='changed'?'Changed':'No baseline';
450
+ bl.className = 'baseline-badge ' + (s === 'ok' ? 'ok' : s === 'changed' ? 'warn' : '');
451
+ bl.querySelector('.bl-dot').style.background = s === 'ok' ? 'var(--ok)' : s === 'changed' ? 'var(--warn)' : 'var(--muted)';
452
+ bl.querySelector('span').textContent = s === 'ok' ? 'Validated' : s === 'changed' ? 'Changed' : 'No baseline';
453
+ }
454
+
455
+ function escHtml(s) {
456
+ const el = document.createElement('span');
457
+ el.textContent = s;
458
+ return el.innerHTML;
202
459
  }
203
460
 
204
461
  function updateAnnotations() {
@@ -213,16 +470,21 @@ function updateAnnotations() {
213
470
  left.classList.add('show'); right.classList.add('show');
214
471
 
215
472
  const f = F[cur]; if (!f?.hotspots?.length) return;
216
-
217
473
  f.hotspots.forEach((h, j) => {
218
474
  const container = j % 2 === 0 ? left : right;
219
475
  const el = document.createElement('div');
220
- el.className = 'side-annot fade-in';
221
- el.style.animationDelay = (j * 0.1) + 's';
222
- el.innerHTML = `
223
- <div class="side-annot-title"><div class="side-annot-dot" style="background:${h.color}"></div>${h.label}</div>
224
- ${h.description !== h.label ? `<div class="side-annot-desc">${h.description.slice(0, 60)}</div>` : ''}
225
- `;
476
+ el.className = 'side-annot';
477
+ const titleDiv = document.createElement('div');
478
+ titleDiv.className = 'side-annot-title';
479
+ titleDiv.innerHTML = '<div class="side-annot-dot" style="background:' + h.color + '"></div>';
480
+ titleDiv.appendChild(document.createTextNode(h.label));
481
+ el.appendChild(titleDiv);
482
+ if (h.description !== h.label) {
483
+ const descDiv = document.createElement('div');
484
+ descDiv.className = 'side-annot-desc';
485
+ descDiv.textContent = h.description.slice(0, 72);
486
+ el.appendChild(descDiv);
487
+ }
226
488
  container.appendChild(el);
227
489
  });
228
490
  }
@@ -234,11 +496,14 @@ function play() {
234
496
  document.getElementById('statusText').textContent = 'playing';
235
497
  document.getElementById('annotsLeft').classList.remove('show');
236
498
  document.getElementById('annotsRight').classList.remove('show');
237
- timer = setInterval(() => { cur = (cur+1)%F.length; go(cur); }, 2200);
499
+ clearInterval(timer);
500
+ if (F.length < 2) return;
501
+ timer = setInterval(() => { cur = (cur + 1) % F.length; go(cur); }, 2200);
238
502
  }
239
503
 
240
504
  function stop() {
241
- playing = false; clearInterval(timer);
505
+ playing = false;
506
+ clearInterval(timer);
242
507
  document.getElementById('playIco').style.display = '';
243
508
  document.getElementById('pauseIco').style.display = 'none';
244
509
  document.getElementById('statusText').textContent = 'paused';
@@ -246,7 +511,6 @@ function stop() {
246
511
  }
247
512
 
248
513
  document.getElementById('playBtn').onclick = () => playing ? stop() : play();
249
-
250
514
  updatePanel();
251
515
  play();
252
516
  </script>