granola-toolkit 0.21.0 → 0.21.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.
- package/dist/cli.js +444 -431
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2132,436 +2132,8 @@ function resolveNoteFormat(value) {
|
|
|
2132
2132
|
}
|
|
2133
2133
|
}
|
|
2134
2134
|
//#endregion
|
|
2135
|
-
//#region src/
|
|
2136
|
-
|
|
2137
|
-
return `<!doctype html>
|
|
2138
|
-
<html lang="en">
|
|
2139
|
-
<head>
|
|
2140
|
-
<meta charset="utf-8" />
|
|
2141
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
2142
|
-
<title>Granola Toolkit</title>
|
|
2143
|
-
<style>
|
|
2144
|
-
:root {
|
|
2145
|
-
--bg: #f2ede2;
|
|
2146
|
-
--panel: rgba(255, 252, 247, 0.86);
|
|
2147
|
-
--panel-strong: #fffaf2;
|
|
2148
|
-
--line: rgba(36, 39, 44, 0.12);
|
|
2149
|
-
--ink: #1d242c;
|
|
2150
|
-
--muted: #5d6b77;
|
|
2151
|
-
--accent: #0d6a6d;
|
|
2152
|
-
--accent-soft: rgba(13, 106, 109, 0.12);
|
|
2153
|
-
--warm: #a34f2f;
|
|
2154
|
-
--ok: #246b4f;
|
|
2155
|
-
--error: #9d2c2c;
|
|
2156
|
-
--shadow: 0 24px 80px rgba(40, 32, 16, 0.12);
|
|
2157
|
-
--radius: 24px;
|
|
2158
|
-
--mono: "SF Mono", "IBM Plex Mono", "Cascadia Code", monospace;
|
|
2159
|
-
--serif: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia, serif;
|
|
2160
|
-
--sans: "Avenir Next", "Segoe UI", sans-serif;
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
* { box-sizing: border-box; }
|
|
2164
|
-
|
|
2165
|
-
body {
|
|
2166
|
-
margin: 0;
|
|
2167
|
-
min-height: 100vh;
|
|
2168
|
-
font-family: var(--sans);
|
|
2169
|
-
color: var(--ink);
|
|
2170
|
-
background:
|
|
2171
|
-
radial-gradient(circle at top left, rgba(163, 79, 47, 0.18), transparent 32%),
|
|
2172
|
-
radial-gradient(circle at right 12%, rgba(13, 106, 109, 0.16), transparent 28%),
|
|
2173
|
-
linear-gradient(180deg, #f8f2e8 0%, var(--bg) 100%);
|
|
2174
|
-
}
|
|
2175
|
-
|
|
2176
|
-
.shell {
|
|
2177
|
-
display: grid;
|
|
2178
|
-
grid-template-columns: 320px minmax(0, 1fr);
|
|
2179
|
-
gap: 18px;
|
|
2180
|
-
min-height: 100vh;
|
|
2181
|
-
padding: 24px;
|
|
2182
|
-
}
|
|
2183
|
-
|
|
2184
|
-
.pane {
|
|
2185
|
-
background: var(--panel);
|
|
2186
|
-
backdrop-filter: blur(18px);
|
|
2187
|
-
border: 1px solid var(--line);
|
|
2188
|
-
border-radius: var(--radius);
|
|
2189
|
-
box-shadow: var(--shadow);
|
|
2190
|
-
}
|
|
2191
|
-
|
|
2192
|
-
.sidebar {
|
|
2193
|
-
display: grid;
|
|
2194
|
-
grid-template-rows: auto auto 1fr;
|
|
2195
|
-
overflow: hidden;
|
|
2196
|
-
}
|
|
2197
|
-
|
|
2198
|
-
.hero, .toolbar, .detail-head {
|
|
2199
|
-
padding: 22px 24px;
|
|
2200
|
-
border-bottom: 1px solid var(--line);
|
|
2201
|
-
}
|
|
2202
|
-
|
|
2203
|
-
.hero h1 {
|
|
2204
|
-
margin: 0;
|
|
2205
|
-
font-family: var(--serif);
|
|
2206
|
-
font-size: clamp(2rem, 3vw, 2.8rem);
|
|
2207
|
-
font-weight: 600;
|
|
2208
|
-
letter-spacing: -0.04em;
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
.hero p, .toolbar p {
|
|
2212
|
-
margin: 8px 0 0;
|
|
2213
|
-
color: var(--muted);
|
|
2214
|
-
line-height: 1.5;
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
.search,
|
|
2218
|
-
.select,
|
|
2219
|
-
.field-input {
|
|
2220
|
-
width: 100%;
|
|
2221
|
-
margin-top: 16px;
|
|
2222
|
-
padding: 12px 14px;
|
|
2223
|
-
border: 1px solid var(--line);
|
|
2224
|
-
border-radius: 999px;
|
|
2225
|
-
background: rgba(255, 255, 255, 0.7);
|
|
2226
|
-
color: var(--ink);
|
|
2227
|
-
font: inherit;
|
|
2228
|
-
}
|
|
2229
|
-
|
|
2230
|
-
.field-row {
|
|
2231
|
-
display: grid;
|
|
2232
|
-
gap: 10px;
|
|
2233
|
-
margin-top: 12px;
|
|
2234
|
-
}
|
|
2235
|
-
|
|
2236
|
-
.field-row--inline {
|
|
2237
|
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
.field-label {
|
|
2241
|
-
display: block;
|
|
2242
|
-
margin-bottom: 6px;
|
|
2243
|
-
color: var(--muted);
|
|
2244
|
-
font-size: 0.78rem;
|
|
2245
|
-
font-weight: 700;
|
|
2246
|
-
letter-spacing: 0.08em;
|
|
2247
|
-
text-transform: uppercase;
|
|
2248
|
-
}
|
|
2249
|
-
|
|
2250
|
-
.meeting-list {
|
|
2251
|
-
padding: 14px;
|
|
2252
|
-
overflow: auto;
|
|
2253
|
-
}
|
|
2254
|
-
|
|
2255
|
-
.meeting-row {
|
|
2256
|
-
width: 100%;
|
|
2257
|
-
display: grid;
|
|
2258
|
-
gap: 4px;
|
|
2259
|
-
text-align: left;
|
|
2260
|
-
margin: 0 0 10px;
|
|
2261
|
-
padding: 14px 16px;
|
|
2262
|
-
border: 1px solid transparent;
|
|
2263
|
-
border-radius: 18px;
|
|
2264
|
-
background: rgba(255, 255, 255, 0.72);
|
|
2265
|
-
color: inherit;
|
|
2266
|
-
cursor: pointer;
|
|
2267
|
-
transition: transform 140ms ease, border-color 140ms ease, background 140ms ease;
|
|
2268
|
-
}
|
|
2269
|
-
|
|
2270
|
-
.meeting-row:hover,
|
|
2271
|
-
.meeting-row[data-selected="true"] {
|
|
2272
|
-
transform: translateY(-1px);
|
|
2273
|
-
border-color: rgba(13, 106, 109, 0.25);
|
|
2274
|
-
background: var(--panel-strong);
|
|
2275
|
-
}
|
|
2276
|
-
|
|
2277
|
-
.meeting-row__title {
|
|
2278
|
-
font-weight: 600;
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
|
-
.meeting-row__meta {
|
|
2282
|
-
color: var(--muted);
|
|
2283
|
-
font-size: 0.92rem;
|
|
2284
|
-
}
|
|
2285
|
-
|
|
2286
|
-
.meeting-empty {
|
|
2287
|
-
padding: 18px;
|
|
2288
|
-
color: var(--muted);
|
|
2289
|
-
}
|
|
2290
|
-
|
|
2291
|
-
.meeting-empty--error {
|
|
2292
|
-
color: var(--error);
|
|
2293
|
-
}
|
|
2294
|
-
|
|
2295
|
-
.detail {
|
|
2296
|
-
display: grid;
|
|
2297
|
-
grid-template-rows: auto auto 1fr;
|
|
2298
|
-
min-width: 0;
|
|
2299
|
-
}
|
|
2300
|
-
|
|
2301
|
-
.detail-head {
|
|
2302
|
-
display: flex;
|
|
2303
|
-
align-items: center;
|
|
2304
|
-
justify-content: space-between;
|
|
2305
|
-
gap: 18px;
|
|
2306
|
-
}
|
|
2307
|
-
|
|
2308
|
-
.detail-head h2 {
|
|
2309
|
-
margin: 0;
|
|
2310
|
-
font-family: var(--serif);
|
|
2311
|
-
font-size: clamp(1.8rem, 2.4vw, 2.4rem);
|
|
2312
|
-
font-weight: 600;
|
|
2313
|
-
}
|
|
2314
|
-
|
|
2315
|
-
.state-badge {
|
|
2316
|
-
padding: 10px 14px;
|
|
2317
|
-
border-radius: 999px;
|
|
2318
|
-
background: var(--accent-soft);
|
|
2319
|
-
color: var(--accent);
|
|
2320
|
-
font-size: 0.92rem;
|
|
2321
|
-
font-weight: 700;
|
|
2322
|
-
}
|
|
2323
|
-
|
|
2324
|
-
.state-badge[data-tone="busy"] { color: var(--warm); background: rgba(163, 79, 47, 0.12); }
|
|
2325
|
-
.state-badge[data-tone="error"] { color: var(--error); background: rgba(157, 44, 44, 0.12); }
|
|
2326
|
-
.state-badge[data-tone="ok"] { color: var(--ok); background: rgba(36, 107, 79, 0.12); }
|
|
2327
|
-
|
|
2328
|
-
.toolbar {
|
|
2329
|
-
display: flex;
|
|
2330
|
-
flex-wrap: wrap;
|
|
2331
|
-
align-items: center;
|
|
2332
|
-
justify-content: space-between;
|
|
2333
|
-
gap: 14px;
|
|
2334
|
-
}
|
|
2335
|
-
|
|
2336
|
-
.toolbar-actions {
|
|
2337
|
-
display: flex;
|
|
2338
|
-
flex-wrap: wrap;
|
|
2339
|
-
gap: 10px;
|
|
2340
|
-
}
|
|
2341
|
-
|
|
2342
|
-
.toolbar-form {
|
|
2343
|
-
display: grid;
|
|
2344
|
-
grid-template-columns: minmax(0, 1fr) auto;
|
|
2345
|
-
gap: 10px;
|
|
2346
|
-
width: min(440px, 100%);
|
|
2347
|
-
}
|
|
2348
|
-
|
|
2349
|
-
.workspace-tabs {
|
|
2350
|
-
display: flex;
|
|
2351
|
-
flex-wrap: wrap;
|
|
2352
|
-
align-items: center;
|
|
2353
|
-
gap: 10px;
|
|
2354
|
-
padding: 0 24px 18px;
|
|
2355
|
-
}
|
|
2356
|
-
|
|
2357
|
-
.workspace-tab {
|
|
2358
|
-
border: 1px solid var(--line);
|
|
2359
|
-
border-radius: 999px;
|
|
2360
|
-
padding: 10px 14px;
|
|
2361
|
-
background: rgba(255, 255, 255, 0.72);
|
|
2362
|
-
color: var(--muted);
|
|
2363
|
-
cursor: pointer;
|
|
2364
|
-
font: inherit;
|
|
2365
|
-
font-weight: 700;
|
|
2366
|
-
}
|
|
2367
|
-
|
|
2368
|
-
.workspace-tab[data-selected="true"] {
|
|
2369
|
-
background: var(--ink);
|
|
2370
|
-
color: white;
|
|
2371
|
-
border-color: var(--ink);
|
|
2372
|
-
}
|
|
2373
|
-
|
|
2374
|
-
.workspace-hint {
|
|
2375
|
-
color: var(--muted);
|
|
2376
|
-
font-size: 0.86rem;
|
|
2377
|
-
margin-left: auto;
|
|
2378
|
-
}
|
|
2379
|
-
|
|
2380
|
-
.button {
|
|
2381
|
-
border: 0;
|
|
2382
|
-
border-radius: 999px;
|
|
2383
|
-
padding: 12px 16px;
|
|
2384
|
-
font: inherit;
|
|
2385
|
-
font-weight: 700;
|
|
2386
|
-
cursor: pointer;
|
|
2387
|
-
}
|
|
2388
|
-
|
|
2389
|
-
.button--primary {
|
|
2390
|
-
background: var(--ink);
|
|
2391
|
-
color: white;
|
|
2392
|
-
}
|
|
2393
|
-
|
|
2394
|
-
.button--secondary {
|
|
2395
|
-
background: rgba(255, 255, 255, 0.72);
|
|
2396
|
-
color: var(--ink);
|
|
2397
|
-
border: 1px solid var(--line);
|
|
2398
|
-
}
|
|
2399
|
-
|
|
2400
|
-
.status-grid {
|
|
2401
|
-
display: grid;
|
|
2402
|
-
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
2403
|
-
gap: 14px;
|
|
2404
|
-
}
|
|
2405
|
-
|
|
2406
|
-
.status-label {
|
|
2407
|
-
display: block;
|
|
2408
|
-
margin-bottom: 6px;
|
|
2409
|
-
color: var(--muted);
|
|
2410
|
-
font-size: 0.78rem;
|
|
2411
|
-
letter-spacing: 0.08em;
|
|
2412
|
-
text-transform: uppercase;
|
|
2413
|
-
}
|
|
2414
|
-
|
|
2415
|
-
.detail-meta {
|
|
2416
|
-
display: flex;
|
|
2417
|
-
flex-wrap: wrap;
|
|
2418
|
-
gap: 10px;
|
|
2419
|
-
padding: 0 24px 18px;
|
|
2420
|
-
}
|
|
2421
|
-
|
|
2422
|
-
.detail-chip {
|
|
2423
|
-
padding: 10px 12px;
|
|
2424
|
-
border-radius: 999px;
|
|
2425
|
-
background: rgba(255, 255, 255, 0.72);
|
|
2426
|
-
border: 1px solid var(--line);
|
|
2427
|
-
color: var(--muted);
|
|
2428
|
-
font-size: 0.88rem;
|
|
2429
|
-
}
|
|
2430
|
-
|
|
2431
|
-
.detail-body {
|
|
2432
|
-
padding: 0 24px 24px;
|
|
2433
|
-
overflow: auto;
|
|
2434
|
-
}
|
|
2435
|
-
|
|
2436
|
-
.workspace-grid {
|
|
2437
|
-
display: grid;
|
|
2438
|
-
grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
|
|
2439
|
-
gap: 18px;
|
|
2440
|
-
}
|
|
2441
|
-
|
|
2442
|
-
.workspace-sidebar,
|
|
2443
|
-
.workspace-main {
|
|
2444
|
-
margin-bottom: 0;
|
|
2445
|
-
}
|
|
2446
|
-
|
|
2447
|
-
.detail-section {
|
|
2448
|
-
margin-bottom: 20px;
|
|
2449
|
-
padding: 20px;
|
|
2450
|
-
background: rgba(255, 255, 255, 0.72);
|
|
2451
|
-
border: 1px solid var(--line);
|
|
2452
|
-
border-radius: 20px;
|
|
2453
|
-
}
|
|
2454
|
-
|
|
2455
|
-
.detail-section h2 {
|
|
2456
|
-
margin: 0 0 14px;
|
|
2457
|
-
font-size: 1rem;
|
|
2458
|
-
letter-spacing: 0.08em;
|
|
2459
|
-
text-transform: uppercase;
|
|
2460
|
-
}
|
|
2461
|
-
|
|
2462
|
-
.detail-pre {
|
|
2463
|
-
margin: 0;
|
|
2464
|
-
white-space: pre-wrap;
|
|
2465
|
-
word-break: break-word;
|
|
2466
|
-
font-family: var(--mono);
|
|
2467
|
-
line-height: 1.55;
|
|
2468
|
-
}
|
|
2469
|
-
|
|
2470
|
-
.empty {
|
|
2471
|
-
margin: 24px;
|
|
2472
|
-
padding: 24px;
|
|
2473
|
-
border-radius: 20px;
|
|
2474
|
-
background: rgba(255, 255, 255, 0.72);
|
|
2475
|
-
border: 1px dashed rgba(36, 39, 44, 0.2);
|
|
2476
|
-
color: var(--muted);
|
|
2477
|
-
}
|
|
2478
|
-
|
|
2479
|
-
@media (max-width: 900px) {
|
|
2480
|
-
.shell {
|
|
2481
|
-
grid-template-columns: 1fr;
|
|
2482
|
-
}
|
|
2483
|
-
|
|
2484
|
-
.field-row--inline,
|
|
2485
|
-
.toolbar-form,
|
|
2486
|
-
.workspace-grid {
|
|
2487
|
-
grid-template-columns: 1fr;
|
|
2488
|
-
}
|
|
2489
|
-
|
|
2490
|
-
.workspace-hint {
|
|
2491
|
-
margin-left: 0;
|
|
2492
|
-
}
|
|
2493
|
-
}
|
|
2494
|
-
</style>
|
|
2495
|
-
</head>
|
|
2496
|
-
<body>
|
|
2497
|
-
<div class="shell">
|
|
2498
|
-
<aside class="pane sidebar">
|
|
2499
|
-
<section class="hero">
|
|
2500
|
-
<h1>Granola Toolkit</h1>
|
|
2501
|
-
<p>Browser workspace for meetings, notes, transcripts, and export flows on top of one local server instance.</p>
|
|
2502
|
-
<input class="search" data-search placeholder="Search meetings, ids, or tags" />
|
|
2503
|
-
<div class="field-row field-row--inline">
|
|
2504
|
-
<label>
|
|
2505
|
-
<span class="field-label">Sort</span>
|
|
2506
|
-
<select class="select" data-sort>
|
|
2507
|
-
<option value="updated-desc">Newest first</option>
|
|
2508
|
-
<option value="updated-asc">Oldest first</option>
|
|
2509
|
-
<option value="title-asc">Title A-Z</option>
|
|
2510
|
-
<option value="title-desc">Title Z-A</option>
|
|
2511
|
-
</select>
|
|
2512
|
-
</label>
|
|
2513
|
-
<label>
|
|
2514
|
-
<span class="field-label">Updated From</span>
|
|
2515
|
-
<input class="field-input" data-updated-from type="date" />
|
|
2516
|
-
</label>
|
|
2517
|
-
</div>
|
|
2518
|
-
<label class="field-row">
|
|
2519
|
-
<span class="field-label">Updated To</span>
|
|
2520
|
-
<input class="field-input" data-updated-to type="date" />
|
|
2521
|
-
</label>
|
|
2522
|
-
</section>
|
|
2523
|
-
<section class="toolbar">
|
|
2524
|
-
<div>
|
|
2525
|
-
<p>Meetings are loaded from the shared server state so this view can later coexist with the terminal UI.</p>
|
|
2526
|
-
</div>
|
|
2527
|
-
<div class="toolbar-form">
|
|
2528
|
-
<input class="field-input" data-quick-open placeholder="Quick open by id or title" />
|
|
2529
|
-
<button class="button button--secondary" data-quick-open-button>Open</button>
|
|
2530
|
-
</div>
|
|
2531
|
-
</section>
|
|
2532
|
-
<section class="meeting-list" data-meeting-list></section>
|
|
2533
|
-
</aside>
|
|
2534
|
-
<main class="pane detail">
|
|
2535
|
-
<section class="detail-head">
|
|
2536
|
-
<div>
|
|
2537
|
-
<h2>Meeting Workspace</h2>
|
|
2538
|
-
<div data-app-state></div>
|
|
2539
|
-
</div>
|
|
2540
|
-
<div class="state-badge" data-state-badge data-tone="idle">Connecting…</div>
|
|
2541
|
-
</section>
|
|
2542
|
-
<section class="toolbar">
|
|
2543
|
-
<div class="toolbar-actions">
|
|
2544
|
-
<button class="button button--primary" data-refresh>Refresh</button>
|
|
2545
|
-
<button class="button button--secondary" data-export-notes>Export Notes</button>
|
|
2546
|
-
<button class="button button--secondary" data-export-transcripts>Export Transcripts</button>
|
|
2547
|
-
</div>
|
|
2548
|
-
<p>Initial beta web client. It speaks to the same local API that future TUI and attach flows will use.</p>
|
|
2549
|
-
</section>
|
|
2550
|
-
<nav class="workspace-tabs">
|
|
2551
|
-
<button class="workspace-tab" data-workspace-tab="notes">Notes</button>
|
|
2552
|
-
<button class="workspace-tab" data-workspace-tab="transcript">Transcript</button>
|
|
2553
|
-
<button class="workspace-tab" data-workspace-tab="metadata">Metadata</button>
|
|
2554
|
-
<button class="workspace-tab" data-workspace-tab="raw">Raw</button>
|
|
2555
|
-
<span class="workspace-hint">1-4 switch tabs, [ and ] cycle</span>
|
|
2556
|
-
</nav>
|
|
2557
|
-
<div class="detail-meta" data-detail-meta></div>
|
|
2558
|
-
<div class="detail-body" data-detail-body>
|
|
2559
|
-
<div class="empty" data-empty>Select a meeting to inspect its notes and transcript.</div>
|
|
2560
|
-
</div>
|
|
2561
|
-
</main>
|
|
2562
|
-
</div>
|
|
2563
|
-
<script type="module">
|
|
2564
|
-
${String.raw`
|
|
2135
|
+
//#region src/web/client-script.ts
|
|
2136
|
+
const granolaWebClientScript = String.raw`
|
|
2565
2137
|
const state = {
|
|
2566
2138
|
appState: null,
|
|
2567
2139
|
detailError: "",
|
|
@@ -3061,7 +2633,448 @@ void refreshAll().catch((error) => {
|
|
|
3061
2633
|
els.empty.hidden = false;
|
|
3062
2634
|
els.empty.textContent = error.message;
|
|
3063
2635
|
});
|
|
3064
|
-
|
|
2636
|
+
`;
|
|
2637
|
+
//#endregion
|
|
2638
|
+
//#region src/web/markup.ts
|
|
2639
|
+
const granolaWebMarkup = String.raw`
|
|
2640
|
+
<div class="shell">
|
|
2641
|
+
<aside class="pane sidebar">
|
|
2642
|
+
<section class="hero">
|
|
2643
|
+
<h1>Granola Toolkit</h1>
|
|
2644
|
+
<p>Browser workspace for meetings, notes, transcripts, and export flows on top of one local server instance.</p>
|
|
2645
|
+
<input class="search" data-search placeholder="Search meetings, ids, or tags" />
|
|
2646
|
+
<div class="field-row field-row--inline">
|
|
2647
|
+
<label>
|
|
2648
|
+
<span class="field-label">Sort</span>
|
|
2649
|
+
<select class="select" data-sort>
|
|
2650
|
+
<option value="updated-desc">Newest first</option>
|
|
2651
|
+
<option value="updated-asc">Oldest first</option>
|
|
2652
|
+
<option value="title-asc">Title A-Z</option>
|
|
2653
|
+
<option value="title-desc">Title Z-A</option>
|
|
2654
|
+
</select>
|
|
2655
|
+
</label>
|
|
2656
|
+
<label>
|
|
2657
|
+
<span class="field-label">Updated From</span>
|
|
2658
|
+
<input class="field-input" data-updated-from type="date" />
|
|
2659
|
+
</label>
|
|
2660
|
+
</div>
|
|
2661
|
+
<label class="field-row">
|
|
2662
|
+
<span class="field-label">Updated To</span>
|
|
2663
|
+
<input class="field-input" data-updated-to type="date" />
|
|
2664
|
+
</label>
|
|
2665
|
+
</section>
|
|
2666
|
+
<section class="toolbar">
|
|
2667
|
+
<div>
|
|
2668
|
+
<p>Meetings are loaded from the shared server state so this view can later coexist with the terminal UI.</p>
|
|
2669
|
+
</div>
|
|
2670
|
+
<div class="toolbar-form">
|
|
2671
|
+
<input class="field-input" data-quick-open placeholder="Quick open by id or title" />
|
|
2672
|
+
<button class="button button--secondary" data-quick-open-button>Open</button>
|
|
2673
|
+
</div>
|
|
2674
|
+
</section>
|
|
2675
|
+
<section class="meeting-list" data-meeting-list></section>
|
|
2676
|
+
</aside>
|
|
2677
|
+
<main class="pane detail">
|
|
2678
|
+
<section class="detail-head">
|
|
2679
|
+
<div>
|
|
2680
|
+
<h2>Meeting Workspace</h2>
|
|
2681
|
+
<div data-app-state></div>
|
|
2682
|
+
</div>
|
|
2683
|
+
<div class="state-badge" data-state-badge data-tone="idle">Connecting…</div>
|
|
2684
|
+
</section>
|
|
2685
|
+
<section class="toolbar">
|
|
2686
|
+
<div class="toolbar-actions">
|
|
2687
|
+
<button class="button button--primary" data-refresh>Refresh</button>
|
|
2688
|
+
<button class="button button--secondary" data-export-notes>Export Notes</button>
|
|
2689
|
+
<button class="button button--secondary" data-export-transcripts>Export Transcripts</button>
|
|
2690
|
+
</div>
|
|
2691
|
+
<p>Initial beta web client. It speaks to the same local API that future TUI and attach flows will use.</p>
|
|
2692
|
+
</section>
|
|
2693
|
+
<nav class="workspace-tabs">
|
|
2694
|
+
<button class="workspace-tab" data-workspace-tab="notes">Notes</button>
|
|
2695
|
+
<button class="workspace-tab" data-workspace-tab="transcript">Transcript</button>
|
|
2696
|
+
<button class="workspace-tab" data-workspace-tab="metadata">Metadata</button>
|
|
2697
|
+
<button class="workspace-tab" data-workspace-tab="raw">Raw</button>
|
|
2698
|
+
<span class="workspace-hint">1-4 switch tabs, [ and ] cycle</span>
|
|
2699
|
+
</nav>
|
|
2700
|
+
<div class="detail-meta" data-detail-meta></div>
|
|
2701
|
+
<div class="detail-body" data-detail-body>
|
|
2702
|
+
<div class="empty" data-empty>Select a meeting to inspect its notes and transcript.</div>
|
|
2703
|
+
</div>
|
|
2704
|
+
</main>
|
|
2705
|
+
</div>
|
|
2706
|
+
`;
|
|
2707
|
+
//#endregion
|
|
2708
|
+
//#region src/web/styles.ts
|
|
2709
|
+
const granolaWebStyles = String.raw`
|
|
2710
|
+
:root {
|
|
2711
|
+
--bg: #f2ede2;
|
|
2712
|
+
--panel: rgba(255, 252, 247, 0.86);
|
|
2713
|
+
--panel-strong: #fffaf2;
|
|
2714
|
+
--line: rgba(36, 39, 44, 0.12);
|
|
2715
|
+
--ink: #1d242c;
|
|
2716
|
+
--muted: #5d6b77;
|
|
2717
|
+
--accent: #0d6a6d;
|
|
2718
|
+
--accent-soft: rgba(13, 106, 109, 0.12);
|
|
2719
|
+
--warm: #a34f2f;
|
|
2720
|
+
--ok: #246b4f;
|
|
2721
|
+
--error: #9d2c2c;
|
|
2722
|
+
--shadow: 0 24px 80px rgba(40, 32, 16, 0.12);
|
|
2723
|
+
--radius: 24px;
|
|
2724
|
+
--mono: "SF Mono", "IBM Plex Mono", "Cascadia Code", monospace;
|
|
2725
|
+
--serif: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia, serif;
|
|
2726
|
+
--sans: "Avenir Next", "Segoe UI", sans-serif;
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
* { box-sizing: border-box; }
|
|
2730
|
+
|
|
2731
|
+
body {
|
|
2732
|
+
margin: 0;
|
|
2733
|
+
min-height: 100vh;
|
|
2734
|
+
font-family: var(--sans);
|
|
2735
|
+
color: var(--ink);
|
|
2736
|
+
background:
|
|
2737
|
+
radial-gradient(circle at top left, rgba(163, 79, 47, 0.18), transparent 32%),
|
|
2738
|
+
radial-gradient(circle at right 12%, rgba(13, 106, 109, 0.16), transparent 28%),
|
|
2739
|
+
linear-gradient(180deg, #f8f2e8 0%, var(--bg) 100%);
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
.shell {
|
|
2743
|
+
display: grid;
|
|
2744
|
+
grid-template-columns: 320px minmax(0, 1fr);
|
|
2745
|
+
gap: 18px;
|
|
2746
|
+
min-height: 100vh;
|
|
2747
|
+
padding: 24px;
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
.pane {
|
|
2751
|
+
background: var(--panel);
|
|
2752
|
+
backdrop-filter: blur(18px);
|
|
2753
|
+
border: 1px solid var(--line);
|
|
2754
|
+
border-radius: var(--radius);
|
|
2755
|
+
box-shadow: var(--shadow);
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
.sidebar {
|
|
2759
|
+
display: grid;
|
|
2760
|
+
grid-template-rows: auto auto 1fr;
|
|
2761
|
+
overflow: hidden;
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
.hero, .toolbar, .detail-head {
|
|
2765
|
+
padding: 22px 24px;
|
|
2766
|
+
border-bottom: 1px solid var(--line);
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
.hero h1 {
|
|
2770
|
+
margin: 0;
|
|
2771
|
+
font-family: var(--serif);
|
|
2772
|
+
font-size: clamp(2rem, 3vw, 2.8rem);
|
|
2773
|
+
font-weight: 600;
|
|
2774
|
+
letter-spacing: -0.04em;
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
.hero p, .toolbar p {
|
|
2778
|
+
margin: 8px 0 0;
|
|
2779
|
+
color: var(--muted);
|
|
2780
|
+
line-height: 1.5;
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2783
|
+
.search,
|
|
2784
|
+
.select,
|
|
2785
|
+
.field-input {
|
|
2786
|
+
width: 100%;
|
|
2787
|
+
margin-top: 16px;
|
|
2788
|
+
padding: 12px 14px;
|
|
2789
|
+
border: 1px solid var(--line);
|
|
2790
|
+
border-radius: 999px;
|
|
2791
|
+
background: rgba(255, 255, 255, 0.7);
|
|
2792
|
+
color: var(--ink);
|
|
2793
|
+
font: inherit;
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
.field-row {
|
|
2797
|
+
display: grid;
|
|
2798
|
+
gap: 10px;
|
|
2799
|
+
margin-top: 12px;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
.field-row--inline {
|
|
2803
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
.field-label {
|
|
2807
|
+
display: block;
|
|
2808
|
+
margin-bottom: 6px;
|
|
2809
|
+
color: var(--muted);
|
|
2810
|
+
font-size: 0.78rem;
|
|
2811
|
+
font-weight: 700;
|
|
2812
|
+
letter-spacing: 0.08em;
|
|
2813
|
+
text-transform: uppercase;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
.meeting-list {
|
|
2817
|
+
padding: 14px;
|
|
2818
|
+
overflow: auto;
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
.meeting-row {
|
|
2822
|
+
width: 100%;
|
|
2823
|
+
display: grid;
|
|
2824
|
+
gap: 4px;
|
|
2825
|
+
text-align: left;
|
|
2826
|
+
margin: 0 0 10px;
|
|
2827
|
+
padding: 14px 16px;
|
|
2828
|
+
border: 1px solid transparent;
|
|
2829
|
+
border-radius: 18px;
|
|
2830
|
+
background: rgba(255, 255, 255, 0.72);
|
|
2831
|
+
color: inherit;
|
|
2832
|
+
cursor: pointer;
|
|
2833
|
+
transition: transform 140ms ease, border-color 140ms ease, background 140ms ease;
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
.meeting-row:hover,
|
|
2837
|
+
.meeting-row[data-selected="true"] {
|
|
2838
|
+
transform: translateY(-1px);
|
|
2839
|
+
border-color: rgba(13, 106, 109, 0.25);
|
|
2840
|
+
background: var(--panel-strong);
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2843
|
+
.meeting-row__title {
|
|
2844
|
+
font-weight: 600;
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
.meeting-row__meta {
|
|
2848
|
+
color: var(--muted);
|
|
2849
|
+
font-size: 0.92rem;
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
.meeting-empty {
|
|
2853
|
+
padding: 18px;
|
|
2854
|
+
color: var(--muted);
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
.meeting-empty--error {
|
|
2858
|
+
color: var(--error);
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
.detail {
|
|
2862
|
+
display: grid;
|
|
2863
|
+
grid-template-rows: auto auto 1fr;
|
|
2864
|
+
min-width: 0;
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
.detail-head {
|
|
2868
|
+
display: flex;
|
|
2869
|
+
align-items: center;
|
|
2870
|
+
justify-content: space-between;
|
|
2871
|
+
gap: 18px;
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
.detail-head h2 {
|
|
2875
|
+
margin: 0;
|
|
2876
|
+
font-family: var(--serif);
|
|
2877
|
+
font-size: clamp(1.8rem, 2.4vw, 2.4rem);
|
|
2878
|
+
font-weight: 600;
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
.state-badge {
|
|
2882
|
+
padding: 10px 14px;
|
|
2883
|
+
border-radius: 999px;
|
|
2884
|
+
background: var(--accent-soft);
|
|
2885
|
+
color: var(--accent);
|
|
2886
|
+
font-size: 0.92rem;
|
|
2887
|
+
font-weight: 700;
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
.state-badge[data-tone="busy"] { color: var(--warm); background: rgba(163, 79, 47, 0.12); }
|
|
2891
|
+
.state-badge[data-tone="error"] { color: var(--error); background: rgba(157, 44, 44, 0.12); }
|
|
2892
|
+
.state-badge[data-tone="ok"] { color: var(--ok); background: rgba(36, 107, 79, 0.12); }
|
|
2893
|
+
|
|
2894
|
+
.toolbar {
|
|
2895
|
+
display: flex;
|
|
2896
|
+
flex-wrap: wrap;
|
|
2897
|
+
align-items: center;
|
|
2898
|
+
justify-content: space-between;
|
|
2899
|
+
gap: 14px;
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
.toolbar-actions {
|
|
2903
|
+
display: flex;
|
|
2904
|
+
flex-wrap: wrap;
|
|
2905
|
+
gap: 10px;
|
|
2906
|
+
}
|
|
2907
|
+
|
|
2908
|
+
.toolbar-form {
|
|
2909
|
+
display: grid;
|
|
2910
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
2911
|
+
gap: 10px;
|
|
2912
|
+
width: min(440px, 100%);
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
.workspace-tabs {
|
|
2916
|
+
display: flex;
|
|
2917
|
+
flex-wrap: wrap;
|
|
2918
|
+
align-items: center;
|
|
2919
|
+
gap: 10px;
|
|
2920
|
+
padding: 0 24px 18px;
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
.workspace-tab {
|
|
2924
|
+
border: 1px solid var(--line);
|
|
2925
|
+
border-radius: 999px;
|
|
2926
|
+
padding: 10px 14px;
|
|
2927
|
+
background: rgba(255, 255, 255, 0.72);
|
|
2928
|
+
color: var(--muted);
|
|
2929
|
+
cursor: pointer;
|
|
2930
|
+
font: inherit;
|
|
2931
|
+
font-weight: 700;
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
.workspace-tab[data-selected="true"] {
|
|
2935
|
+
background: var(--ink);
|
|
2936
|
+
color: white;
|
|
2937
|
+
border-color: var(--ink);
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
.workspace-hint {
|
|
2941
|
+
color: var(--muted);
|
|
2942
|
+
font-size: 0.86rem;
|
|
2943
|
+
margin-left: auto;
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
.button {
|
|
2947
|
+
border: 0;
|
|
2948
|
+
border-radius: 999px;
|
|
2949
|
+
padding: 12px 16px;
|
|
2950
|
+
font: inherit;
|
|
2951
|
+
font-weight: 700;
|
|
2952
|
+
cursor: pointer;
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
.button--primary {
|
|
2956
|
+
background: var(--ink);
|
|
2957
|
+
color: white;
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
.button--secondary {
|
|
2961
|
+
background: rgba(255, 255, 255, 0.72);
|
|
2962
|
+
color: var(--ink);
|
|
2963
|
+
border: 1px solid var(--line);
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
.status-grid {
|
|
2967
|
+
display: grid;
|
|
2968
|
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
2969
|
+
gap: 14px;
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
.status-label {
|
|
2973
|
+
display: block;
|
|
2974
|
+
margin-bottom: 6px;
|
|
2975
|
+
color: var(--muted);
|
|
2976
|
+
font-size: 0.78rem;
|
|
2977
|
+
letter-spacing: 0.08em;
|
|
2978
|
+
text-transform: uppercase;
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
.detail-meta {
|
|
2982
|
+
display: flex;
|
|
2983
|
+
flex-wrap: wrap;
|
|
2984
|
+
gap: 10px;
|
|
2985
|
+
padding: 0 24px 18px;
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
.detail-chip {
|
|
2989
|
+
padding: 10px 12px;
|
|
2990
|
+
border-radius: 999px;
|
|
2991
|
+
background: rgba(255, 255, 255, 0.72);
|
|
2992
|
+
border: 1px solid var(--line);
|
|
2993
|
+
color: var(--muted);
|
|
2994
|
+
font-size: 0.88rem;
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2997
|
+
.detail-body {
|
|
2998
|
+
padding: 0 24px 24px;
|
|
2999
|
+
overflow: auto;
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
.workspace-grid {
|
|
3003
|
+
display: grid;
|
|
3004
|
+
grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
|
|
3005
|
+
gap: 18px;
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
.workspace-sidebar,
|
|
3009
|
+
.workspace-main {
|
|
3010
|
+
margin-bottom: 0;
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
.detail-section {
|
|
3014
|
+
margin-bottom: 20px;
|
|
3015
|
+
padding: 20px;
|
|
3016
|
+
background: rgba(255, 255, 255, 0.72);
|
|
3017
|
+
border: 1px solid var(--line);
|
|
3018
|
+
border-radius: 20px;
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
.detail-section h2 {
|
|
3022
|
+
margin: 0 0 14px;
|
|
3023
|
+
font-size: 1rem;
|
|
3024
|
+
letter-spacing: 0.08em;
|
|
3025
|
+
text-transform: uppercase;
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
.detail-pre {
|
|
3029
|
+
margin: 0;
|
|
3030
|
+
white-space: pre-wrap;
|
|
3031
|
+
word-break: break-word;
|
|
3032
|
+
font-family: var(--mono);
|
|
3033
|
+
line-height: 1.55;
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
.empty {
|
|
3037
|
+
margin: 24px;
|
|
3038
|
+
padding: 24px;
|
|
3039
|
+
border-radius: 20px;
|
|
3040
|
+
background: rgba(255, 255, 255, 0.72);
|
|
3041
|
+
border: 1px dashed rgba(36, 39, 44, 0.2);
|
|
3042
|
+
color: var(--muted);
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
@media (max-width: 900px) {
|
|
3046
|
+
.shell {
|
|
3047
|
+
grid-template-columns: 1fr;
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
.field-row--inline,
|
|
3051
|
+
.toolbar-form,
|
|
3052
|
+
.workspace-grid {
|
|
3053
|
+
grid-template-columns: 1fr;
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
.workspace-hint {
|
|
3057
|
+
margin-left: 0;
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
`;
|
|
3061
|
+
//#endregion
|
|
3062
|
+
//#region src/server/web.ts
|
|
3063
|
+
function renderGranolaWebPage() {
|
|
3064
|
+
return `<!doctype html>
|
|
3065
|
+
<html lang="en">
|
|
3066
|
+
<head>
|
|
3067
|
+
<meta charset="utf-8" />
|
|
3068
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
3069
|
+
<title>Granola Toolkit</title>
|
|
3070
|
+
<style>
|
|
3071
|
+
${granolaWebStyles}
|
|
3072
|
+
</style>
|
|
3073
|
+
</head>
|
|
3074
|
+
<body>
|
|
3075
|
+
${granolaWebMarkup}
|
|
3076
|
+
<script type="module">
|
|
3077
|
+
${granolaWebClientScript}
|
|
3065
3078
|
<\/script>
|
|
3066
3079
|
</body>
|
|
3067
3080
|
</html>`;
|