granola-toolkit 0.38.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli.js +2957 -1599
- package/package.json +8 -4
package/dist/cli.js
CHANGED
|
@@ -5115,1757 +5115,3105 @@ async function openExternalUrl(url, options = {}) {
|
|
|
5115
5115
|
}))(command.file, command.args);
|
|
5116
5116
|
}
|
|
5117
5117
|
//#endregion
|
|
5118
|
-
//#region src/web/
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
const url = new URL(currentHref);
|
|
5137
|
-
if (selection.selectedFolderId) url.searchParams.set("folder", selection.selectedFolderId);
|
|
5138
|
-
else url.searchParams.delete("folder");
|
|
5139
|
-
if (selection.selectedMeetingId) url.searchParams.set("meeting", selection.selectedMeetingId);
|
|
5140
|
-
else url.searchParams.delete("meeting");
|
|
5141
|
-
if (parseWorkspaceTab(selection.workspaceTab) !== "notes") url.searchParams.set("tab", parseWorkspaceTab(selection.workspaceTab));
|
|
5142
|
-
else url.searchParams.delete("tab");
|
|
5143
|
-
return `${url.pathname}${url.search}${url.hash}`;
|
|
5144
|
-
}
|
|
5145
|
-
function exportScopeLabel(scope) {
|
|
5146
|
-
return scope && scope.mode === "folder" ? `Folder: ${scope.folderName || scope.folderId}` : "Scope: All meetings";
|
|
5147
|
-
}
|
|
5148
|
-
function currentFilterSummary(filters) {
|
|
5149
|
-
const parts = [];
|
|
5150
|
-
if (filters.selectedFolderId) {
|
|
5151
|
-
const folder = filters.folders.find((candidate) => candidate.id === filters.selectedFolderId);
|
|
5152
|
-
parts.push(`folder "${folder ? folder.name : filters.selectedFolderId}"`);
|
|
5153
|
-
}
|
|
5154
|
-
if (filters.search) parts.push(`search "${filters.search}"`);
|
|
5155
|
-
if (filters.updatedFrom) parts.push(`from ${filters.updatedFrom}`);
|
|
5156
|
-
if (filters.updatedTo) parts.push(`to ${filters.updatedTo}`);
|
|
5157
|
-
return parts.join(", ");
|
|
5158
|
-
}
|
|
5159
|
-
function selectMeetingId(meetings, selectedMeetingId) {
|
|
5160
|
-
if (selectedMeetingId && meetings.some((meeting) => meeting.id === selectedMeetingId)) return selectedMeetingId;
|
|
5161
|
-
return meetings[0]?.id ?? null;
|
|
5162
|
-
}
|
|
5163
|
-
function buildMeetingsQuery(filters, options = {}) {
|
|
5164
|
-
const params = new URLSearchParams();
|
|
5165
|
-
params.set("limit", String(options.limit ?? 100));
|
|
5166
|
-
params.set("sort", filters.sort || "updated-desc");
|
|
5167
|
-
if (filters.search) params.set("search", filters.search);
|
|
5168
|
-
if (filters.updatedFrom) params.set("updatedFrom", filters.updatedFrom);
|
|
5169
|
-
if (filters.updatedTo) params.set("updatedTo", filters.updatedTo);
|
|
5170
|
-
if (filters.selectedFolderId) params.set("folderId", filters.selectedFolderId);
|
|
5171
|
-
if (options.refresh) params.set("refresh", "true");
|
|
5172
|
-
return `?${params.toString()}`;
|
|
5173
|
-
}
|
|
5174
|
-
function buildNotesExportRequest(selectedFolderId) {
|
|
5175
|
-
return {
|
|
5176
|
-
folderId: selectedFolderId || void 0,
|
|
5177
|
-
format: "markdown"
|
|
5178
|
-
};
|
|
5179
|
-
}
|
|
5180
|
-
function buildTranscriptsExportRequest(selectedFolderId) {
|
|
5181
|
-
return {
|
|
5182
|
-
folderId: selectedFolderId || void 0,
|
|
5183
|
-
format: "text"
|
|
5184
|
-
};
|
|
5185
|
-
}
|
|
5186
|
-
function nextWorkspaceTab(currentTab, key) {
|
|
5187
|
-
const current = parseWorkspaceTab(currentTab);
|
|
5188
|
-
switch (key) {
|
|
5189
|
-
case "1": return "notes";
|
|
5190
|
-
case "2": return "transcript";
|
|
5191
|
-
case "3": return "metadata";
|
|
5192
|
-
case "4": return "raw";
|
|
5193
|
-
case "]":
|
|
5194
|
-
switch (current) {
|
|
5195
|
-
case "notes": return "transcript";
|
|
5196
|
-
case "transcript": return "metadata";
|
|
5197
|
-
case "metadata": return "raw";
|
|
5198
|
-
case "raw": return "notes";
|
|
5199
|
-
}
|
|
5200
|
-
break;
|
|
5201
|
-
case "[":
|
|
5202
|
-
switch (current) {
|
|
5203
|
-
case "notes": return "raw";
|
|
5204
|
-
case "transcript": return "notes";
|
|
5205
|
-
case "metadata": return "transcript";
|
|
5206
|
-
case "raw": return "metadata";
|
|
5207
|
-
}
|
|
5208
|
-
break;
|
|
5209
|
-
default: return;
|
|
5210
|
-
}
|
|
5118
|
+
//#region src/web/generated.ts
|
|
5119
|
+
const granolaWebClientCss = String.raw`:root {
|
|
5120
|
+
--bg: #f2ede2;
|
|
5121
|
+
--panel: rgba(255, 252, 247, 0.86);
|
|
5122
|
+
--panel-strong: #fffaf2;
|
|
5123
|
+
--line: rgba(36, 39, 44, 0.12);
|
|
5124
|
+
--ink: #1d242c;
|
|
5125
|
+
--muted: #5d6b77;
|
|
5126
|
+
--accent: #0d6a6d;
|
|
5127
|
+
--accent-soft: rgba(13, 106, 109, 0.12);
|
|
5128
|
+
--warm: #a34f2f;
|
|
5129
|
+
--ok: #246b4f;
|
|
5130
|
+
--error: #9d2c2c;
|
|
5131
|
+
--shadow: 0 24px 80px rgba(40, 32, 16, 0.12);
|
|
5132
|
+
--radius: 24px;
|
|
5133
|
+
--mono: "SF Mono", "IBM Plex Mono", "Cascadia Code", monospace;
|
|
5134
|
+
--serif: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia, serif;
|
|
5135
|
+
--sans: "Avenir Next", "Segoe UI", sans-serif;
|
|
5211
5136
|
}
|
|
5212
|
-
//#endregion
|
|
5213
|
-
//#region src/web/client-script.ts
|
|
5214
|
-
const granolaWebClientScript = String.raw`
|
|
5215
|
-
const serverConfig = window.__GRANOLA_SERVER__ || { passwordRequired: false };
|
|
5216
|
-
const workspaceTabs = ["notes", "transcript", "metadata", "raw"];
|
|
5217
|
-
${parseWorkspaceTab.toString()}
|
|
5218
|
-
${startupSelectionFromSearch.toString()}
|
|
5219
|
-
${buildBrowserUrlPath.toString()}
|
|
5220
|
-
${exportScopeLabel.toString()}
|
|
5221
|
-
${currentFilterSummary.toString()}
|
|
5222
|
-
${selectMeetingId.toString()}
|
|
5223
|
-
${buildMeetingsQuery.toString()}
|
|
5224
|
-
${buildNotesExportRequest.toString()}
|
|
5225
|
-
${buildTranscriptsExportRequest.toString()}
|
|
5226
|
-
${nextWorkspaceTab.toString()}
|
|
5227
|
-
|
|
5228
|
-
const state = {
|
|
5229
|
-
appState: null,
|
|
5230
|
-
detailError: "",
|
|
5231
|
-
folderError: "",
|
|
5232
|
-
folders: [],
|
|
5233
|
-
listError: "",
|
|
5234
|
-
meetings: [],
|
|
5235
|
-
quickOpen: "",
|
|
5236
|
-
search: "",
|
|
5237
|
-
selectedFolderId: null,
|
|
5238
|
-
selectedMeeting: null,
|
|
5239
|
-
selectedMeetingBundle: null,
|
|
5240
|
-
selectedMeetingId: null,
|
|
5241
|
-
meetingSource: "live",
|
|
5242
|
-
serverLocked: Boolean(serverConfig.passwordRequired),
|
|
5243
|
-
sort: "updated-desc",
|
|
5244
|
-
updatedFrom: "",
|
|
5245
|
-
updatedTo: "",
|
|
5246
|
-
workspaceTab: "notes",
|
|
5247
|
-
};
|
|
5248
|
-
|
|
5249
|
-
const els = {
|
|
5250
|
-
appState: document.querySelector("[data-app-state]"),
|
|
5251
|
-
authPanel: document.querySelector("[data-auth-panel]"),
|
|
5252
|
-
detailBody: document.querySelector("[data-detail-body]"),
|
|
5253
|
-
detailMeta: document.querySelector("[data-detail-meta]"),
|
|
5254
|
-
empty: document.querySelector("[data-empty]"),
|
|
5255
|
-
folderList: document.querySelector("[data-folder-list]"),
|
|
5256
|
-
jobsList: document.querySelector("[data-jobs-list]"),
|
|
5257
|
-
list: document.querySelector("[data-meeting-list]"),
|
|
5258
|
-
noteButton: document.querySelector("[data-export-notes]"),
|
|
5259
|
-
quickOpen: document.querySelector("[data-quick-open]"),
|
|
5260
|
-
quickOpenButton: document.querySelector("[data-quick-open-button]"),
|
|
5261
|
-
refreshButton: document.querySelector("[data-refresh]"),
|
|
5262
|
-
search: document.querySelector("[data-search]"),
|
|
5263
|
-
securityPanel: document.querySelector("[data-security-panel]"),
|
|
5264
|
-
serverPassword: document.querySelector("[data-server-password]"),
|
|
5265
|
-
lockServerButton: document.querySelector("[data-lock-server]"),
|
|
5266
|
-
sort: document.querySelector("[data-sort]"),
|
|
5267
|
-
stateBadge: document.querySelector("[data-state-badge]"),
|
|
5268
|
-
transcriptButton: document.querySelector("[data-export-transcripts]"),
|
|
5269
|
-
unlockServerButton: document.querySelector("[data-unlock-server]"),
|
|
5270
|
-
updatedFrom: document.querySelector("[data-updated-from]"),
|
|
5271
|
-
updatedTo: document.querySelector("[data-updated-to]"),
|
|
5272
|
-
workspaceTabs: document.querySelectorAll("[data-workspace-tab]"),
|
|
5273
|
-
};
|
|
5274
5137
|
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
selectedFolderId: state.selectedFolderId,
|
|
5278
|
-
selectedMeetingId: state.selectedMeetingId,
|
|
5279
|
-
workspaceTab: state.workspaceTab,
|
|
5280
|
-
});
|
|
5281
|
-
const currentPath = window.location.pathname + window.location.search + window.location.hash;
|
|
5282
|
-
if (nextPath !== currentPath) {
|
|
5283
|
-
history.replaceState(null, "", nextPath);
|
|
5284
|
-
}
|
|
5138
|
+
* {
|
|
5139
|
+
box-sizing: border-box;
|
|
5285
5140
|
}
|
|
5286
5141
|
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5142
|
+
body {
|
|
5143
|
+
margin: 0;
|
|
5144
|
+
min-height: 100vh;
|
|
5145
|
+
font-family: var(--sans);
|
|
5146
|
+
color: var(--ink);
|
|
5147
|
+
background:
|
|
5148
|
+
radial-gradient(circle at top left, rgba(163, 79, 47, 0.18), transparent 32%),
|
|
5149
|
+
radial-gradient(circle at right 12%, rgba(13, 106, 109, 0.16), transparent 28%),
|
|
5150
|
+
linear-gradient(180deg, #f8f2e8 0%, var(--bg) 100%);
|
|
5293
5151
|
}
|
|
5294
5152
|
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5153
|
+
button,
|
|
5154
|
+
input,
|
|
5155
|
+
select {
|
|
5156
|
+
font: inherit;
|
|
5298
5157
|
}
|
|
5299
5158
|
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
els.search.value = state.search;
|
|
5303
|
-
els.sort.value = state.sort;
|
|
5304
|
-
els.updatedFrom.value = state.updatedFrom;
|
|
5305
|
-
els.updatedTo.value = state.updatedTo;
|
|
5159
|
+
#granola-web-root {
|
|
5160
|
+
min-height: 100vh;
|
|
5306
5161
|
}
|
|
5307
5162
|
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5163
|
+
.shell {
|
|
5164
|
+
display: grid;
|
|
5165
|
+
grid-template-columns: 320px minmax(0, 1fr);
|
|
5166
|
+
gap: 18px;
|
|
5167
|
+
min-height: 100vh;
|
|
5168
|
+
padding: 24px;
|
|
5312
5169
|
}
|
|
5313
5170
|
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
}
|
|
5321
|
-
|
|
5322
|
-
const appState = state.appState;
|
|
5323
|
-
const authMode =
|
|
5324
|
-
appState.auth.mode === "api-key"
|
|
5325
|
-
? "API key"
|
|
5326
|
-
: appState.auth.mode === "stored-session"
|
|
5327
|
-
? "Stored session"
|
|
5328
|
-
: "supabase.json";
|
|
5329
|
-
const docs = appState.documents.loaded ? String(appState.documents.count) : "not loaded";
|
|
5330
|
-
const cache = appState.cache.loaded
|
|
5331
|
-
? appState.cache.transcriptCount + " transcript sets"
|
|
5332
|
-
: appState.cache.configured
|
|
5333
|
-
? "configured"
|
|
5334
|
-
: "not configured";
|
|
5335
|
-
const indexStatus = appState.index.loaded
|
|
5336
|
-
? appState.index.meetingCount + " meetings"
|
|
5337
|
-
: appState.index.available
|
|
5338
|
-
? "available"
|
|
5339
|
-
: "not built";
|
|
5340
|
-
const folderStatus = appState.folders.loaded
|
|
5341
|
-
? appState.folders.count + " folders"
|
|
5342
|
-
: "not loaded";
|
|
5343
|
-
const syncStatus = appState.sync.running
|
|
5344
|
-
? "running"
|
|
5345
|
-
: appState.sync.lastError
|
|
5346
|
-
? "error"
|
|
5347
|
-
: appState.sync.lastCompletedAt
|
|
5348
|
-
? "last " + appState.sync.lastCompletedAt.slice(11, 19)
|
|
5349
|
-
: "idle";
|
|
5350
|
-
|
|
5351
|
-
els.appState.innerHTML = [
|
|
5352
|
-
'<div class="status-grid">',
|
|
5353
|
-
'<div><span class="status-label">Surface</span><strong>' + escapeHtml(appState.ui.surface) + "</strong></div>",
|
|
5354
|
-
'<div><span class="status-label">View</span><strong>' + escapeHtml(appState.ui.view) + "</strong></div>",
|
|
5355
|
-
'<div><span class="status-label">Auth</span><strong>' + escapeHtml(authMode) + "</strong></div>",
|
|
5356
|
-
'<div><span class="status-label">Sync</span><strong>' + escapeHtml(syncStatus) + "</strong></div>",
|
|
5357
|
-
'<div><span class="status-label">Documents</span><strong>' + escapeHtml(docs) + "</strong></div>",
|
|
5358
|
-
'<div><span class="status-label">Folders</span><strong>' + escapeHtml(folderStatus) + "</strong></div>",
|
|
5359
|
-
'<div><span class="status-label">Cache</span><strong>' + escapeHtml(cache) + "</strong></div>",
|
|
5360
|
-
'<div><span class="status-label">Index</span><strong>' + escapeHtml(indexStatus) + "</strong></div>",
|
|
5361
|
-
"</div>",
|
|
5362
|
-
].join("");
|
|
5363
|
-
|
|
5364
|
-
renderSecurityPanel();
|
|
5365
|
-
renderAuthPanel();
|
|
5366
|
-
renderExportJobs();
|
|
5171
|
+
.pane {
|
|
5172
|
+
background: var(--panel);
|
|
5173
|
+
backdrop-filter: blur(18px);
|
|
5174
|
+
border: 1px solid var(--line);
|
|
5175
|
+
border-radius: var(--radius);
|
|
5176
|
+
box-shadow: var(--shadow);
|
|
5367
5177
|
}
|
|
5368
5178
|
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
return;
|
|
5374
|
-
}
|
|
5375
|
-
|
|
5376
|
-
const buttons = [
|
|
5377
|
-
[
|
|
5378
|
-
'<button class="folder-row"' +
|
|
5379
|
-
(state.selectedFolderId ? "" : ' data-selected="true"') +
|
|
5380
|
-
' data-folder-id="">',
|
|
5381
|
-
'<span class="folder-row__title">All meetings</span>',
|
|
5382
|
-
'<span class="folder-row__meta">Browse the full meeting list.</span>',
|
|
5383
|
-
"</button>",
|
|
5384
|
-
].join(""),
|
|
5385
|
-
];
|
|
5386
|
-
|
|
5387
|
-
for (const folder of state.folders) {
|
|
5388
|
-
buttons.push(
|
|
5389
|
-
[
|
|
5390
|
-
'<button class="folder-row"' +
|
|
5391
|
-
(folder.id === state.selectedFolderId ? ' data-selected="true"' : "") +
|
|
5392
|
-
' data-folder-id="' +
|
|
5393
|
-
escapeHtml(folder.id) +
|
|
5394
|
-
'">',
|
|
5395
|
-
'<span class="folder-row__title">' +
|
|
5396
|
-
escapeHtml((folder.isFavourite ? "★ " : "") + (folder.name || folder.id)) +
|
|
5397
|
-
"</span>",
|
|
5398
|
-
'<span class="folder-row__meta">' +
|
|
5399
|
-
escapeHtml(String(folder.documentCount) + " meetings") +
|
|
5400
|
-
"</span>",
|
|
5401
|
-
"</button>",
|
|
5402
|
-
].join(""),
|
|
5403
|
-
);
|
|
5404
|
-
}
|
|
5405
|
-
|
|
5406
|
-
if (buttons.length === 1) {
|
|
5407
|
-
buttons.push('<div class="folder-empty">No folders found.</div>');
|
|
5408
|
-
}
|
|
5409
|
-
|
|
5410
|
-
els.folderList.innerHTML = buttons.join("");
|
|
5179
|
+
.sidebar {
|
|
5180
|
+
display: grid;
|
|
5181
|
+
grid-template-rows: auto auto auto 1fr;
|
|
5182
|
+
overflow: hidden;
|
|
5411
5183
|
}
|
|
5412
5184
|
|
|
5413
|
-
|
|
5414
|
-
|
|
5185
|
+
.hero,
|
|
5186
|
+
.toolbar,
|
|
5187
|
+
.detail-head,
|
|
5188
|
+
.folder-panel {
|
|
5189
|
+
padding: 22px 24px;
|
|
5190
|
+
border-bottom: 1px solid var(--line);
|
|
5415
5191
|
}
|
|
5416
5192
|
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
">" +
|
|
5424
|
-
escapeHtml(label) +
|
|
5425
|
-
"</button>"
|
|
5426
|
-
);
|
|
5193
|
+
.hero h1 {
|
|
5194
|
+
margin: 0;
|
|
5195
|
+
font-family: var(--serif);
|
|
5196
|
+
font-size: clamp(2rem, 3vw, 2.8rem);
|
|
5197
|
+
font-weight: 600;
|
|
5198
|
+
letter-spacing: -0.04em;
|
|
5427
5199
|
}
|
|
5428
5200
|
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
(disabled ? " disabled" : "") +
|
|
5435
|
-
">" +
|
|
5436
|
-
escapeHtml(label) +
|
|
5437
|
-
"</button>"
|
|
5438
|
-
);
|
|
5201
|
+
.hero p,
|
|
5202
|
+
.toolbar p {
|
|
5203
|
+
margin: 8px 0 0;
|
|
5204
|
+
color: var(--muted);
|
|
5205
|
+
line-height: 1.5;
|
|
5439
5206
|
}
|
|
5440
5207
|
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
? "Stored session"
|
|
5453
|
-
: "supabase.json";
|
|
5454
|
-
const lastError = auth.lastError
|
|
5455
|
-
? '<div class="auth-card__meta auth-card__error">' + escapeHtml(auth.lastError) + "</div>"
|
|
5456
|
-
: "";
|
|
5457
|
-
|
|
5458
|
-
els.authPanel.innerHTML = [
|
|
5459
|
-
'<div class="auth-card">',
|
|
5460
|
-
'<div class="status-grid">',
|
|
5461
|
-
'<div><span class="status-label">Active</span><strong>' + escapeHtml(activeSource) + "</strong></div>",
|
|
5462
|
-
'<div><span class="status-label">API key</span><strong>' + escapeHtml(auth.apiKeyAvailable ? "available" : "missing") + "</strong></div>",
|
|
5463
|
-
'<div><span class="status-label">Stored</span><strong>' + escapeHtml(auth.storedSessionAvailable ? "available" : "missing") + "</strong></div>",
|
|
5464
|
-
'<div><span class="status-label">supabase.json</span><strong>' + escapeHtml(auth.supabaseAvailable ? "available" : "missing") + "</strong></div>",
|
|
5465
|
-
'<div><span class="status-label">Refresh</span><strong>' + escapeHtml(auth.refreshAvailable ? "available" : "missing") + "</strong></div>",
|
|
5466
|
-
"</div>",
|
|
5467
|
-
auth.clientId
|
|
5468
|
-
? '<div class="auth-card__meta">Client ID: ' + escapeHtml(auth.clientId) + "</div>"
|
|
5469
|
-
: "",
|
|
5470
|
-
auth.signInMethod
|
|
5471
|
-
? '<div class="auth-card__meta">Sign-in method: ' + escapeHtml(auth.signInMethod) + "</div>"
|
|
5472
|
-
: "",
|
|
5473
|
-
auth.supabasePath
|
|
5474
|
-
? '<div class="auth-card__meta">supabase path: ' + escapeHtml(auth.supabasePath) + "</div>"
|
|
5475
|
-
: "",
|
|
5476
|
-
lastError,
|
|
5477
|
-
'<div class="auth-card__meta">Store a Granola Personal API key here or use <code>granola auth login --api-key <token></code>.</div>',
|
|
5478
|
-
'<div class="auth-card__actions">',
|
|
5479
|
-
'<input class="input" type="password" placeholder="grn_..." data-auth-api-key />',
|
|
5480
|
-
authActionButton("Save API key", "login-api-key", false),
|
|
5481
|
-
authActionButton("Import desktop session", "login", !auth.supabaseAvailable),
|
|
5482
|
-
authActionButton("Refresh stored session", "refresh", !auth.storedSessionAvailable || !auth.refreshAvailable),
|
|
5483
|
-
authModeButton("Use API key", "api-key", !auth.apiKeyAvailable || auth.mode === "api-key"),
|
|
5484
|
-
authModeButton("Use stored session", "stored-session", !auth.storedSessionAvailable || auth.mode === "stored-session"),
|
|
5485
|
-
authModeButton("Use supabase.json", "supabase-file", !auth.supabaseAvailable || auth.mode === "supabase-file"),
|
|
5486
|
-
authActionButton("Sign out", "logout", !auth.apiKeyAvailable && !auth.storedSessionAvailable),
|
|
5487
|
-
"</div>",
|
|
5488
|
-
"</div>",
|
|
5489
|
-
].join("");
|
|
5208
|
+
.search,
|
|
5209
|
+
.select,
|
|
5210
|
+
.field-input,
|
|
5211
|
+
.input {
|
|
5212
|
+
width: 100%;
|
|
5213
|
+
margin-top: 16px;
|
|
5214
|
+
padding: 12px 14px;
|
|
5215
|
+
border: 1px solid var(--line);
|
|
5216
|
+
border-radius: 999px;
|
|
5217
|
+
background: rgba(255, 255, 255, 0.7);
|
|
5218
|
+
color: var(--ink);
|
|
5490
5219
|
}
|
|
5491
5220
|
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
return;
|
|
5497
|
-
}
|
|
5498
|
-
|
|
5499
|
-
if (state.meetings.length === 0) {
|
|
5500
|
-
state.selectedMeetingId = null;
|
|
5501
|
-
state.selectedMeeting = null;
|
|
5502
|
-
state.selectedMeetingBundle = null;
|
|
5503
|
-
syncBrowserUrl();
|
|
5504
|
-
const filterSummary = currentFilterSummary({
|
|
5505
|
-
folders: state.folders,
|
|
5506
|
-
search: state.search,
|
|
5507
|
-
selectedFolderId: state.selectedFolderId,
|
|
5508
|
-
updatedFrom: state.updatedFrom,
|
|
5509
|
-
updatedTo: state.updatedTo,
|
|
5510
|
-
});
|
|
5511
|
-
const message = filterSummary
|
|
5512
|
-
? "No meetings match " + filterSummary + "."
|
|
5513
|
-
: "No meetings yet. Try Sync now.";
|
|
5514
|
-
els.list.innerHTML = '<div class="meeting-empty">' + escapeHtml(message) + "</div>";
|
|
5515
|
-
renderMeetingDetail();
|
|
5516
|
-
return;
|
|
5517
|
-
}
|
|
5518
|
-
|
|
5519
|
-
state.selectedMeetingId = selectMeetingId(state.meetings, state.selectedMeetingId);
|
|
5520
|
-
syncBrowserUrl();
|
|
5521
|
-
|
|
5522
|
-
els.list.innerHTML = state.meetings
|
|
5523
|
-
.map((meeting) => {
|
|
5524
|
-
const selected = meeting.id === state.selectedMeetingId ? ' data-selected="true"' : "";
|
|
5525
|
-
const tags = meeting.tags.length ? meeting.tags.map((tag) => "#" + tag).join(" ") : "untagged";
|
|
5526
|
-
return [
|
|
5527
|
-
'<button class="meeting-row"' + selected + ' data-meeting-id="' + escapeHtml(meeting.id) + '">',
|
|
5528
|
-
'<span class="meeting-row__title">' + escapeHtml(meeting.title || meeting.id) + "</span>",
|
|
5529
|
-
'<span class="meeting-row__meta">' + escapeHtml(tags) + "</span>",
|
|
5530
|
-
'<span class="meeting-row__meta">' + escapeHtml(meeting.updatedAt.slice(0, 10) || "unknown") + "</span>",
|
|
5531
|
-
"</button>",
|
|
5532
|
-
].join("");
|
|
5533
|
-
})
|
|
5534
|
-
.join("");
|
|
5221
|
+
.field-row {
|
|
5222
|
+
display: grid;
|
|
5223
|
+
gap: 10px;
|
|
5224
|
+
margin-top: 12px;
|
|
5535
5225
|
}
|
|
5536
5226
|
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
if (state.detailError) {
|
|
5541
|
-
els.empty.hidden = false;
|
|
5542
|
-
els.empty.textContent = state.detailError;
|
|
5543
|
-
els.detailMeta.innerHTML = "";
|
|
5544
|
-
els.detailBody.innerHTML = "";
|
|
5545
|
-
return;
|
|
5546
|
-
}
|
|
5547
|
-
|
|
5548
|
-
const record = state.selectedMeeting;
|
|
5549
|
-
if (!record) {
|
|
5550
|
-
els.empty.hidden = false;
|
|
5551
|
-
els.empty.textContent = "Select a meeting to inspect its notes and transcript.";
|
|
5552
|
-
els.detailMeta.innerHTML = "";
|
|
5553
|
-
els.detailBody.innerHTML = "";
|
|
5554
|
-
return;
|
|
5555
|
-
}
|
|
5556
|
-
|
|
5557
|
-
els.empty.hidden = true;
|
|
5558
|
-
els.detailMeta.innerHTML = [
|
|
5559
|
-
'<div class="detail-chip">ID: ' + escapeHtml(record.meeting.id) + "</div>",
|
|
5560
|
-
'<div class="detail-chip">Source: ' + escapeHtml(record.meeting.noteContentSource) + "</div>",
|
|
5561
|
-
'<div class="detail-chip">Transcript: ' + escapeHtml(String(record.meeting.transcriptSegmentCount)) + " segments</div>",
|
|
5562
|
-
].join("");
|
|
5563
|
-
|
|
5564
|
-
const bundle = state.selectedMeetingBundle;
|
|
5565
|
-
const metadataLines = [
|
|
5566
|
-
"Title: " + (record.meeting.title || record.meeting.id),
|
|
5567
|
-
"Created: " + record.meeting.createdAt,
|
|
5568
|
-
"Updated: " + record.meeting.updatedAt,
|
|
5569
|
-
"Folders: " + (record.meeting.folders.length ? record.meeting.folders.map((folder) => folder.name).join(", ") : "none"),
|
|
5570
|
-
"Tags: " + (record.meeting.tags.length ? record.meeting.tags.join(", ") : "none"),
|
|
5571
|
-
"Transcript loaded: " + (record.meeting.transcriptLoaded ? "yes" : "no"),
|
|
5572
|
-
].join("\n");
|
|
5573
|
-
|
|
5574
|
-
let mainTitle = "Notes";
|
|
5575
|
-
let mainBody = record.noteMarkdown || "";
|
|
5576
|
-
|
|
5577
|
-
switch (state.workspaceTab) {
|
|
5578
|
-
case "transcript":
|
|
5579
|
-
mainTitle = "Transcript";
|
|
5580
|
-
mainBody = record.transcriptText || "(Transcript unavailable)";
|
|
5581
|
-
break;
|
|
5582
|
-
case "metadata":
|
|
5583
|
-
mainTitle = "Metadata";
|
|
5584
|
-
mainBody = metadataLines;
|
|
5585
|
-
break;
|
|
5586
|
-
case "raw":
|
|
5587
|
-
mainTitle = "Raw Bundle";
|
|
5588
|
-
mainBody = JSON.stringify(bundle || record, null, 2);
|
|
5589
|
-
break;
|
|
5590
|
-
default:
|
|
5591
|
-
break;
|
|
5592
|
-
}
|
|
5593
|
-
|
|
5594
|
-
els.detailBody.innerHTML = [
|
|
5595
|
-
'<div class="workspace-grid">',
|
|
5596
|
-
'<aside class="detail-section workspace-sidebar">',
|
|
5597
|
-
"<h2>Meeting Metadata</h2>",
|
|
5598
|
-
'<pre class="detail-pre">' + escapeHtml(metadataLines) + "</pre>",
|
|
5599
|
-
"</aside>",
|
|
5600
|
-
'<section class="detail-section workspace-main">',
|
|
5601
|
-
"<h2>" + escapeHtml(mainTitle) + "</h2>",
|
|
5602
|
-
'<pre class="detail-pre">' + escapeHtml(mainBody) + "</pre>",
|
|
5603
|
-
"</section>",
|
|
5604
|
-
"</div>",
|
|
5605
|
-
].join("");
|
|
5227
|
+
.field-row--inline {
|
|
5228
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
5606
5229
|
}
|
|
5607
5230
|
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
.slice(0, 6)
|
|
5617
|
-
.map((job) => {
|
|
5618
|
-
const progress = job.itemCount > 0
|
|
5619
|
-
? job.completedCount + "/" + job.itemCount + " items"
|
|
5620
|
-
: "0 items";
|
|
5621
|
-
const error = job.error ? '<div class="job-card__meta">' + escapeHtml(job.error) + "</div>" : "";
|
|
5622
|
-
const rerunButton =
|
|
5623
|
-
job.status === "running"
|
|
5624
|
-
? ""
|
|
5625
|
-
: '<button class="button button--secondary" data-rerun-job-id="' + escapeHtml(job.id) + '">Rerun</button>';
|
|
5626
|
-
|
|
5627
|
-
return [
|
|
5628
|
-
'<article class="job-card">',
|
|
5629
|
-
'<div class="job-card__head">',
|
|
5630
|
-
'<div>',
|
|
5631
|
-
'<div class="job-card__title">' + escapeHtml(job.kind) + " export</div>",
|
|
5632
|
-
'<div class="job-card__meta">' + escapeHtml(job.id) + "</div>",
|
|
5633
|
-
"</div>",
|
|
5634
|
-
'<div class="job-card__status" data-status="' + escapeHtml(job.status) + '">' + escapeHtml(job.status) + "</div>",
|
|
5635
|
-
"</div>",
|
|
5636
|
-
'<div class="job-card__meta">Format: ' + escapeHtml(job.format) + " • " + escapeHtml(exportScopeLabel(job.scope)) + " • " + escapeHtml(progress) + " • Written: " + escapeHtml(String(job.written)) + "</div>",
|
|
5637
|
-
'<div class="job-card__meta">Started: ' + escapeHtml(job.startedAt.slice(0, 19)) + "</div>",
|
|
5638
|
-
'<div class="job-card__meta">Output: ' + escapeHtml(job.outputDir) + "</div>",
|
|
5639
|
-
error,
|
|
5640
|
-
'<div class="job-card__actions">' + rerunButton + "</div>",
|
|
5641
|
-
"</article>",
|
|
5642
|
-
].join("");
|
|
5643
|
-
})
|
|
5644
|
-
.join("");
|
|
5231
|
+
.field-label {
|
|
5232
|
+
display: block;
|
|
5233
|
+
margin-bottom: 6px;
|
|
5234
|
+
color: var(--muted);
|
|
5235
|
+
font-size: 0.78rem;
|
|
5236
|
+
font-weight: 700;
|
|
5237
|
+
letter-spacing: 0.08em;
|
|
5238
|
+
text-transform: uppercase;
|
|
5645
5239
|
}
|
|
5646
5240
|
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
if (!response.ok) {
|
|
5651
|
-
if (payload.authRequired) {
|
|
5652
|
-
state.serverLocked = true;
|
|
5653
|
-
renderSecurityPanel();
|
|
5654
|
-
}
|
|
5655
|
-
|
|
5656
|
-
const error = new Error(payload.error || response.statusText || "Request failed");
|
|
5657
|
-
error.authRequired = Boolean(payload.authRequired);
|
|
5658
|
-
throw error;
|
|
5659
|
-
}
|
|
5660
|
-
return payload;
|
|
5241
|
+
.folder-panel {
|
|
5242
|
+
display: grid;
|
|
5243
|
+
gap: 14px;
|
|
5661
5244
|
}
|
|
5662
5245
|
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
const params = new URLSearchParams();
|
|
5669
|
-
params.set("limit", "100");
|
|
5670
|
-
if (refresh) {
|
|
5671
|
-
params.set("refresh", "true");
|
|
5672
|
-
}
|
|
5673
|
-
|
|
5674
|
-
const payload = await fetchJson("/folders?" + params.toString());
|
|
5675
|
-
state.folders = payload.folders || [];
|
|
5676
|
-
if (
|
|
5677
|
-
state.selectedFolderId &&
|
|
5678
|
-
!state.folders.some((folder) => folder.id === state.selectedFolderId)
|
|
5679
|
-
) {
|
|
5680
|
-
state.selectedFolderId = null;
|
|
5681
|
-
}
|
|
5682
|
-
} catch (error) {
|
|
5683
|
-
if (error.authRequired) {
|
|
5684
|
-
throw error;
|
|
5685
|
-
}
|
|
5686
|
-
|
|
5687
|
-
state.folderError = error instanceof Error ? error.message : String(error);
|
|
5688
|
-
state.folders = [];
|
|
5689
|
-
state.selectedFolderId = null;
|
|
5690
|
-
}
|
|
5691
|
-
|
|
5692
|
-
renderFolderList();
|
|
5693
|
-
syncBrowserUrl();
|
|
5246
|
+
.folder-panel__head h2 {
|
|
5247
|
+
margin: 0;
|
|
5248
|
+
font-size: 0.92rem;
|
|
5249
|
+
letter-spacing: 0.08em;
|
|
5250
|
+
text-transform: uppercase;
|
|
5694
5251
|
}
|
|
5695
5252
|
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
try {
|
|
5701
|
-
state.listError = "";
|
|
5702
|
-
const payload = await fetchJson(
|
|
5703
|
-
"/meetings" +
|
|
5704
|
-
buildMeetingsQuery(
|
|
5705
|
-
{
|
|
5706
|
-
search: state.search,
|
|
5707
|
-
selectedFolderId: state.selectedFolderId,
|
|
5708
|
-
sort: state.sort,
|
|
5709
|
-
updatedFrom: state.updatedFrom,
|
|
5710
|
-
updatedTo: state.updatedTo,
|
|
5711
|
-
},
|
|
5712
|
-
{
|
|
5713
|
-
limit: 100,
|
|
5714
|
-
refresh,
|
|
5715
|
-
},
|
|
5716
|
-
),
|
|
5717
|
-
);
|
|
5718
|
-
state.meetings = payload.meetings || [];
|
|
5719
|
-
state.meetingSource = payload.source || "live";
|
|
5720
|
-
|
|
5721
|
-
if (preferredMeetingId && state.meetings.some((meeting) => meeting.id === preferredMeetingId)) {
|
|
5722
|
-
state.selectedMeetingId = preferredMeetingId;
|
|
5723
|
-
}
|
|
5724
|
-
|
|
5725
|
-
renderMeetingList();
|
|
5726
|
-
if (state.selectedMeetingId) {
|
|
5727
|
-
await loadMeeting(state.selectedMeetingId);
|
|
5728
|
-
return;
|
|
5729
|
-
}
|
|
5730
|
-
|
|
5731
|
-
state.detailError = "";
|
|
5732
|
-
renderMeetingDetail();
|
|
5733
|
-
} catch (error) {
|
|
5734
|
-
state.listError = error instanceof Error ? error.message : String(error);
|
|
5735
|
-
state.selectedMeeting = null;
|
|
5736
|
-
state.selectedMeetingBundle = null;
|
|
5737
|
-
state.detailError = state.listError;
|
|
5738
|
-
renderMeetingList();
|
|
5739
|
-
renderMeetingDetail();
|
|
5740
|
-
}
|
|
5253
|
+
.folder-panel__head p {
|
|
5254
|
+
margin: 6px 0 0;
|
|
5255
|
+
color: var(--muted);
|
|
5256
|
+
font-size: 0.9rem;
|
|
5741
5257
|
}
|
|
5742
5258
|
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
try {
|
|
5749
|
-
state.detailError = "";
|
|
5750
|
-
const payload = await fetchJson("/meetings/" + encodeURIComponent(id));
|
|
5751
|
-
state.selectedMeetingBundle = payload;
|
|
5752
|
-
state.selectedMeeting = payload.meeting || null;
|
|
5753
|
-
renderMeetingDetail();
|
|
5754
|
-
} catch (error) {
|
|
5755
|
-
state.selectedMeeting = null;
|
|
5756
|
-
state.selectedMeetingBundle = null;
|
|
5757
|
-
state.detailError = error instanceof Error ? error.message : String(error);
|
|
5758
|
-
renderMeetingDetail();
|
|
5759
|
-
}
|
|
5259
|
+
.folder-list,
|
|
5260
|
+
.jobs-list {
|
|
5261
|
+
display: grid;
|
|
5262
|
+
gap: 10px;
|
|
5760
5263
|
}
|
|
5761
5264
|
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
syncFilterInputs();
|
|
5779
|
-
renderFolderList();
|
|
5780
|
-
await loadMeetings({
|
|
5781
|
-
preferredMeetingId: payload.document.id,
|
|
5782
|
-
});
|
|
5783
|
-
setStatus("Connected", "ok");
|
|
5784
|
-
} catch (error) {
|
|
5785
|
-
state.detailError = error instanceof Error ? error.message : String(error);
|
|
5786
|
-
renderMeetingDetail();
|
|
5787
|
-
setStatus("Quick open failed", "error");
|
|
5788
|
-
}
|
|
5265
|
+
.folder-row,
|
|
5266
|
+
.meeting-row {
|
|
5267
|
+
width: 100%;
|
|
5268
|
+
display: grid;
|
|
5269
|
+
gap: 4px;
|
|
5270
|
+
text-align: left;
|
|
5271
|
+
padding: 12px 14px;
|
|
5272
|
+
border: 1px solid transparent;
|
|
5273
|
+
border-radius: 16px;
|
|
5274
|
+
background: rgba(255, 255, 255, 0.72);
|
|
5275
|
+
color: inherit;
|
|
5276
|
+
cursor: pointer;
|
|
5277
|
+
transition:
|
|
5278
|
+
transform 140ms ease,
|
|
5279
|
+
border-color 140ms ease,
|
|
5280
|
+
background 140ms ease;
|
|
5789
5281
|
}
|
|
5790
5282
|
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
body: JSON.stringify({ forceRefresh: true }),
|
|
5797
|
-
headers: { "content-type": "application/json" },
|
|
5798
|
-
method: "POST",
|
|
5799
|
-
});
|
|
5800
|
-
}
|
|
5801
|
-
|
|
5802
|
-
await loadFolders({ refresh: forceLiveMeetings });
|
|
5803
|
-
const [appState, authState] = await Promise.all([fetchJson("/state"), fetchJson("/auth/status")]);
|
|
5804
|
-
await loadMeetings({ refresh: forceLiveMeetings });
|
|
5805
|
-
state.serverLocked = false;
|
|
5806
|
-
state.appState = {
|
|
5807
|
-
...appState,
|
|
5808
|
-
auth: authState,
|
|
5809
|
-
};
|
|
5810
|
-
renderAppState();
|
|
5811
|
-
setStatus(
|
|
5812
|
-
forceLiveMeetings
|
|
5813
|
-
? "Sync complete"
|
|
5814
|
-
: state.meetingSource === "index"
|
|
5815
|
-
? "Loaded from index"
|
|
5816
|
-
: "Connected",
|
|
5817
|
-
"ok",
|
|
5818
|
-
);
|
|
5819
|
-
} catch (error) {
|
|
5820
|
-
if (error.authRequired) {
|
|
5821
|
-
setStatus("Server locked", "error");
|
|
5822
|
-
renderSecurityPanel();
|
|
5823
|
-
return;
|
|
5824
|
-
}
|
|
5283
|
+
.meeting-row {
|
|
5284
|
+
margin: 0 0 10px;
|
|
5285
|
+
padding: 14px 16px;
|
|
5286
|
+
border-radius: 18px;
|
|
5287
|
+
}
|
|
5825
5288
|
|
|
5826
|
-
|
|
5827
|
-
|
|
5289
|
+
.folder-row:hover,
|
|
5290
|
+
.folder-row[data-selected="true"] {
|
|
5291
|
+
transform: translateY(-1px);
|
|
5292
|
+
border-color: rgba(163, 79, 47, 0.26);
|
|
5293
|
+
background: var(--panel-strong);
|
|
5828
5294
|
}
|
|
5829
5295
|
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
};
|
|
5836
|
-
renderAppState();
|
|
5296
|
+
.meeting-row:hover,
|
|
5297
|
+
.meeting-row[data-selected="true"] {
|
|
5298
|
+
transform: translateY(-1px);
|
|
5299
|
+
border-color: rgba(13, 106, 109, 0.25);
|
|
5300
|
+
background: var(--panel-strong);
|
|
5837
5301
|
}
|
|
5838
5302
|
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
body: JSON.stringify(buildNotesExportRequest(state.selectedFolderId)),
|
|
5843
|
-
headers: { "content-type": "application/json" },
|
|
5844
|
-
method: "POST",
|
|
5845
|
-
});
|
|
5846
|
-
await refreshAll();
|
|
5303
|
+
.folder-row__title,
|
|
5304
|
+
.job-card__title {
|
|
5305
|
+
font-weight: 700;
|
|
5847
5306
|
}
|
|
5848
5307
|
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
state.selectedFolderId ? "Exporting folder transcripts…" : "Exporting transcripts…",
|
|
5852
|
-
"busy",
|
|
5853
|
-
);
|
|
5854
|
-
await fetchJson("/exports/transcripts", {
|
|
5855
|
-
body: JSON.stringify(buildTranscriptsExportRequest(state.selectedFolderId)),
|
|
5856
|
-
headers: { "content-type": "application/json" },
|
|
5857
|
-
method: "POST",
|
|
5858
|
-
});
|
|
5859
|
-
await refreshAll();
|
|
5308
|
+
.meeting-row__title {
|
|
5309
|
+
font-weight: 600;
|
|
5860
5310
|
}
|
|
5861
5311
|
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
state.detailError = error instanceof Error ? error.message : String(error);
|
|
5872
|
-
renderMeetingDetail();
|
|
5873
|
-
}
|
|
5312
|
+
.folder-row__meta,
|
|
5313
|
+
.meeting-row__meta,
|
|
5314
|
+
.auth-card__meta,
|
|
5315
|
+
.job-card__meta,
|
|
5316
|
+
.folder-empty,
|
|
5317
|
+
.job-empty,
|
|
5318
|
+
.meeting-empty {
|
|
5319
|
+
color: var(--muted);
|
|
5320
|
+
font-size: 0.9rem;
|
|
5874
5321
|
}
|
|
5875
5322
|
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
await fetchJson("/auth/login", {
|
|
5881
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
5882
|
-
headers: body ? { "content-type": "application/json" } : undefined,
|
|
5883
|
-
method: "POST",
|
|
5884
|
-
});
|
|
5885
|
-
await refreshAll();
|
|
5886
|
-
} catch (error) {
|
|
5887
|
-
await syncAuthState();
|
|
5888
|
-
setStatus(apiKey ? "API key save failed" : "Auth import failed", "error");
|
|
5889
|
-
state.detailError = error instanceof Error ? error.message : String(error);
|
|
5890
|
-
renderMeetingDetail();
|
|
5891
|
-
}
|
|
5323
|
+
.folder-empty--error,
|
|
5324
|
+
.meeting-empty--error,
|
|
5325
|
+
.auth-card__error {
|
|
5326
|
+
color: var(--error);
|
|
5892
5327
|
}
|
|
5893
5328
|
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
await fetchJson("/auth/logout", { method: "POST" });
|
|
5898
|
-
await refreshAll();
|
|
5899
|
-
} catch (error) {
|
|
5900
|
-
await syncAuthState();
|
|
5901
|
-
setStatus("Sign out failed", "error");
|
|
5902
|
-
state.detailError = error instanceof Error ? error.message : String(error);
|
|
5903
|
-
renderMeetingDetail();
|
|
5904
|
-
}
|
|
5329
|
+
.meeting-list {
|
|
5330
|
+
padding: 14px;
|
|
5331
|
+
overflow: auto;
|
|
5905
5332
|
}
|
|
5906
5333
|
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
await refreshAll();
|
|
5912
|
-
} catch (error) {
|
|
5913
|
-
await syncAuthState();
|
|
5914
|
-
setStatus("Refresh failed", "error");
|
|
5915
|
-
state.detailError = error instanceof Error ? error.message : String(error);
|
|
5916
|
-
renderMeetingDetail();
|
|
5917
|
-
}
|
|
5334
|
+
.detail {
|
|
5335
|
+
display: grid;
|
|
5336
|
+
grid-template-rows: auto auto 1fr;
|
|
5337
|
+
min-width: 0;
|
|
5918
5338
|
}
|
|
5919
5339
|
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
headers: { "content-type": "application/json" },
|
|
5926
|
-
method: "POST",
|
|
5927
|
-
});
|
|
5928
|
-
await refreshAll();
|
|
5929
|
-
} catch (error) {
|
|
5930
|
-
await syncAuthState();
|
|
5931
|
-
setStatus("Switch failed", "error");
|
|
5932
|
-
state.detailError = error instanceof Error ? error.message : String(error);
|
|
5933
|
-
renderMeetingDetail();
|
|
5934
|
-
}
|
|
5340
|
+
.detail-head {
|
|
5341
|
+
display: flex;
|
|
5342
|
+
align-items: center;
|
|
5343
|
+
justify-content: space-between;
|
|
5344
|
+
gap: 18px;
|
|
5935
5345
|
}
|
|
5936
5346
|
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
}
|
|
5943
|
-
|
|
5944
|
-
setStatus("Unlocking server…", "busy");
|
|
5945
|
-
try {
|
|
5946
|
-
await fetchJson("/auth/unlock", {
|
|
5947
|
-
body: JSON.stringify({ password }),
|
|
5948
|
-
headers: { "content-type": "application/json" },
|
|
5949
|
-
method: "POST",
|
|
5950
|
-
});
|
|
5951
|
-
els.serverPassword.value = "";
|
|
5952
|
-
state.serverLocked = false;
|
|
5953
|
-
await refreshAll(true);
|
|
5954
|
-
} catch (error) {
|
|
5955
|
-
setStatus("Unlock failed", "error");
|
|
5956
|
-
state.detailError = error instanceof Error ? error.message : String(error);
|
|
5957
|
-
renderMeetingDetail();
|
|
5958
|
-
}
|
|
5347
|
+
.detail-head h2 {
|
|
5348
|
+
margin: 0;
|
|
5349
|
+
font-family: var(--serif);
|
|
5350
|
+
font-size: clamp(1.8rem, 2.4vw, 2.4rem);
|
|
5351
|
+
font-weight: 600;
|
|
5959
5352
|
}
|
|
5960
5353
|
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
state.serverLocked = true;
|
|
5969
|
-
state.appState = null;
|
|
5970
|
-
state.folders = [];
|
|
5971
|
-
state.meetings = [];
|
|
5972
|
-
state.selectedFolderId = null;
|
|
5973
|
-
state.selectedMeeting = null;
|
|
5974
|
-
state.selectedMeetingBundle = null;
|
|
5975
|
-
state.detailError = "";
|
|
5976
|
-
state.folderError = "";
|
|
5977
|
-
els.serverPassword.value = "";
|
|
5978
|
-
renderSecurityPanel();
|
|
5979
|
-
renderFolderList();
|
|
5980
|
-
renderMeetingList();
|
|
5981
|
-
renderMeetingDetail();
|
|
5982
|
-
setStatus("Server locked", "error");
|
|
5354
|
+
.state-badge {
|
|
5355
|
+
padding: 10px 14px;
|
|
5356
|
+
border-radius: 999px;
|
|
5357
|
+
background: var(--accent-soft);
|
|
5358
|
+
color: var(--accent);
|
|
5359
|
+
font-size: 0.92rem;
|
|
5360
|
+
font-weight: 700;
|
|
5983
5361
|
}
|
|
5984
5362
|
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5363
|
+
.state-badge[data-tone="busy"] {
|
|
5364
|
+
background: rgba(163, 79, 47, 0.12);
|
|
5365
|
+
color: var(--warm);
|
|
5366
|
+
}
|
|
5989
5367
|
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5368
|
+
.state-badge[data-tone="error"] {
|
|
5369
|
+
background: rgba(157, 44, 44, 0.12);
|
|
5370
|
+
color: var(--error);
|
|
5371
|
+
}
|
|
5994
5372
|
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
state.selectedMeetingBundle = null;
|
|
6000
|
-
renderFolderList();
|
|
6001
|
-
void loadMeetings();
|
|
6002
|
-
});
|
|
5373
|
+
.state-badge[data-tone="ok"] {
|
|
5374
|
+
background: rgba(36, 107, 79, 0.12);
|
|
5375
|
+
color: var(--ok);
|
|
5376
|
+
}
|
|
6003
5377
|
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
5378
|
+
.toolbar {
|
|
5379
|
+
display: flex;
|
|
5380
|
+
flex-wrap: wrap;
|
|
5381
|
+
align-items: center;
|
|
5382
|
+
justify-content: space-between;
|
|
5383
|
+
gap: 14px;
|
|
5384
|
+
}
|
|
6008
5385
|
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
5386
|
+
.toolbar-actions,
|
|
5387
|
+
.auth-card__actions,
|
|
5388
|
+
.job-card__actions {
|
|
5389
|
+
display: flex;
|
|
5390
|
+
flex-wrap: wrap;
|
|
5391
|
+
gap: 10px;
|
|
5392
|
+
}
|
|
6013
5393
|
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
5394
|
+
.toolbar-form {
|
|
5395
|
+
display: grid;
|
|
5396
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
5397
|
+
gap: 10px;
|
|
5398
|
+
width: min(440px, 100%);
|
|
5399
|
+
}
|
|
6018
5400
|
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
5401
|
+
.security-panel,
|
|
5402
|
+
.auth-panel,
|
|
5403
|
+
.jobs-panel {
|
|
5404
|
+
padding: 0 24px 18px;
|
|
5405
|
+
}
|
|
6023
5406
|
|
|
6024
|
-
|
|
6025
|
-
|
|
5407
|
+
.security-panel__head h3,
|
|
5408
|
+
.auth-panel__head h3,
|
|
5409
|
+
.jobs-panel__head h3 {
|
|
5410
|
+
margin: 0;
|
|
5411
|
+
font-size: 0.92rem;
|
|
5412
|
+
letter-spacing: 0.08em;
|
|
5413
|
+
text-transform: uppercase;
|
|
5414
|
+
}
|
|
6026
5415
|
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
5416
|
+
.security-panel__head p,
|
|
5417
|
+
.auth-panel__head p,
|
|
5418
|
+
.jobs-panel__head p {
|
|
5419
|
+
margin: 6px 0 0;
|
|
5420
|
+
color: var(--muted);
|
|
5421
|
+
font-size: 0.9rem;
|
|
5422
|
+
}
|
|
6031
5423
|
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
apiKeyInput && "value" in apiKeyInput ? String(apiKeyInput.value || "").trim() : "";
|
|
6039
|
-
if (!apiKey) {
|
|
6040
|
-
setStatus("Enter a Granola API key", "error");
|
|
6041
|
-
return;
|
|
6042
|
-
}
|
|
6043
|
-
void loginAuth(apiKey);
|
|
6044
|
-
return;
|
|
6045
|
-
}
|
|
6046
|
-
case "login":
|
|
6047
|
-
void loginAuth();
|
|
6048
|
-
return;
|
|
6049
|
-
case "logout":
|
|
6050
|
-
void logoutAuth();
|
|
6051
|
-
return;
|
|
6052
|
-
case "refresh":
|
|
6053
|
-
void refreshAuth();
|
|
6054
|
-
return;
|
|
6055
|
-
default:
|
|
6056
|
-
return;
|
|
6057
|
-
}
|
|
6058
|
-
}
|
|
5424
|
+
.security-panel__body,
|
|
5425
|
+
.auth-panel__body {
|
|
5426
|
+
display: grid;
|
|
5427
|
+
gap: 12px;
|
|
5428
|
+
margin-top: 14px;
|
|
5429
|
+
}
|
|
6059
5430
|
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
5431
|
+
.auth-card,
|
|
5432
|
+
.job-card {
|
|
5433
|
+
display: grid;
|
|
5434
|
+
gap: 12px;
|
|
5435
|
+
padding: 14px 16px;
|
|
5436
|
+
border: 1px solid var(--line);
|
|
5437
|
+
border-radius: 18px;
|
|
5438
|
+
background: rgba(255, 255, 255, 0.72);
|
|
5439
|
+
}
|
|
6064
5440
|
|
|
6065
|
-
|
|
6066
|
-
|
|
5441
|
+
.job-card__head {
|
|
5442
|
+
display: flex;
|
|
5443
|
+
flex-wrap: wrap;
|
|
5444
|
+
align-items: center;
|
|
5445
|
+
justify-content: space-between;
|
|
5446
|
+
gap: 10px;
|
|
5447
|
+
}
|
|
6067
5448
|
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
5449
|
+
.job-card__status {
|
|
5450
|
+
padding: 6px 10px;
|
|
5451
|
+
border-radius: 999px;
|
|
5452
|
+
background: var(--accent-soft);
|
|
5453
|
+
color: var(--accent);
|
|
5454
|
+
font-size: 0.82rem;
|
|
5455
|
+
font-weight: 700;
|
|
5456
|
+
}
|
|
6071
5457
|
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
5458
|
+
.job-card__status[data-status="running"] {
|
|
5459
|
+
background: rgba(163, 79, 47, 0.12);
|
|
5460
|
+
color: var(--warm);
|
|
5461
|
+
}
|
|
6075
5462
|
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
5463
|
+
.job-card__status[data-status="failed"] {
|
|
5464
|
+
background: rgba(157, 44, 44, 0.12);
|
|
5465
|
+
color: var(--error);
|
|
5466
|
+
}
|
|
6080
5467
|
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
});
|
|
5468
|
+
.job-card__status[data-status="completed"] {
|
|
5469
|
+
background: rgba(36, 107, 79, 0.12);
|
|
5470
|
+
color: var(--ok);
|
|
5471
|
+
}
|
|
6086
5472
|
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
5473
|
+
.workspace-tabs {
|
|
5474
|
+
display: flex;
|
|
5475
|
+
flex-wrap: wrap;
|
|
5476
|
+
align-items: center;
|
|
5477
|
+
gap: 10px;
|
|
5478
|
+
padding: 0 24px 18px;
|
|
5479
|
+
}
|
|
6090
5480
|
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
5481
|
+
.workspace-tab,
|
|
5482
|
+
.button {
|
|
5483
|
+
border: 1px solid var(--line);
|
|
5484
|
+
border-radius: 999px;
|
|
5485
|
+
padding: 10px 14px;
|
|
5486
|
+
background: rgba(255, 255, 255, 0.72);
|
|
5487
|
+
color: var(--ink);
|
|
5488
|
+
cursor: pointer;
|
|
5489
|
+
font-weight: 700;
|
|
5490
|
+
}
|
|
6094
5491
|
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
}
|
|
5492
|
+
.button {
|
|
5493
|
+
padding: 12px 16px;
|
|
5494
|
+
}
|
|
6098
5495
|
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
5496
|
+
.workspace-tab[data-selected="true"],
|
|
5497
|
+
.button--primary {
|
|
5498
|
+
background: var(--ink);
|
|
5499
|
+
color: #fff;
|
|
5500
|
+
border-color: var(--ink);
|
|
5501
|
+
}
|
|
6103
5502
|
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
}
|
|
5503
|
+
.button--secondary {
|
|
5504
|
+
background: rgba(255, 255, 255, 0.72);
|
|
5505
|
+
}
|
|
6107
5506
|
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
5507
|
+
.button:disabled {
|
|
5508
|
+
cursor: not-allowed;
|
|
5509
|
+
opacity: 0.56;
|
|
5510
|
+
}
|
|
6112
5511
|
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
5512
|
+
.workspace-hint {
|
|
5513
|
+
margin-left: auto;
|
|
5514
|
+
color: var(--muted);
|
|
5515
|
+
font-size: 0.86rem;
|
|
5516
|
+
}
|
|
6116
5517
|
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
5518
|
+
.status-grid {
|
|
5519
|
+
display: grid;
|
|
5520
|
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
5521
|
+
gap: 14px;
|
|
5522
|
+
}
|
|
6121
5523
|
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
5524
|
+
.status-label {
|
|
5525
|
+
display: block;
|
|
5526
|
+
margin-bottom: 6px;
|
|
5527
|
+
color: var(--muted);
|
|
5528
|
+
font-size: 0.78rem;
|
|
5529
|
+
letter-spacing: 0.08em;
|
|
5530
|
+
text-transform: uppercase;
|
|
5531
|
+
}
|
|
6125
5532
|
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
5533
|
+
.detail-meta {
|
|
5534
|
+
display: flex;
|
|
5535
|
+
flex-wrap: wrap;
|
|
5536
|
+
gap: 10px;
|
|
5537
|
+
padding: 0 24px 18px;
|
|
5538
|
+
}
|
|
6130
5539
|
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
5540
|
+
.detail-chip {
|
|
5541
|
+
padding: 10px 12px;
|
|
5542
|
+
border-radius: 999px;
|
|
5543
|
+
background: rgba(255, 255, 255, 0.72);
|
|
5544
|
+
border: 1px solid var(--line);
|
|
5545
|
+
color: var(--muted);
|
|
5546
|
+
font-size: 0.88rem;
|
|
5547
|
+
}
|
|
6134
5548
|
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
5549
|
+
.detail-body {
|
|
5550
|
+
padding: 0 24px 24px;
|
|
5551
|
+
overflow: auto;
|
|
5552
|
+
}
|
|
6139
5553
|
|
|
6140
|
-
|
|
6141
|
-
|
|
5554
|
+
.workspace-grid {
|
|
5555
|
+
display: grid;
|
|
5556
|
+
grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
|
|
5557
|
+
gap: 18px;
|
|
5558
|
+
}
|
|
6142
5559
|
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
5560
|
+
.detail-section {
|
|
5561
|
+
margin-bottom: 20px;
|
|
5562
|
+
padding: 20px;
|
|
5563
|
+
background: rgba(255, 255, 255, 0.72);
|
|
5564
|
+
border: 1px solid var(--line);
|
|
5565
|
+
border-radius: 20px;
|
|
5566
|
+
}
|
|
6147
5567
|
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
5568
|
+
.detail-section h2 {
|
|
5569
|
+
margin: 0 0 14px;
|
|
5570
|
+
font-size: 1rem;
|
|
5571
|
+
letter-spacing: 0.08em;
|
|
5572
|
+
text-transform: uppercase;
|
|
5573
|
+
}
|
|
6153
5574
|
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
5575
|
+
.detail-pre {
|
|
5576
|
+
margin: 0;
|
|
5577
|
+
white-space: pre-wrap;
|
|
5578
|
+
word-break: break-word;
|
|
5579
|
+
font-family: var(--mono);
|
|
5580
|
+
line-height: 1.55;
|
|
5581
|
+
}
|
|
6157
5582
|
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
}
|
|
5583
|
+
.empty {
|
|
5584
|
+
margin: 24px;
|
|
5585
|
+
padding: 24px;
|
|
5586
|
+
border-radius: 20px;
|
|
5587
|
+
background: rgba(255, 255, 255, 0.72);
|
|
5588
|
+
color: var(--muted);
|
|
5589
|
+
}
|
|
6165
5590
|
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
event.target instanceof HTMLTextAreaElement
|
|
6171
|
-
) {
|
|
6172
|
-
return;
|
|
6173
|
-
}
|
|
6174
|
-
|
|
6175
|
-
const nextTab = nextWorkspaceTab(state.workspaceTab, event.key);
|
|
6176
|
-
if (nextTab) {
|
|
6177
|
-
state.workspaceTab = nextTab;
|
|
6178
|
-
syncBrowserUrl();
|
|
6179
|
-
renderMeetingDetail();
|
|
6180
|
-
}
|
|
6181
|
-
});
|
|
6182
|
-
|
|
6183
|
-
const initialSelection = startupSelectionFromSearch(window.location.search);
|
|
6184
|
-
state.selectedFolderId = initialSelection.folderId || null;
|
|
6185
|
-
state.selectedMeetingId = initialSelection.meetingId || null;
|
|
6186
|
-
state.workspaceTab = initialSelection.workspaceTab;
|
|
6187
|
-
|
|
6188
|
-
const events = new EventSource("/events");
|
|
6189
|
-
events.addEventListener("state.updated", (event) => {
|
|
6190
|
-
const previousLoadedAt = state.appState?.documents?.loadedAt;
|
|
6191
|
-
const payload = JSON.parse(event.data);
|
|
6192
|
-
state.appState = payload.state;
|
|
6193
|
-
renderAppState();
|
|
6194
|
-
|
|
6195
|
-
if (
|
|
6196
|
-
state.meetingSource === "index" &&
|
|
6197
|
-
payload.state.documents?.loadedAt &&
|
|
6198
|
-
payload.state.documents.loadedAt !== previousLoadedAt
|
|
6199
|
-
) {
|
|
6200
|
-
void (async () => {
|
|
6201
|
-
await loadFolders();
|
|
6202
|
-
await loadMeetings({
|
|
6203
|
-
preferredMeetingId: state.selectedMeetingId,
|
|
6204
|
-
});
|
|
6205
|
-
})();
|
|
5591
|
+
@media (max-width: 1024px) {
|
|
5592
|
+
.shell,
|
|
5593
|
+
.workspace-grid {
|
|
5594
|
+
grid-template-columns: 1fr;
|
|
6206
5595
|
}
|
|
6207
|
-
});
|
|
6208
|
-
events.addEventListener("error", () => {
|
|
6209
|
-
setStatus("Disconnected", "error");
|
|
6210
|
-
});
|
|
6211
|
-
|
|
6212
|
-
syncFilterInputs();
|
|
6213
|
-
renderSecurityPanel();
|
|
6214
|
-
renderFolderList();
|
|
6215
|
-
|
|
6216
|
-
void refreshAll().catch((error) => {
|
|
6217
|
-
setStatus("Error", "error");
|
|
6218
|
-
els.empty.hidden = false;
|
|
6219
|
-
els.empty.textContent = error.message;
|
|
6220
|
-
});
|
|
6221
|
-
`;
|
|
6222
|
-
//#endregion
|
|
6223
|
-
//#region src/web/markup.ts
|
|
6224
|
-
const granolaWebMarkup = String.raw`
|
|
6225
|
-
<div class="shell">
|
|
6226
|
-
<aside class="pane sidebar">
|
|
6227
|
-
<section class="hero">
|
|
6228
|
-
<h1>Granola Toolkit</h1>
|
|
6229
|
-
<p>Browser workspace for folders, meetings, notes, transcripts, and export flows on top of one local server instance.</p>
|
|
6230
|
-
<input class="search" data-search placeholder="Search meetings, ids, or tags" />
|
|
6231
|
-
<div class="field-row field-row--inline">
|
|
6232
|
-
<label>
|
|
6233
|
-
<span class="field-label">Sort</span>
|
|
6234
|
-
<select class="select" data-sort>
|
|
6235
|
-
<option value="updated-desc">Newest first</option>
|
|
6236
|
-
<option value="updated-asc">Oldest first</option>
|
|
6237
|
-
<option value="title-asc">Title A-Z</option>
|
|
6238
|
-
<option value="title-desc">Title Z-A</option>
|
|
6239
|
-
</select>
|
|
6240
|
-
</label>
|
|
6241
|
-
<label>
|
|
6242
|
-
<span class="field-label">Updated From</span>
|
|
6243
|
-
<input class="field-input" data-updated-from type="date" />
|
|
6244
|
-
</label>
|
|
6245
|
-
</div>
|
|
6246
|
-
<label class="field-row">
|
|
6247
|
-
<span class="field-label">Updated To</span>
|
|
6248
|
-
<input class="field-input" data-updated-to type="date" />
|
|
6249
|
-
</label>
|
|
6250
|
-
</section>
|
|
6251
|
-
<section class="folder-panel">
|
|
6252
|
-
<div class="folder-panel__head">
|
|
6253
|
-
<h2>Folders</h2>
|
|
6254
|
-
<p>Pick a folder to scope the meeting browser, or stay on All meetings.</p>
|
|
6255
|
-
</div>
|
|
6256
|
-
<div class="folder-list" data-folder-list></div>
|
|
6257
|
-
</section>
|
|
6258
|
-
<section class="toolbar">
|
|
6259
|
-
<div>
|
|
6260
|
-
<p>Meetings are loaded from the shared server state so this view can later coexist with the terminal UI.</p>
|
|
6261
|
-
</div>
|
|
6262
|
-
<div class="toolbar-form">
|
|
6263
|
-
<input class="field-input" data-quick-open placeholder="Quick open by id or title" />
|
|
6264
|
-
<button class="button button--secondary" data-quick-open-button>Open</button>
|
|
6265
|
-
</div>
|
|
6266
|
-
</section>
|
|
6267
|
-
<section class="meeting-list" data-meeting-list></section>
|
|
6268
|
-
</aside>
|
|
6269
|
-
<main class="pane detail">
|
|
6270
|
-
<section class="detail-head">
|
|
6271
|
-
<div>
|
|
6272
|
-
<h2>Meeting Workspace</h2>
|
|
6273
|
-
<div data-app-state></div>
|
|
6274
|
-
</div>
|
|
6275
|
-
<div class="state-badge" data-state-badge data-tone="idle">Connecting…</div>
|
|
6276
|
-
</section>
|
|
6277
|
-
<section class="toolbar">
|
|
6278
|
-
<div class="toolbar-actions">
|
|
6279
|
-
<button class="button button--primary" data-refresh>Sync now</button>
|
|
6280
|
-
<button class="button button--secondary" data-export-notes>Export Notes</button>
|
|
6281
|
-
<button class="button button--secondary" data-export-transcripts>Export Transcripts</button>
|
|
6282
|
-
</div>
|
|
6283
|
-
<p>Initial beta web client. It speaks to the same local API that future TUI and attach flows will use.</p>
|
|
6284
|
-
</section>
|
|
6285
|
-
<section class="security-panel" data-security-panel hidden>
|
|
6286
|
-
<div class="security-panel__head">
|
|
6287
|
-
<h3>Server Access</h3>
|
|
6288
|
-
<p>This server is locked with a password. Unlock it to load meetings and live state.</p>
|
|
6289
|
-
</div>
|
|
6290
|
-
<div class="security-panel__body">
|
|
6291
|
-
<input class="field-input" data-server-password type="password" placeholder="Server password" />
|
|
6292
|
-
<div class="toolbar-actions">
|
|
6293
|
-
<button class="button button--primary" data-unlock-server>Unlock</button>
|
|
6294
|
-
<button class="button button--secondary" data-lock-server>Lock</button>
|
|
6295
|
-
</div>
|
|
6296
|
-
</div>
|
|
6297
|
-
</section>
|
|
6298
|
-
<section class="auth-panel">
|
|
6299
|
-
<div class="auth-panel__head">
|
|
6300
|
-
<h3>Auth Session</h3>
|
|
6301
|
-
<p>Inspect, refresh, and switch between stored session and <code>supabase.json</code>.</p>
|
|
6302
|
-
</div>
|
|
6303
|
-
<div class="auth-panel__body" data-auth-panel></div>
|
|
6304
|
-
</section>
|
|
6305
|
-
<section class="jobs-panel">
|
|
6306
|
-
<div class="jobs-panel__head">
|
|
6307
|
-
<h3>Recent Export Jobs</h3>
|
|
6308
|
-
<p>Tracked across CLI and web runs.</p>
|
|
6309
|
-
</div>
|
|
6310
|
-
<div class="jobs-list" data-jobs-list></div>
|
|
6311
|
-
</section>
|
|
6312
|
-
<nav class="workspace-tabs">
|
|
6313
|
-
<button class="workspace-tab" data-workspace-tab="notes">Notes</button>
|
|
6314
|
-
<button class="workspace-tab" data-workspace-tab="transcript">Transcript</button>
|
|
6315
|
-
<button class="workspace-tab" data-workspace-tab="metadata">Metadata</button>
|
|
6316
|
-
<button class="workspace-tab" data-workspace-tab="raw">Raw</button>
|
|
6317
|
-
<span class="workspace-hint">1-4 switch tabs, [ and ] cycle</span>
|
|
6318
|
-
</nav>
|
|
6319
|
-
<div class="detail-meta" data-detail-meta></div>
|
|
6320
|
-
<div class="detail-body" data-detail-body>
|
|
6321
|
-
<div class="empty" data-empty>Select a meeting to inspect its notes and transcript.</div>
|
|
6322
|
-
</div>
|
|
6323
|
-
</main>
|
|
6324
|
-
</div>
|
|
6325
|
-
`;
|
|
6326
|
-
//#endregion
|
|
6327
|
-
//#region src/web/styles.ts
|
|
6328
|
-
const granolaWebStyles = String.raw`
|
|
6329
|
-
:root {
|
|
6330
|
-
--bg: #f2ede2;
|
|
6331
|
-
--panel: rgba(255, 252, 247, 0.86);
|
|
6332
|
-
--panel-strong: #fffaf2;
|
|
6333
|
-
--line: rgba(36, 39, 44, 0.12);
|
|
6334
|
-
--ink: #1d242c;
|
|
6335
|
-
--muted: #5d6b77;
|
|
6336
|
-
--accent: #0d6a6d;
|
|
6337
|
-
--accent-soft: rgba(13, 106, 109, 0.12);
|
|
6338
|
-
--warm: #a34f2f;
|
|
6339
|
-
--ok: #246b4f;
|
|
6340
|
-
--error: #9d2c2c;
|
|
6341
|
-
--shadow: 0 24px 80px rgba(40, 32, 16, 0.12);
|
|
6342
|
-
--radius: 24px;
|
|
6343
|
-
--mono: "SF Mono", "IBM Plex Mono", "Cascadia Code", monospace;
|
|
6344
|
-
--serif: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia, serif;
|
|
6345
|
-
--sans: "Avenir Next", "Segoe UI", sans-serif;
|
|
6346
|
-
}
|
|
6347
|
-
|
|
6348
|
-
* { box-sizing: border-box; }
|
|
6349
|
-
|
|
6350
|
-
body {
|
|
6351
|
-
margin: 0;
|
|
6352
|
-
min-height: 100vh;
|
|
6353
|
-
font-family: var(--sans);
|
|
6354
|
-
color: var(--ink);
|
|
6355
|
-
background:
|
|
6356
|
-
radial-gradient(circle at top left, rgba(163, 79, 47, 0.18), transparent 32%),
|
|
6357
|
-
radial-gradient(circle at right 12%, rgba(13, 106, 109, 0.16), transparent 28%),
|
|
6358
|
-
linear-gradient(180deg, #f8f2e8 0%, var(--bg) 100%);
|
|
6359
|
-
}
|
|
6360
|
-
|
|
6361
|
-
.shell {
|
|
6362
|
-
display: grid;
|
|
6363
|
-
grid-template-columns: 320px minmax(0, 1fr);
|
|
6364
|
-
gap: 18px;
|
|
6365
|
-
min-height: 100vh;
|
|
6366
|
-
padding: 24px;
|
|
6367
|
-
}
|
|
6368
|
-
|
|
6369
|
-
.pane {
|
|
6370
|
-
background: var(--panel);
|
|
6371
|
-
backdrop-filter: blur(18px);
|
|
6372
|
-
border: 1px solid var(--line);
|
|
6373
|
-
border-radius: var(--radius);
|
|
6374
|
-
box-shadow: var(--shadow);
|
|
6375
|
-
}
|
|
6376
|
-
|
|
6377
|
-
.sidebar {
|
|
6378
|
-
display: grid;
|
|
6379
|
-
grid-template-rows: auto auto auto 1fr;
|
|
6380
|
-
overflow: hidden;
|
|
6381
5596
|
}
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
5597
|
+
/*$vite$:1*/`;
|
|
5598
|
+
const granolaWebClientJs = String.raw`//#region node_modules/solid-js/dist/solid.js
|
|
5599
|
+
var sharedConfig = {
|
|
5600
|
+
context: void 0,
|
|
5601
|
+
registry: void 0,
|
|
5602
|
+
effects: void 0,
|
|
5603
|
+
done: false,
|
|
5604
|
+
getContextId() {
|
|
5605
|
+
return getContextId(this.context.count);
|
|
5606
|
+
},
|
|
5607
|
+
getNextContextId() {
|
|
5608
|
+
return getContextId(this.context.count++);
|
|
5609
|
+
}
|
|
5610
|
+
};
|
|
5611
|
+
function getContextId(count) {
|
|
5612
|
+
const num = String(count), len = num.length - 1;
|
|
5613
|
+
return sharedConfig.context.id + (len ? String.fromCharCode(96 + len) : "") + num;
|
|
6386
5614
|
}
|
|
6387
|
-
|
|
6388
|
-
.
|
|
6389
|
-
margin: 0;
|
|
6390
|
-
font-family: var(--serif);
|
|
6391
|
-
font-size: clamp(2rem, 3vw, 2.8rem);
|
|
6392
|
-
font-weight: 600;
|
|
6393
|
-
letter-spacing: -0.04em;
|
|
5615
|
+
function setHydrateContext(context) {
|
|
5616
|
+
sharedConfig.context = context;
|
|
6394
5617
|
}
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
5618
|
+
function nextHydrateContext() {
|
|
5619
|
+
return {
|
|
5620
|
+
...sharedConfig.context,
|
|
5621
|
+
id: sharedConfig.getNextContextId(),
|
|
5622
|
+
count: 0
|
|
5623
|
+
};
|
|
6400
5624
|
}
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
5625
|
+
var equalFn = (a, b) => a === b;
|
|
5626
|
+
var $PROXY = Symbol("solid-proxy");
|
|
5627
|
+
var $TRACK = Symbol("solid-track");
|
|
5628
|
+
var signalOptions = { equals: equalFn };
|
|
5629
|
+
var ERROR = null;
|
|
5630
|
+
var runEffects = runQueue;
|
|
5631
|
+
var STALE = 1;
|
|
5632
|
+
var PENDING = 2;
|
|
5633
|
+
var UNOWNED = {
|
|
5634
|
+
owned: null,
|
|
5635
|
+
cleanups: null,
|
|
5636
|
+
context: null,
|
|
5637
|
+
owner: null
|
|
5638
|
+
};
|
|
5639
|
+
var Owner = null;
|
|
5640
|
+
var Transition = null;
|
|
5641
|
+
var Scheduler = null;
|
|
5642
|
+
var ExternalSourceConfig = null;
|
|
5643
|
+
var Listener = null;
|
|
5644
|
+
var Updates = null;
|
|
5645
|
+
var Effects = null;
|
|
5646
|
+
var ExecCount = 0;
|
|
5647
|
+
function createRoot(fn, detachedOwner) {
|
|
5648
|
+
const listener = Listener, owner = Owner, unowned = fn.length === 0, current = detachedOwner === void 0 ? owner : detachedOwner, root = unowned ? UNOWNED : {
|
|
5649
|
+
owned: null,
|
|
5650
|
+
cleanups: null,
|
|
5651
|
+
context: current ? current.context : null,
|
|
5652
|
+
owner: current
|
|
5653
|
+
}, updateFn = unowned ? fn : () => fn(() => untrack(() => cleanNode(root)));
|
|
5654
|
+
Owner = root;
|
|
5655
|
+
Listener = null;
|
|
5656
|
+
try {
|
|
5657
|
+
return runUpdates(updateFn, true);
|
|
5658
|
+
} finally {
|
|
5659
|
+
Listener = listener;
|
|
5660
|
+
Owner = owner;
|
|
5661
|
+
}
|
|
5662
|
+
}
|
|
5663
|
+
function createSignal(value, options) {
|
|
5664
|
+
options = options ? Object.assign({}, signalOptions, options) : signalOptions;
|
|
5665
|
+
const s = {
|
|
5666
|
+
value,
|
|
5667
|
+
observers: null,
|
|
5668
|
+
observerSlots: null,
|
|
5669
|
+
comparator: options.equals || void 0
|
|
5670
|
+
};
|
|
5671
|
+
const setter = (value) => {
|
|
5672
|
+
if (typeof value === "function") if (Transition && Transition.running && Transition.sources.has(s)) value = value(s.tValue);
|
|
5673
|
+
else value = value(s.value);
|
|
5674
|
+
return writeSignal(s, value);
|
|
5675
|
+
};
|
|
5676
|
+
return [readSignal.bind(s), setter];
|
|
5677
|
+
}
|
|
5678
|
+
function createRenderEffect(fn, value, options) {
|
|
5679
|
+
const c = createComputation(fn, value, false, STALE);
|
|
5680
|
+
if (Scheduler && Transition && Transition.running) Updates.push(c);
|
|
5681
|
+
else updateComputation(c);
|
|
5682
|
+
}
|
|
5683
|
+
function createEffect(fn, value, options) {
|
|
5684
|
+
runEffects = runUserEffects;
|
|
5685
|
+
const c = createComputation(fn, value, false, STALE), s = SuspenseContext && useContext(SuspenseContext);
|
|
5686
|
+
if (s) c.suspense = s;
|
|
5687
|
+
if (!options || !options.render) c.user = true;
|
|
5688
|
+
Effects ? Effects.push(c) : updateComputation(c);
|
|
5689
|
+
}
|
|
5690
|
+
function createMemo(fn, value, options) {
|
|
5691
|
+
options = options ? Object.assign({}, signalOptions, options) : signalOptions;
|
|
5692
|
+
const c = createComputation(fn, value, true, 0);
|
|
5693
|
+
c.observers = null;
|
|
5694
|
+
c.observerSlots = null;
|
|
5695
|
+
c.comparator = options.equals || void 0;
|
|
5696
|
+
if (Scheduler && Transition && Transition.running) {
|
|
5697
|
+
c.tState = STALE;
|
|
5698
|
+
Updates.push(c);
|
|
5699
|
+
} else updateComputation(c);
|
|
5700
|
+
return readSignal.bind(c);
|
|
5701
|
+
}
|
|
5702
|
+
function batch(fn) {
|
|
5703
|
+
return runUpdates(fn, false);
|
|
5704
|
+
}
|
|
5705
|
+
function untrack(fn) {
|
|
5706
|
+
if (!ExternalSourceConfig && Listener === null) return fn();
|
|
5707
|
+
const listener = Listener;
|
|
5708
|
+
Listener = null;
|
|
5709
|
+
try {
|
|
5710
|
+
if (ExternalSourceConfig) return ExternalSourceConfig.untrack(fn);
|
|
5711
|
+
return fn();
|
|
5712
|
+
} finally {
|
|
5713
|
+
Listener = listener;
|
|
5714
|
+
}
|
|
5715
|
+
}
|
|
5716
|
+
function onMount(fn) {
|
|
5717
|
+
createEffect(() => untrack(fn));
|
|
5718
|
+
}
|
|
5719
|
+
function onCleanup(fn) {
|
|
5720
|
+
if (Owner === null);
|
|
5721
|
+
else if (Owner.cleanups === null) Owner.cleanups = [fn];
|
|
5722
|
+
else Owner.cleanups.push(fn);
|
|
5723
|
+
return fn;
|
|
5724
|
+
}
|
|
5725
|
+
function getListener() {
|
|
5726
|
+
return Listener;
|
|
5727
|
+
}
|
|
5728
|
+
function startTransition(fn) {
|
|
5729
|
+
if (Transition && Transition.running) {
|
|
5730
|
+
fn();
|
|
5731
|
+
return Transition.done;
|
|
5732
|
+
}
|
|
5733
|
+
const l = Listener;
|
|
5734
|
+
const o = Owner;
|
|
5735
|
+
return Promise.resolve().then(() => {
|
|
5736
|
+
Listener = l;
|
|
5737
|
+
Owner = o;
|
|
5738
|
+
let t;
|
|
5739
|
+
if (Scheduler || SuspenseContext) {
|
|
5740
|
+
t = Transition || (Transition = {
|
|
5741
|
+
sources: /* @__PURE__ */ new Set(),
|
|
5742
|
+
effects: [],
|
|
5743
|
+
promises: /* @__PURE__ */ new Set(),
|
|
5744
|
+
disposed: /* @__PURE__ */ new Set(),
|
|
5745
|
+
queue: /* @__PURE__ */ new Set(),
|
|
5746
|
+
running: true
|
|
5747
|
+
});
|
|
5748
|
+
t.done || (t.done = new Promise((res) => t.resolve = res));
|
|
5749
|
+
t.running = true;
|
|
5750
|
+
}
|
|
5751
|
+
runUpdates(fn, false);
|
|
5752
|
+
Listener = Owner = null;
|
|
5753
|
+
return t ? t.done : void 0;
|
|
5754
|
+
});
|
|
6413
5755
|
}
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
5756
|
+
var [transPending, setTransPending] = /* @__PURE__ */ createSignal(false);
|
|
5757
|
+
function useContext(context) {
|
|
5758
|
+
let value;
|
|
5759
|
+
return Owner && Owner.context && (value = Owner.context[context.id]) !== void 0 ? value : context.defaultValue;
|
|
5760
|
+
}
|
|
5761
|
+
var SuspenseContext;
|
|
5762
|
+
function readSignal() {
|
|
5763
|
+
const runningTransition = Transition && Transition.running;
|
|
5764
|
+
if (this.sources && (runningTransition ? this.tState : this.state)) if ((runningTransition ? this.tState : this.state) === STALE) updateComputation(this);
|
|
5765
|
+
else {
|
|
5766
|
+
const updates = Updates;
|
|
5767
|
+
Updates = null;
|
|
5768
|
+
runUpdates(() => lookUpstream(this), false);
|
|
5769
|
+
Updates = updates;
|
|
5770
|
+
}
|
|
5771
|
+
if (Listener) {
|
|
5772
|
+
const sSlot = this.observers ? this.observers.length : 0;
|
|
5773
|
+
if (!Listener.sources) {
|
|
5774
|
+
Listener.sources = [this];
|
|
5775
|
+
Listener.sourceSlots = [sSlot];
|
|
5776
|
+
} else {
|
|
5777
|
+
Listener.sources.push(this);
|
|
5778
|
+
Listener.sourceSlots.push(sSlot);
|
|
5779
|
+
}
|
|
5780
|
+
if (!this.observers) {
|
|
5781
|
+
this.observers = [Listener];
|
|
5782
|
+
this.observerSlots = [Listener.sources.length - 1];
|
|
5783
|
+
} else {
|
|
5784
|
+
this.observers.push(Listener);
|
|
5785
|
+
this.observerSlots.push(Listener.sources.length - 1);
|
|
5786
|
+
}
|
|
5787
|
+
}
|
|
5788
|
+
if (runningTransition && Transition.sources.has(this)) return this.tValue;
|
|
5789
|
+
return this.value;
|
|
5790
|
+
}
|
|
5791
|
+
function writeSignal(node, value, isComp) {
|
|
5792
|
+
let current = Transition && Transition.running && Transition.sources.has(node) ? node.tValue : node.value;
|
|
5793
|
+
if (!node.comparator || !node.comparator(current, value)) {
|
|
5794
|
+
if (Transition) {
|
|
5795
|
+
const TransitionRunning = Transition.running;
|
|
5796
|
+
if (TransitionRunning || !isComp && Transition.sources.has(node)) {
|
|
5797
|
+
Transition.sources.add(node);
|
|
5798
|
+
node.tValue = value;
|
|
5799
|
+
}
|
|
5800
|
+
if (!TransitionRunning) node.value = value;
|
|
5801
|
+
} else node.value = value;
|
|
5802
|
+
if (node.observers && node.observers.length) runUpdates(() => {
|
|
5803
|
+
for (let i = 0; i < node.observers.length; i += 1) {
|
|
5804
|
+
const o = node.observers[i];
|
|
5805
|
+
const TransitionRunning = Transition && Transition.running;
|
|
5806
|
+
if (TransitionRunning && Transition.disposed.has(o)) continue;
|
|
5807
|
+
if (TransitionRunning ? !o.tState : !o.state) {
|
|
5808
|
+
if (o.pure) Updates.push(o);
|
|
5809
|
+
else Effects.push(o);
|
|
5810
|
+
if (o.observers) markDownstream(o);
|
|
5811
|
+
}
|
|
5812
|
+
if (!TransitionRunning) o.state = STALE;
|
|
5813
|
+
else o.tState = STALE;
|
|
5814
|
+
}
|
|
5815
|
+
if (Updates.length > 1e6) {
|
|
5816
|
+
Updates = [];
|
|
5817
|
+
throw new Error();
|
|
5818
|
+
}
|
|
5819
|
+
}, false);
|
|
5820
|
+
}
|
|
5821
|
+
return value;
|
|
6419
5822
|
}
|
|
6420
|
-
|
|
6421
|
-
.
|
|
6422
|
-
|
|
5823
|
+
function updateComputation(node) {
|
|
5824
|
+
if (!node.fn) return;
|
|
5825
|
+
cleanNode(node);
|
|
5826
|
+
const time = ExecCount;
|
|
5827
|
+
runComputation(node, Transition && Transition.running && Transition.sources.has(node) ? node.tValue : node.value, time);
|
|
5828
|
+
if (Transition && !Transition.running && Transition.sources.has(node)) queueMicrotask(() => {
|
|
5829
|
+
runUpdates(() => {
|
|
5830
|
+
Transition && (Transition.running = true);
|
|
5831
|
+
Listener = Owner = node;
|
|
5832
|
+
runComputation(node, node.tValue, time);
|
|
5833
|
+
Listener = Owner = null;
|
|
5834
|
+
}, false);
|
|
5835
|
+
});
|
|
6423
5836
|
}
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
5837
|
+
function runComputation(node, value, time) {
|
|
5838
|
+
let nextValue;
|
|
5839
|
+
const owner = Owner, listener = Listener;
|
|
5840
|
+
Listener = Owner = node;
|
|
5841
|
+
try {
|
|
5842
|
+
nextValue = node.fn(value);
|
|
5843
|
+
} catch (err) {
|
|
5844
|
+
if (node.pure) if (Transition && Transition.running) {
|
|
5845
|
+
node.tState = STALE;
|
|
5846
|
+
node.tOwned && node.tOwned.forEach(cleanNode);
|
|
5847
|
+
node.tOwned = void 0;
|
|
5848
|
+
} else {
|
|
5849
|
+
node.state = STALE;
|
|
5850
|
+
node.owned && node.owned.forEach(cleanNode);
|
|
5851
|
+
node.owned = null;
|
|
5852
|
+
}
|
|
5853
|
+
node.updatedAt = time + 1;
|
|
5854
|
+
return handleError(err);
|
|
5855
|
+
} finally {
|
|
5856
|
+
Listener = listener;
|
|
5857
|
+
Owner = owner;
|
|
5858
|
+
}
|
|
5859
|
+
if (!node.updatedAt || node.updatedAt <= time) {
|
|
5860
|
+
if (node.updatedAt != null && "observers" in node) writeSignal(node, nextValue, true);
|
|
5861
|
+
else if (Transition && Transition.running && node.pure) {
|
|
5862
|
+
if (!Transition.sources.has(node)) node.value = nextValue;
|
|
5863
|
+
Transition.sources.add(node);
|
|
5864
|
+
node.tValue = nextValue;
|
|
5865
|
+
} else node.value = nextValue;
|
|
5866
|
+
node.updatedAt = time;
|
|
5867
|
+
}
|
|
5868
|
+
}
|
|
5869
|
+
function createComputation(fn, init, pure, state = STALE, options) {
|
|
5870
|
+
const c = {
|
|
5871
|
+
fn,
|
|
5872
|
+
state,
|
|
5873
|
+
updatedAt: null,
|
|
5874
|
+
owned: null,
|
|
5875
|
+
sources: null,
|
|
5876
|
+
sourceSlots: null,
|
|
5877
|
+
cleanups: null,
|
|
5878
|
+
value: init,
|
|
5879
|
+
owner: Owner,
|
|
5880
|
+
context: Owner ? Owner.context : null,
|
|
5881
|
+
pure
|
|
5882
|
+
};
|
|
5883
|
+
if (Transition && Transition.running) {
|
|
5884
|
+
c.state = 0;
|
|
5885
|
+
c.tState = state;
|
|
5886
|
+
}
|
|
5887
|
+
if (Owner === null);
|
|
5888
|
+
else if (Owner !== UNOWNED) if (Transition && Transition.running && Owner.pure) if (!Owner.tOwned) Owner.tOwned = [c];
|
|
5889
|
+
else Owner.tOwned.push(c);
|
|
5890
|
+
else if (!Owner.owned) Owner.owned = [c];
|
|
5891
|
+
else Owner.owned.push(c);
|
|
5892
|
+
if (ExternalSourceConfig && c.fn) {
|
|
5893
|
+
const sourceFn = c.fn;
|
|
5894
|
+
const [track, trigger] = createSignal(void 0, { equals: false });
|
|
5895
|
+
const ordinary = ExternalSourceConfig.factory(sourceFn, trigger);
|
|
5896
|
+
onCleanup(() => ordinary.dispose());
|
|
5897
|
+
let inTransition;
|
|
5898
|
+
const triggerInTransition = () => startTransition(trigger).then(() => {
|
|
5899
|
+
if (inTransition) {
|
|
5900
|
+
inTransition.dispose();
|
|
5901
|
+
inTransition = void 0;
|
|
5902
|
+
}
|
|
5903
|
+
});
|
|
5904
|
+
c.fn = (x) => {
|
|
5905
|
+
track();
|
|
5906
|
+
if (Transition && Transition.running) {
|
|
5907
|
+
if (!inTransition) inTransition = ExternalSourceConfig.factory(sourceFn, triggerInTransition);
|
|
5908
|
+
return inTransition.track(x);
|
|
5909
|
+
}
|
|
5910
|
+
return ordinary.track(x);
|
|
5911
|
+
};
|
|
5912
|
+
}
|
|
5913
|
+
return c;
|
|
5914
|
+
}
|
|
5915
|
+
function runTop(node) {
|
|
5916
|
+
const runningTransition = Transition && Transition.running;
|
|
5917
|
+
if ((runningTransition ? node.tState : node.state) === 0) return;
|
|
5918
|
+
if ((runningTransition ? node.tState : node.state) === PENDING) return lookUpstream(node);
|
|
5919
|
+
if (node.suspense && untrack(node.suspense.inFallback)) return node.suspense.effects.push(node);
|
|
5920
|
+
const ancestors = [node];
|
|
5921
|
+
while ((node = node.owner) && (!node.updatedAt || node.updatedAt < ExecCount)) {
|
|
5922
|
+
if (runningTransition && Transition.disposed.has(node)) return;
|
|
5923
|
+
if (runningTransition ? node.tState : node.state) ancestors.push(node);
|
|
5924
|
+
}
|
|
5925
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
5926
|
+
node = ancestors[i];
|
|
5927
|
+
if (runningTransition) {
|
|
5928
|
+
let top = node, prev = ancestors[i + 1];
|
|
5929
|
+
while ((top = top.owner) && top !== prev) if (Transition.disposed.has(top)) return;
|
|
5930
|
+
}
|
|
5931
|
+
if ((runningTransition ? node.tState : node.state) === STALE) updateComputation(node);
|
|
5932
|
+
else if ((runningTransition ? node.tState : node.state) === PENDING) {
|
|
5933
|
+
const updates = Updates;
|
|
5934
|
+
Updates = null;
|
|
5935
|
+
runUpdates(() => lookUpstream(node, ancestors[0]), false);
|
|
5936
|
+
Updates = updates;
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
6433
5939
|
}
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
5940
|
+
function runUpdates(fn, init) {
|
|
5941
|
+
if (Updates) return fn();
|
|
5942
|
+
let wait = false;
|
|
5943
|
+
if (!init) Updates = [];
|
|
5944
|
+
if (Effects) wait = true;
|
|
5945
|
+
else Effects = [];
|
|
5946
|
+
ExecCount++;
|
|
5947
|
+
try {
|
|
5948
|
+
const res = fn();
|
|
5949
|
+
completeUpdates(wait);
|
|
5950
|
+
return res;
|
|
5951
|
+
} catch (err) {
|
|
5952
|
+
if (!wait) Effects = null;
|
|
5953
|
+
Updates = null;
|
|
5954
|
+
handleError(err);
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5957
|
+
function completeUpdates(wait) {
|
|
5958
|
+
if (Updates) {
|
|
5959
|
+
if (Scheduler && Transition && Transition.running) scheduleQueue(Updates);
|
|
5960
|
+
else runQueue(Updates);
|
|
5961
|
+
Updates = null;
|
|
5962
|
+
}
|
|
5963
|
+
if (wait) return;
|
|
5964
|
+
let res;
|
|
5965
|
+
if (Transition) {
|
|
5966
|
+
if (!Transition.promises.size && !Transition.queue.size) {
|
|
5967
|
+
const sources = Transition.sources;
|
|
5968
|
+
const disposed = Transition.disposed;
|
|
5969
|
+
Effects.push.apply(Effects, Transition.effects);
|
|
5970
|
+
res = Transition.resolve;
|
|
5971
|
+
for (const e of Effects) {
|
|
5972
|
+
"tState" in e && (e.state = e.tState);
|
|
5973
|
+
delete e.tState;
|
|
5974
|
+
}
|
|
5975
|
+
Transition = null;
|
|
5976
|
+
runUpdates(() => {
|
|
5977
|
+
for (const d of disposed) cleanNode(d);
|
|
5978
|
+
for (const v of sources) {
|
|
5979
|
+
v.value = v.tValue;
|
|
5980
|
+
if (v.owned) for (let i = 0, len = v.owned.length; i < len; i++) cleanNode(v.owned[i]);
|
|
5981
|
+
if (v.tOwned) v.owned = v.tOwned;
|
|
5982
|
+
delete v.tValue;
|
|
5983
|
+
delete v.tOwned;
|
|
5984
|
+
v.tState = 0;
|
|
5985
|
+
}
|
|
5986
|
+
setTransPending(false);
|
|
5987
|
+
}, false);
|
|
5988
|
+
} else if (Transition.running) {
|
|
5989
|
+
Transition.running = false;
|
|
5990
|
+
Transition.effects.push.apply(Transition.effects, Effects);
|
|
5991
|
+
Effects = null;
|
|
5992
|
+
setTransPending(true);
|
|
5993
|
+
return;
|
|
5994
|
+
}
|
|
5995
|
+
}
|
|
5996
|
+
const e = Effects;
|
|
5997
|
+
Effects = null;
|
|
5998
|
+
if (e.length) runUpdates(() => runEffects(e), false);
|
|
5999
|
+
if (res) res();
|
|
6000
|
+
}
|
|
6001
|
+
function runQueue(queue) {
|
|
6002
|
+
for (let i = 0; i < queue.length; i++) runTop(queue[i]);
|
|
6003
|
+
}
|
|
6004
|
+
function scheduleQueue(queue) {
|
|
6005
|
+
for (let i = 0; i < queue.length; i++) {
|
|
6006
|
+
const item = queue[i];
|
|
6007
|
+
const tasks = Transition.queue;
|
|
6008
|
+
if (!tasks.has(item)) {
|
|
6009
|
+
tasks.add(item);
|
|
6010
|
+
Scheduler(() => {
|
|
6011
|
+
tasks.delete(item);
|
|
6012
|
+
runUpdates(() => {
|
|
6013
|
+
Transition.running = true;
|
|
6014
|
+
runTop(item);
|
|
6015
|
+
}, false);
|
|
6016
|
+
Transition && (Transition.running = false);
|
|
6017
|
+
});
|
|
6018
|
+
}
|
|
6019
|
+
}
|
|
6438
6020
|
}
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6021
|
+
function runUserEffects(queue) {
|
|
6022
|
+
let i, userLength = 0;
|
|
6023
|
+
for (i = 0; i < queue.length; i++) {
|
|
6024
|
+
const e = queue[i];
|
|
6025
|
+
if (!e.user) runTop(e);
|
|
6026
|
+
else queue[userLength++] = e;
|
|
6027
|
+
}
|
|
6028
|
+
if (sharedConfig.context) {
|
|
6029
|
+
if (sharedConfig.count) {
|
|
6030
|
+
sharedConfig.effects || (sharedConfig.effects = []);
|
|
6031
|
+
sharedConfig.effects.push(...queue.slice(0, userLength));
|
|
6032
|
+
return;
|
|
6033
|
+
}
|
|
6034
|
+
setHydrateContext();
|
|
6035
|
+
}
|
|
6036
|
+
if (sharedConfig.effects && (sharedConfig.done || !sharedConfig.count)) {
|
|
6037
|
+
queue = [...sharedConfig.effects, ...queue];
|
|
6038
|
+
userLength += sharedConfig.effects.length;
|
|
6039
|
+
delete sharedConfig.effects;
|
|
6040
|
+
}
|
|
6041
|
+
for (i = 0; i < userLength; i++) runTop(queue[i]);
|
|
6042
|
+
}
|
|
6043
|
+
function lookUpstream(node, ignore) {
|
|
6044
|
+
const runningTransition = Transition && Transition.running;
|
|
6045
|
+
if (runningTransition) node.tState = 0;
|
|
6046
|
+
else node.state = 0;
|
|
6047
|
+
for (let i = 0; i < node.sources.length; i += 1) {
|
|
6048
|
+
const source = node.sources[i];
|
|
6049
|
+
if (source.sources) {
|
|
6050
|
+
const state = runningTransition ? source.tState : source.state;
|
|
6051
|
+
if (state === STALE) {
|
|
6052
|
+
if (source !== ignore && (!source.updatedAt || source.updatedAt < ExecCount)) runTop(source);
|
|
6053
|
+
} else if (state === PENDING) lookUpstream(source, ignore);
|
|
6054
|
+
}
|
|
6055
|
+
}
|
|
6443
6056
|
}
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6057
|
+
function markDownstream(node) {
|
|
6058
|
+
const runningTransition = Transition && Transition.running;
|
|
6059
|
+
for (let i = 0; i < node.observers.length; i += 1) {
|
|
6060
|
+
const o = node.observers[i];
|
|
6061
|
+
if (runningTransition ? !o.tState : !o.state) {
|
|
6062
|
+
if (runningTransition) o.tState = PENDING;
|
|
6063
|
+
else o.state = PENDING;
|
|
6064
|
+
if (o.pure) Updates.push(o);
|
|
6065
|
+
else Effects.push(o);
|
|
6066
|
+
o.observers && markDownstream(o);
|
|
6067
|
+
}
|
|
6068
|
+
}
|
|
6450
6069
|
}
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6070
|
+
function cleanNode(node) {
|
|
6071
|
+
let i;
|
|
6072
|
+
if (node.sources) while (node.sources.length) {
|
|
6073
|
+
const source = node.sources.pop(), index = node.sourceSlots.pop(), obs = source.observers;
|
|
6074
|
+
if (obs && obs.length) {
|
|
6075
|
+
const n = obs.pop(), s = source.observerSlots.pop();
|
|
6076
|
+
if (index < obs.length) {
|
|
6077
|
+
n.sourceSlots[s] = index;
|
|
6078
|
+
obs[index] = n;
|
|
6079
|
+
source.observerSlots[index] = s;
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
}
|
|
6083
|
+
if (node.tOwned) {
|
|
6084
|
+
for (i = node.tOwned.length - 1; i >= 0; i--) cleanNode(node.tOwned[i]);
|
|
6085
|
+
delete node.tOwned;
|
|
6086
|
+
}
|
|
6087
|
+
if (Transition && Transition.running && node.pure) reset(node, true);
|
|
6088
|
+
else if (node.owned) {
|
|
6089
|
+
for (i = node.owned.length - 1; i >= 0; i--) cleanNode(node.owned[i]);
|
|
6090
|
+
node.owned = null;
|
|
6091
|
+
}
|
|
6092
|
+
if (node.cleanups) {
|
|
6093
|
+
for (i = node.cleanups.length - 1; i >= 0; i--) node.cleanups[i]();
|
|
6094
|
+
node.cleanups = null;
|
|
6095
|
+
}
|
|
6096
|
+
if (Transition && Transition.running) node.tState = 0;
|
|
6097
|
+
else node.state = 0;
|
|
6456
6098
|
}
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6099
|
+
function reset(node, top) {
|
|
6100
|
+
if (!top) {
|
|
6101
|
+
node.tState = 0;
|
|
6102
|
+
Transition.disposed.add(node);
|
|
6103
|
+
}
|
|
6104
|
+
if (node.owned) for (let i = 0; i < node.owned.length; i++) reset(node.owned[i]);
|
|
6461
6105
|
}
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
display: grid;
|
|
6466
|
-
gap: 4px;
|
|
6467
|
-
text-align: left;
|
|
6468
|
-
padding: 12px 14px;
|
|
6469
|
-
border: 1px solid transparent;
|
|
6470
|
-
border-radius: 16px;
|
|
6471
|
-
background: rgba(255, 255, 255, 0.72);
|
|
6472
|
-
color: inherit;
|
|
6473
|
-
cursor: pointer;
|
|
6474
|
-
transition: transform 140ms ease, border-color 140ms ease, background 140ms ease;
|
|
6106
|
+
function castError(err) {
|
|
6107
|
+
if (err instanceof Error) return err;
|
|
6108
|
+
return new Error(typeof err === "string" ? err : "Unknown error", { cause: err });
|
|
6475
6109
|
}
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6110
|
+
function runErrors(err, fns, owner) {
|
|
6111
|
+
try {
|
|
6112
|
+
for (const f of fns) f(err);
|
|
6113
|
+
} catch (e) {
|
|
6114
|
+
handleError(e, owner && owner.owner || null);
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
6117
|
+
function handleError(err, owner = Owner) {
|
|
6118
|
+
const fns = ERROR && owner && owner.context && owner.context[ERROR];
|
|
6119
|
+
const error = castError(err);
|
|
6120
|
+
if (!fns) throw error;
|
|
6121
|
+
if (Effects) Effects.push({
|
|
6122
|
+
fn() {
|
|
6123
|
+
runErrors(error, fns, owner);
|
|
6124
|
+
},
|
|
6125
|
+
state: STALE
|
|
6126
|
+
});
|
|
6127
|
+
else runErrors(error, fns, owner);
|
|
6128
|
+
}
|
|
6129
|
+
var FALLBACK = Symbol("fallback");
|
|
6130
|
+
function dispose(d) {
|
|
6131
|
+
for (let i = 0; i < d.length; i++) d[i]();
|
|
6132
|
+
}
|
|
6133
|
+
function mapArray(list, mapFn, options = {}) {
|
|
6134
|
+
let items = [], mapped = [], disposers = [], len = 0, indexes = mapFn.length > 1 ? [] : null;
|
|
6135
|
+
onCleanup(() => dispose(disposers));
|
|
6136
|
+
return () => {
|
|
6137
|
+
let newItems = list() || [], newLen = newItems.length, i, j;
|
|
6138
|
+
newItems[$TRACK];
|
|
6139
|
+
return untrack(() => {
|
|
6140
|
+
let newIndices, newIndicesNext, temp, tempdisposers, tempIndexes, start, end, newEnd, item;
|
|
6141
|
+
if (newLen === 0) {
|
|
6142
|
+
if (len !== 0) {
|
|
6143
|
+
dispose(disposers);
|
|
6144
|
+
disposers = [];
|
|
6145
|
+
items = [];
|
|
6146
|
+
mapped = [];
|
|
6147
|
+
len = 0;
|
|
6148
|
+
indexes && (indexes = []);
|
|
6149
|
+
}
|
|
6150
|
+
if (options.fallback) {
|
|
6151
|
+
items = [FALLBACK];
|
|
6152
|
+
mapped[0] = createRoot((disposer) => {
|
|
6153
|
+
disposers[0] = disposer;
|
|
6154
|
+
return options.fallback();
|
|
6155
|
+
});
|
|
6156
|
+
len = 1;
|
|
6157
|
+
}
|
|
6158
|
+
} else if (len === 0) {
|
|
6159
|
+
mapped = new Array(newLen);
|
|
6160
|
+
for (j = 0; j < newLen; j++) {
|
|
6161
|
+
items[j] = newItems[j];
|
|
6162
|
+
mapped[j] = createRoot(mapper);
|
|
6163
|
+
}
|
|
6164
|
+
len = newLen;
|
|
6165
|
+
} else {
|
|
6166
|
+
temp = new Array(newLen);
|
|
6167
|
+
tempdisposers = new Array(newLen);
|
|
6168
|
+
indexes && (tempIndexes = new Array(newLen));
|
|
6169
|
+
for (start = 0, end = Math.min(len, newLen); start < end && items[start] === newItems[start]; start++);
|
|
6170
|
+
for (end = len - 1, newEnd = newLen - 1; end >= start && newEnd >= start && items[end] === newItems[newEnd]; end--, newEnd--) {
|
|
6171
|
+
temp[newEnd] = mapped[end];
|
|
6172
|
+
tempdisposers[newEnd] = disposers[end];
|
|
6173
|
+
indexes && (tempIndexes[newEnd] = indexes[end]);
|
|
6174
|
+
}
|
|
6175
|
+
newIndices = /* @__PURE__ */ new Map();
|
|
6176
|
+
newIndicesNext = new Array(newEnd + 1);
|
|
6177
|
+
for (j = newEnd; j >= start; j--) {
|
|
6178
|
+
item = newItems[j];
|
|
6179
|
+
i = newIndices.get(item);
|
|
6180
|
+
newIndicesNext[j] = i === void 0 ? -1 : i;
|
|
6181
|
+
newIndices.set(item, j);
|
|
6182
|
+
}
|
|
6183
|
+
for (i = start; i <= end; i++) {
|
|
6184
|
+
item = items[i];
|
|
6185
|
+
j = newIndices.get(item);
|
|
6186
|
+
if (j !== void 0 && j !== -1) {
|
|
6187
|
+
temp[j] = mapped[i];
|
|
6188
|
+
tempdisposers[j] = disposers[i];
|
|
6189
|
+
indexes && (tempIndexes[j] = indexes[i]);
|
|
6190
|
+
j = newIndicesNext[j];
|
|
6191
|
+
newIndices.set(item, j);
|
|
6192
|
+
} else disposers[i]();
|
|
6193
|
+
}
|
|
6194
|
+
for (j = start; j < newLen; j++) if (j in temp) {
|
|
6195
|
+
mapped[j] = temp[j];
|
|
6196
|
+
disposers[j] = tempdisposers[j];
|
|
6197
|
+
if (indexes) {
|
|
6198
|
+
indexes[j] = tempIndexes[j];
|
|
6199
|
+
indexes[j](j);
|
|
6200
|
+
}
|
|
6201
|
+
} else mapped[j] = createRoot(mapper);
|
|
6202
|
+
mapped = mapped.slice(0, len = newLen);
|
|
6203
|
+
items = newItems.slice(0);
|
|
6204
|
+
}
|
|
6205
|
+
return mapped;
|
|
6206
|
+
});
|
|
6207
|
+
function mapper(disposer) {
|
|
6208
|
+
disposers[j] = disposer;
|
|
6209
|
+
if (indexes) {
|
|
6210
|
+
const [s, set] = createSignal(j);
|
|
6211
|
+
indexes[j] = set;
|
|
6212
|
+
return mapFn(newItems[j], s);
|
|
6213
|
+
}
|
|
6214
|
+
return mapFn(newItems[j]);
|
|
6215
|
+
}
|
|
6216
|
+
};
|
|
6482
6217
|
}
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6218
|
+
var hydrationEnabled = false;
|
|
6219
|
+
function createComponent(Comp, props) {
|
|
6220
|
+
if (hydrationEnabled) {
|
|
6221
|
+
if (sharedConfig.context) {
|
|
6222
|
+
const c = sharedConfig.context;
|
|
6223
|
+
setHydrateContext(nextHydrateContext());
|
|
6224
|
+
const r = untrack(() => Comp(props || {}));
|
|
6225
|
+
setHydrateContext(c);
|
|
6226
|
+
return r;
|
|
6227
|
+
}
|
|
6228
|
+
}
|
|
6229
|
+
return untrack(() => Comp(props || {}));
|
|
6230
|
+
}
|
|
6231
|
+
var narrowedError = (name) => \`Stale read from <\${name}>.\`;
|
|
6232
|
+
function For(props) {
|
|
6233
|
+
const fallback = "fallback" in props && { fallback: () => props.fallback };
|
|
6234
|
+
return createMemo(mapArray(() => props.each, props.children, fallback || void 0));
|
|
6235
|
+
}
|
|
6236
|
+
function Show(props) {
|
|
6237
|
+
const keyed = props.keyed;
|
|
6238
|
+
const conditionValue = createMemo(() => props.when, void 0, void 0);
|
|
6239
|
+
const condition = keyed ? conditionValue : createMemo(conditionValue, void 0, { equals: (a, b) => !a === !b });
|
|
6240
|
+
return createMemo(() => {
|
|
6241
|
+
const c = condition();
|
|
6242
|
+
if (c) {
|
|
6243
|
+
const child = props.children;
|
|
6244
|
+
return typeof child === "function" && child.length > 0 ? untrack(() => child(keyed ? c : () => {
|
|
6245
|
+
if (!untrack(condition)) throw narrowedError("Show");
|
|
6246
|
+
return conditionValue();
|
|
6247
|
+
})) : child;
|
|
6248
|
+
}
|
|
6249
|
+
return props.fallback;
|
|
6250
|
+
}, void 0, void 0);
|
|
6486
6251
|
}
|
|
6487
|
-
|
|
6488
|
-
.
|
|
6489
|
-
|
|
6490
|
-
|
|
6252
|
+
//#endregion
|
|
6253
|
+
//#region node_modules/solid-js/web/dist/web.js
|
|
6254
|
+
var memo = (fn) => createMemo(() => fn());
|
|
6255
|
+
function reconcileArrays(parentNode, a, b) {
|
|
6256
|
+
let bLength = b.length, aEnd = a.length, bEnd = bLength, aStart = 0, bStart = 0, after = a[aEnd - 1].nextSibling, map = null;
|
|
6257
|
+
while (aStart < aEnd || bStart < bEnd) {
|
|
6258
|
+
if (a[aStart] === b[bStart]) {
|
|
6259
|
+
aStart++;
|
|
6260
|
+
bStart++;
|
|
6261
|
+
continue;
|
|
6262
|
+
}
|
|
6263
|
+
while (a[aEnd - 1] === b[bEnd - 1]) {
|
|
6264
|
+
aEnd--;
|
|
6265
|
+
bEnd--;
|
|
6266
|
+
}
|
|
6267
|
+
if (aEnd === aStart) {
|
|
6268
|
+
const node = bEnd < bLength ? bStart ? b[bStart - 1].nextSibling : b[bEnd - bStart] : after;
|
|
6269
|
+
while (bStart < bEnd) parentNode.insertBefore(b[bStart++], node);
|
|
6270
|
+
} else if (bEnd === bStart) while (aStart < aEnd) {
|
|
6271
|
+
if (!map || !map.has(a[aStart])) a[aStart].remove();
|
|
6272
|
+
aStart++;
|
|
6273
|
+
}
|
|
6274
|
+
else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {
|
|
6275
|
+
const node = a[--aEnd].nextSibling;
|
|
6276
|
+
parentNode.insertBefore(b[bStart++], a[aStart++].nextSibling);
|
|
6277
|
+
parentNode.insertBefore(b[--bEnd], node);
|
|
6278
|
+
a[aEnd] = b[bEnd];
|
|
6279
|
+
} else {
|
|
6280
|
+
if (!map) {
|
|
6281
|
+
map = /* @__PURE__ */ new Map();
|
|
6282
|
+
let i = bStart;
|
|
6283
|
+
while (i < bEnd) map.set(b[i], i++);
|
|
6284
|
+
}
|
|
6285
|
+
const index = map.get(a[aStart]);
|
|
6286
|
+
if (index != null) if (bStart < index && index < bEnd) {
|
|
6287
|
+
let i = aStart, sequence = 1, t;
|
|
6288
|
+
while (++i < aEnd && i < bEnd) {
|
|
6289
|
+
if ((t = map.get(a[i])) == null || t !== index + sequence) break;
|
|
6290
|
+
sequence++;
|
|
6291
|
+
}
|
|
6292
|
+
if (sequence > index - bStart) {
|
|
6293
|
+
const node = a[aStart];
|
|
6294
|
+
while (bStart < index) parentNode.insertBefore(b[bStart++], node);
|
|
6295
|
+
} else parentNode.replaceChild(b[bStart++], a[aStart++]);
|
|
6296
|
+
} else aStart++;
|
|
6297
|
+
else a[aStart++].remove();
|
|
6298
|
+
}
|
|
6299
|
+
}
|
|
6491
6300
|
}
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6301
|
+
var $$EVENTS = "_$DX_DELEGATE";
|
|
6302
|
+
function render(code, element, init, options = {}) {
|
|
6303
|
+
let disposer;
|
|
6304
|
+
createRoot((dispose) => {
|
|
6305
|
+
disposer = dispose;
|
|
6306
|
+
element === document ? code() : insert(element, code(), element.firstChild ? null : void 0, init);
|
|
6307
|
+
}, options.owner);
|
|
6308
|
+
return () => {
|
|
6309
|
+
disposer();
|
|
6310
|
+
element.textContent = "";
|
|
6311
|
+
};
|
|
6496
6312
|
}
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6313
|
+
function template(html, isImportNode, isSVG, isMathML) {
|
|
6314
|
+
let node;
|
|
6315
|
+
const create = () => {
|
|
6316
|
+
const t = isMathML ? document.createElementNS("http://www.w3.org/1998/Math/MathML", "template") : document.createElement("template");
|
|
6317
|
+
t.innerHTML = html;
|
|
6318
|
+
return isSVG ? t.content.firstChild.firstChild : isMathML ? t.firstChild : t.content.firstChild;
|
|
6319
|
+
};
|
|
6320
|
+
const fn = isImportNode ? () => untrack(() => document.importNode(node || (node = create()), true)) : () => (node || (node = create())).cloneNode(true);
|
|
6321
|
+
fn.cloneNode = fn;
|
|
6322
|
+
return fn;
|
|
6323
|
+
}
|
|
6324
|
+
function delegateEvents(eventNames, document = window.document) {
|
|
6325
|
+
const e = document[$$EVENTS] || (document[$$EVENTS] = /* @__PURE__ */ new Set());
|
|
6326
|
+
for (let i = 0, l = eventNames.length; i < l; i++) {
|
|
6327
|
+
const name = eventNames[i];
|
|
6328
|
+
if (!e.has(name)) {
|
|
6329
|
+
e.add(name);
|
|
6330
|
+
document.addEventListener(name, eventHandler);
|
|
6331
|
+
}
|
|
6332
|
+
}
|
|
6500
6333
|
}
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
}
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6334
|
+
function setAttribute(node, name, value) {
|
|
6335
|
+
if (isHydrating(node)) return;
|
|
6336
|
+
if (value == null) node.removeAttribute(name);
|
|
6337
|
+
else node.setAttribute(name, value);
|
|
6338
|
+
}
|
|
6339
|
+
function addEventListener(node, name, handler, delegate) {
|
|
6340
|
+
if (delegate) if (Array.isArray(handler)) {
|
|
6341
|
+
node[\`$$\${name}\`] = handler[0];
|
|
6342
|
+
node[\`$$\${name}Data\`] = handler[1];
|
|
6343
|
+
} else node[\`$$\${name}\`] = handler;
|
|
6344
|
+
else if (Array.isArray(handler)) {
|
|
6345
|
+
const handlerFn = handler[0];
|
|
6346
|
+
node.addEventListener(name, handler[0] = (e) => handlerFn.call(node, handler[1], e));
|
|
6347
|
+
} else node.addEventListener(name, handler, typeof handler !== "function" && handler);
|
|
6348
|
+
}
|
|
6349
|
+
function insert(parent, accessor, marker, initial) {
|
|
6350
|
+
if (marker !== void 0 && !initial) initial = [];
|
|
6351
|
+
if (typeof accessor !== "function") return insertExpression(parent, accessor, initial, marker);
|
|
6352
|
+
createRenderEffect((current) => insertExpression(parent, accessor(), current, marker), initial);
|
|
6353
|
+
}
|
|
6354
|
+
function isHydrating(node) {
|
|
6355
|
+
return !!sharedConfig.context && !sharedConfig.done && (!node || node.isConnected);
|
|
6356
|
+
}
|
|
6357
|
+
function eventHandler(e) {
|
|
6358
|
+
if (sharedConfig.registry && sharedConfig.events) {
|
|
6359
|
+
if (sharedConfig.events.find(([el, ev]) => ev === e)) return;
|
|
6360
|
+
}
|
|
6361
|
+
let node = e.target;
|
|
6362
|
+
const key = \`$$\${e.type}\`;
|
|
6363
|
+
const oriTarget = e.target;
|
|
6364
|
+
const oriCurrentTarget = e.currentTarget;
|
|
6365
|
+
const retarget = (value) => Object.defineProperty(e, "target", {
|
|
6366
|
+
configurable: true,
|
|
6367
|
+
value
|
|
6368
|
+
});
|
|
6369
|
+
const handleNode = () => {
|
|
6370
|
+
const handler = node[key];
|
|
6371
|
+
if (handler && !node.disabled) {
|
|
6372
|
+
const data = node[\`\${key}Data\`];
|
|
6373
|
+
data !== void 0 ? handler.call(node, data, e) : handler.call(node, e);
|
|
6374
|
+
if (e.cancelBubble) return;
|
|
6375
|
+
}
|
|
6376
|
+
node.host && typeof node.host !== "string" && !node.host._$host && node.contains(e.target) && retarget(node.host);
|
|
6377
|
+
return true;
|
|
6378
|
+
};
|
|
6379
|
+
const walkUpTree = () => {
|
|
6380
|
+
while (handleNode() && (node = node._$host || node.parentNode || node.host));
|
|
6381
|
+
};
|
|
6382
|
+
Object.defineProperty(e, "currentTarget", {
|
|
6383
|
+
configurable: true,
|
|
6384
|
+
get() {
|
|
6385
|
+
return node || document;
|
|
6386
|
+
}
|
|
6387
|
+
});
|
|
6388
|
+
if (sharedConfig.registry && !sharedConfig.done) sharedConfig.done = _$HY.done = true;
|
|
6389
|
+
if (e.composedPath) {
|
|
6390
|
+
const path = e.composedPath();
|
|
6391
|
+
retarget(path[0]);
|
|
6392
|
+
for (let i = 0; i < path.length - 2; i++) {
|
|
6393
|
+
node = path[i];
|
|
6394
|
+
if (!handleNode()) break;
|
|
6395
|
+
if (node._$host) {
|
|
6396
|
+
node = node._$host;
|
|
6397
|
+
walkUpTree();
|
|
6398
|
+
break;
|
|
6399
|
+
}
|
|
6400
|
+
if (node.parentNode === oriCurrentTarget) break;
|
|
6401
|
+
}
|
|
6402
|
+
} else walkUpTree();
|
|
6403
|
+
retarget(oriTarget);
|
|
6404
|
+
}
|
|
6405
|
+
function insertExpression(parent, value, current, marker, unwrapArray) {
|
|
6406
|
+
const hydrating = isHydrating(parent);
|
|
6407
|
+
if (hydrating) {
|
|
6408
|
+
!current && (current = [...parent.childNodes]);
|
|
6409
|
+
let cleaned = [];
|
|
6410
|
+
for (let i = 0; i < current.length; i++) {
|
|
6411
|
+
const node = current[i];
|
|
6412
|
+
if (node.nodeType === 8 && node.data.slice(0, 2) === "!$") node.remove();
|
|
6413
|
+
else cleaned.push(node);
|
|
6414
|
+
}
|
|
6415
|
+
current = cleaned;
|
|
6416
|
+
}
|
|
6417
|
+
while (typeof current === "function") current = current();
|
|
6418
|
+
if (value === current) return current;
|
|
6419
|
+
const t = typeof value, multi = marker !== void 0;
|
|
6420
|
+
parent = multi && current[0] && current[0].parentNode || parent;
|
|
6421
|
+
if (t === "string" || t === "number") {
|
|
6422
|
+
if (hydrating) return current;
|
|
6423
|
+
if (t === "number") {
|
|
6424
|
+
value = value.toString();
|
|
6425
|
+
if (value === current) return current;
|
|
6426
|
+
}
|
|
6427
|
+
if (multi) {
|
|
6428
|
+
let node = current[0];
|
|
6429
|
+
if (node && node.nodeType === 3) node.data !== value && (node.data = value);
|
|
6430
|
+
else node = document.createTextNode(value);
|
|
6431
|
+
current = cleanChildren(parent, current, marker, node);
|
|
6432
|
+
} else if (current !== "" && typeof current === "string") current = parent.firstChild.data = value;
|
|
6433
|
+
else current = parent.textContent = value;
|
|
6434
|
+
} else if (value == null || t === "boolean") {
|
|
6435
|
+
if (hydrating) return current;
|
|
6436
|
+
current = cleanChildren(parent, current, marker);
|
|
6437
|
+
} else if (t === "function") {
|
|
6438
|
+
createRenderEffect(() => {
|
|
6439
|
+
let v = value();
|
|
6440
|
+
while (typeof v === "function") v = v();
|
|
6441
|
+
current = insertExpression(parent, v, current, marker);
|
|
6442
|
+
});
|
|
6443
|
+
return () => current;
|
|
6444
|
+
} else if (Array.isArray(value)) {
|
|
6445
|
+
const array = [];
|
|
6446
|
+
const currentArray = current && Array.isArray(current);
|
|
6447
|
+
if (normalizeIncomingArray(array, value, current, unwrapArray)) {
|
|
6448
|
+
createRenderEffect(() => current = insertExpression(parent, array, current, marker, true));
|
|
6449
|
+
return () => current;
|
|
6450
|
+
}
|
|
6451
|
+
if (hydrating) {
|
|
6452
|
+
if (!array.length) return current;
|
|
6453
|
+
if (marker === void 0) return current = [...parent.childNodes];
|
|
6454
|
+
let node = array[0];
|
|
6455
|
+
if (node.parentNode !== parent) return current;
|
|
6456
|
+
const nodes = [node];
|
|
6457
|
+
while ((node = node.nextSibling) !== marker) nodes.push(node);
|
|
6458
|
+
return current = nodes;
|
|
6459
|
+
}
|
|
6460
|
+
if (array.length === 0) {
|
|
6461
|
+
current = cleanChildren(parent, current, marker);
|
|
6462
|
+
if (multi) return current;
|
|
6463
|
+
} else if (currentArray) if (current.length === 0) appendNodes(parent, array, marker);
|
|
6464
|
+
else reconcileArrays(parent, current, array);
|
|
6465
|
+
else {
|
|
6466
|
+
current && cleanChildren(parent);
|
|
6467
|
+
appendNodes(parent, array);
|
|
6468
|
+
}
|
|
6469
|
+
current = array;
|
|
6470
|
+
} else if (value.nodeType) {
|
|
6471
|
+
if (hydrating && value.parentNode) return current = multi ? [value] : value;
|
|
6472
|
+
if (Array.isArray(current)) {
|
|
6473
|
+
if (multi) return current = cleanChildren(parent, current, marker, value);
|
|
6474
|
+
cleanChildren(parent, current, null, value);
|
|
6475
|
+
} else if (current == null || current === "" || !parent.firstChild) parent.appendChild(value);
|
|
6476
|
+
else parent.replaceChild(value, parent.firstChild);
|
|
6477
|
+
current = value;
|
|
6478
|
+
}
|
|
6479
|
+
return current;
|
|
6480
|
+
}
|
|
6481
|
+
function normalizeIncomingArray(normalized, array, current, unwrap) {
|
|
6482
|
+
let dynamic = false;
|
|
6483
|
+
for (let i = 0, len = array.length; i < len; i++) {
|
|
6484
|
+
let item = array[i], prev = current && current[normalized.length], t;
|
|
6485
|
+
if (item == null || item === true || item === false);
|
|
6486
|
+
else if ((t = typeof item) === "object" && item.nodeType) normalized.push(item);
|
|
6487
|
+
else if (Array.isArray(item)) dynamic = normalizeIncomingArray(normalized, item, prev) || dynamic;
|
|
6488
|
+
else if (t === "function") if (unwrap) {
|
|
6489
|
+
while (typeof item === "function") item = item();
|
|
6490
|
+
dynamic = normalizeIncomingArray(normalized, Array.isArray(item) ? item : [item], Array.isArray(prev) ? prev : [prev]) || dynamic;
|
|
6491
|
+
} else {
|
|
6492
|
+
normalized.push(item);
|
|
6493
|
+
dynamic = true;
|
|
6494
|
+
}
|
|
6495
|
+
else {
|
|
6496
|
+
const value = String(item);
|
|
6497
|
+
if (prev && prev.nodeType === 3 && prev.data === value) normalized.push(prev);
|
|
6498
|
+
else normalized.push(document.createTextNode(value));
|
|
6499
|
+
}
|
|
6500
|
+
}
|
|
6501
|
+
return dynamic;
|
|
6502
|
+
}
|
|
6503
|
+
function appendNodes(parent, array, marker = null) {
|
|
6504
|
+
for (let i = 0, len = array.length; i < len; i++) parent.insertBefore(array[i], marker);
|
|
6505
|
+
}
|
|
6506
|
+
function cleanChildren(parent, current, marker, replacement) {
|
|
6507
|
+
if (marker === void 0) return parent.textContent = "";
|
|
6508
|
+
const node = replacement || document.createTextNode("");
|
|
6509
|
+
if (current.length) {
|
|
6510
|
+
let inserted = false;
|
|
6511
|
+
for (let i = current.length - 1; i >= 0; i--) {
|
|
6512
|
+
const el = current[i];
|
|
6513
|
+
if (node !== el) {
|
|
6514
|
+
const isParent = el.parentNode === parent;
|
|
6515
|
+
if (!inserted && !i) isParent ? parent.replaceChild(node, el) : parent.insertBefore(node, marker);
|
|
6516
|
+
else isParent && el.remove();
|
|
6517
|
+
} else inserted = true;
|
|
6518
|
+
}
|
|
6519
|
+
} else parent.insertBefore(node, marker);
|
|
6520
|
+
return [node];
|
|
6526
6521
|
}
|
|
6527
|
-
|
|
6528
|
-
.
|
|
6529
|
-
|
|
6530
|
-
|
|
6522
|
+
//#endregion
|
|
6523
|
+
//#region node_modules/solid-js/store/dist/store.js
|
|
6524
|
+
var $RAW = Symbol("store-raw"), $NODE = Symbol("store-node"), $HAS = Symbol("store-has"), $SELF = Symbol("store-self");
|
|
6525
|
+
function wrap$1(value) {
|
|
6526
|
+
let p = value[$PROXY];
|
|
6527
|
+
if (!p) {
|
|
6528
|
+
Object.defineProperty(value, $PROXY, { value: p = new Proxy(value, proxyTraps$1) });
|
|
6529
|
+
if (!Array.isArray(value)) {
|
|
6530
|
+
const keys = Object.keys(value), desc = Object.getOwnPropertyDescriptors(value);
|
|
6531
|
+
for (let i = 0, l = keys.length; i < l; i++) {
|
|
6532
|
+
const prop = keys[i];
|
|
6533
|
+
if (desc[prop].get) Object.defineProperty(value, prop, {
|
|
6534
|
+
enumerable: desc[prop].enumerable,
|
|
6535
|
+
get: desc[prop].get.bind(p)
|
|
6536
|
+
});
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
}
|
|
6540
|
+
return p;
|
|
6541
|
+
}
|
|
6542
|
+
function isWrappable(obj) {
|
|
6543
|
+
let proto;
|
|
6544
|
+
return obj != null && typeof obj === "object" && (obj[$PROXY] || !(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype || Array.isArray(obj));
|
|
6545
|
+
}
|
|
6546
|
+
function unwrap(item, set = /* @__PURE__ */ new Set()) {
|
|
6547
|
+
let result, unwrapped, v, prop;
|
|
6548
|
+
if (result = item != null && item[$RAW]) return result;
|
|
6549
|
+
if (!isWrappable(item) || set.has(item)) return item;
|
|
6550
|
+
if (Array.isArray(item)) {
|
|
6551
|
+
if (Object.isFrozen(item)) item = item.slice(0);
|
|
6552
|
+
else set.add(item);
|
|
6553
|
+
for (let i = 0, l = item.length; i < l; i++) {
|
|
6554
|
+
v = item[i];
|
|
6555
|
+
if ((unwrapped = unwrap(v, set)) !== v) item[i] = unwrapped;
|
|
6556
|
+
}
|
|
6557
|
+
} else {
|
|
6558
|
+
if (Object.isFrozen(item)) item = Object.assign({}, item);
|
|
6559
|
+
else set.add(item);
|
|
6560
|
+
const keys = Object.keys(item), desc = Object.getOwnPropertyDescriptors(item);
|
|
6561
|
+
for (let i = 0, l = keys.length; i < l; i++) {
|
|
6562
|
+
prop = keys[i];
|
|
6563
|
+
if (desc[prop].get) continue;
|
|
6564
|
+
v = item[prop];
|
|
6565
|
+
if ((unwrapped = unwrap(v, set)) !== v) item[prop] = unwrapped;
|
|
6566
|
+
}
|
|
6567
|
+
}
|
|
6568
|
+
return item;
|
|
6531
6569
|
}
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6570
|
+
function getNodes(target, symbol) {
|
|
6571
|
+
let nodes = target[symbol];
|
|
6572
|
+
if (!nodes) Object.defineProperty(target, symbol, { value: nodes = Object.create(null) });
|
|
6573
|
+
return nodes;
|
|
6536
6574
|
}
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6575
|
+
function getNode(nodes, property, value) {
|
|
6576
|
+
if (nodes[property]) return nodes[property];
|
|
6577
|
+
const [s, set] = createSignal(value, {
|
|
6578
|
+
equals: false,
|
|
6579
|
+
internal: true
|
|
6580
|
+
});
|
|
6581
|
+
s.$ = set;
|
|
6582
|
+
return nodes[property] = s;
|
|
6583
|
+
}
|
|
6584
|
+
function proxyDescriptor$1(target, property) {
|
|
6585
|
+
const desc = Reflect.getOwnPropertyDescriptor(target, property);
|
|
6586
|
+
if (!desc || desc.get || !desc.configurable || property === $PROXY || property === $NODE) return desc;
|
|
6587
|
+
delete desc.value;
|
|
6588
|
+
delete desc.writable;
|
|
6589
|
+
desc.get = () => target[$PROXY][property];
|
|
6590
|
+
return desc;
|
|
6591
|
+
}
|
|
6592
|
+
function trackSelf(target) {
|
|
6593
|
+
getListener() && getNode(getNodes(target, $NODE), $SELF)();
|
|
6594
|
+
}
|
|
6595
|
+
function ownKeys(target) {
|
|
6596
|
+
trackSelf(target);
|
|
6597
|
+
return Reflect.ownKeys(target);
|
|
6598
|
+
}
|
|
6599
|
+
var proxyTraps$1 = {
|
|
6600
|
+
get(target, property, receiver) {
|
|
6601
|
+
if (property === $RAW) return target;
|
|
6602
|
+
if (property === $PROXY) return receiver;
|
|
6603
|
+
if (property === $TRACK) {
|
|
6604
|
+
trackSelf(target);
|
|
6605
|
+
return receiver;
|
|
6606
|
+
}
|
|
6607
|
+
const nodes = getNodes(target, $NODE);
|
|
6608
|
+
const tracked = nodes[property];
|
|
6609
|
+
let value = tracked ? tracked() : target[property];
|
|
6610
|
+
if (property === $NODE || property === $HAS || property === "__proto__") return value;
|
|
6611
|
+
if (!tracked) {
|
|
6612
|
+
const desc = Object.getOwnPropertyDescriptor(target, property);
|
|
6613
|
+
if (getListener() && (typeof value !== "function" || target.hasOwnProperty(property)) && !(desc && desc.get)) value = getNode(nodes, property, value)();
|
|
6614
|
+
}
|
|
6615
|
+
return isWrappable(value) ? wrap$1(value) : value;
|
|
6616
|
+
},
|
|
6617
|
+
has(target, property) {
|
|
6618
|
+
if (property === $RAW || property === $PROXY || property === $TRACK || property === $NODE || property === $HAS || property === "__proto__") return true;
|
|
6619
|
+
getListener() && getNode(getNodes(target, $HAS), property)();
|
|
6620
|
+
return property in target;
|
|
6621
|
+
},
|
|
6622
|
+
set() {
|
|
6623
|
+
return true;
|
|
6624
|
+
},
|
|
6625
|
+
deleteProperty() {
|
|
6626
|
+
return true;
|
|
6627
|
+
},
|
|
6628
|
+
ownKeys,
|
|
6629
|
+
getOwnPropertyDescriptor: proxyDescriptor$1
|
|
6630
|
+
};
|
|
6631
|
+
function setProperty(state, property, value, deleting = false) {
|
|
6632
|
+
if (!deleting && state[property] === value) return;
|
|
6633
|
+
const prev = state[property], len = state.length;
|
|
6634
|
+
if (value === void 0) {
|
|
6635
|
+
delete state[property];
|
|
6636
|
+
if (state[$HAS] && state[$HAS][property] && prev !== void 0) state[$HAS][property].$();
|
|
6637
|
+
} else {
|
|
6638
|
+
state[property] = value;
|
|
6639
|
+
if (state[$HAS] && state[$HAS][property] && prev === void 0) state[$HAS][property].$();
|
|
6640
|
+
}
|
|
6641
|
+
let nodes = getNodes(state, $NODE), node;
|
|
6642
|
+
if (node = getNode(nodes, property, prev)) node.$(() => value);
|
|
6643
|
+
if (Array.isArray(state) && state.length !== len) {
|
|
6644
|
+
for (let i = state.length; i < len; i++) (node = nodes[i]) && node.$();
|
|
6645
|
+
(node = getNode(nodes, "length", len)) && node.$(state.length);
|
|
6646
|
+
}
|
|
6647
|
+
(node = nodes[$SELF]) && node.$();
|
|
6648
|
+
}
|
|
6649
|
+
function mergeStoreNode(state, value) {
|
|
6650
|
+
const keys = Object.keys(value);
|
|
6651
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
6652
|
+
const key = keys[i];
|
|
6653
|
+
setProperty(state, key, value[key]);
|
|
6654
|
+
}
|
|
6655
|
+
}
|
|
6656
|
+
function updateArray(current, next) {
|
|
6657
|
+
if (typeof next === "function") next = next(current);
|
|
6658
|
+
next = unwrap(next);
|
|
6659
|
+
if (Array.isArray(next)) {
|
|
6660
|
+
if (current === next) return;
|
|
6661
|
+
let i = 0, len = next.length;
|
|
6662
|
+
for (; i < len; i++) {
|
|
6663
|
+
const value = next[i];
|
|
6664
|
+
if (current[i] !== value) setProperty(current, i, value);
|
|
6665
|
+
}
|
|
6666
|
+
setProperty(current, "length", len);
|
|
6667
|
+
} else mergeStoreNode(current, next);
|
|
6668
|
+
}
|
|
6669
|
+
function updatePath(current, path, traversed = []) {
|
|
6670
|
+
let part, prev = current;
|
|
6671
|
+
if (path.length > 1) {
|
|
6672
|
+
part = path.shift();
|
|
6673
|
+
const partType = typeof part, isArray = Array.isArray(current);
|
|
6674
|
+
if (Array.isArray(part)) {
|
|
6675
|
+
for (let i = 0; i < part.length; i++) updatePath(current, [part[i]].concat(path), traversed);
|
|
6676
|
+
return;
|
|
6677
|
+
} else if (isArray && partType === "function") {
|
|
6678
|
+
for (let i = 0; i < current.length; i++) if (part(current[i], i)) updatePath(current, [i].concat(path), traversed);
|
|
6679
|
+
return;
|
|
6680
|
+
} else if (isArray && partType === "object") {
|
|
6681
|
+
const { from = 0, to = current.length - 1, by = 1 } = part;
|
|
6682
|
+
for (let i = from; i <= to; i += by) updatePath(current, [i].concat(path), traversed);
|
|
6683
|
+
return;
|
|
6684
|
+
} else if (path.length > 1) {
|
|
6685
|
+
updatePath(current[part], path, [part].concat(traversed));
|
|
6686
|
+
return;
|
|
6687
|
+
}
|
|
6688
|
+
prev = current[part];
|
|
6689
|
+
traversed = [part].concat(traversed);
|
|
6690
|
+
}
|
|
6691
|
+
let value = path[0];
|
|
6692
|
+
if (typeof value === "function") {
|
|
6693
|
+
value = value(prev, traversed);
|
|
6694
|
+
if (value === prev) return;
|
|
6695
|
+
}
|
|
6696
|
+
if (part === void 0 && value == void 0) return;
|
|
6697
|
+
value = unwrap(value);
|
|
6698
|
+
if (part === void 0 || isWrappable(prev) && isWrappable(value) && !Array.isArray(value)) mergeStoreNode(prev, value);
|
|
6699
|
+
else setProperty(current, part, value);
|
|
6700
|
+
}
|
|
6701
|
+
function createStore(...[store, options]) {
|
|
6702
|
+
const unwrappedStore = unwrap(store || {});
|
|
6703
|
+
const isArray = Array.isArray(unwrappedStore);
|
|
6704
|
+
const wrappedStore = wrap$1(unwrappedStore);
|
|
6705
|
+
function setStore(...args) {
|
|
6706
|
+
batch(() => {
|
|
6707
|
+
isArray && args.length === 1 ? updateArray(unwrappedStore, args[0]) : updatePath(unwrappedStore, args);
|
|
6708
|
+
});
|
|
6709
|
+
}
|
|
6710
|
+
return [wrappedStore, setStore];
|
|
6540
6711
|
}
|
|
6541
|
-
|
|
6542
|
-
.
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6712
|
+
//#endregion
|
|
6713
|
+
//#region src/transport.ts
|
|
6714
|
+
var granolaTransportPaths = {
|
|
6715
|
+
authLock: "/auth/lock",
|
|
6716
|
+
authLogin: "/auth/login",
|
|
6717
|
+
authLogout: "/auth/logout",
|
|
6718
|
+
authMode: "/auth/mode",
|
|
6719
|
+
authRefresh: "/auth/refresh",
|
|
6720
|
+
authStatus: "/auth/status",
|
|
6721
|
+
authUnlock: "/auth/unlock",
|
|
6722
|
+
events: "/events",
|
|
6723
|
+
exportJobs: "/exports/jobs",
|
|
6724
|
+
exportNotes: "/exports/notes",
|
|
6725
|
+
exportTranscripts: "/exports/transcripts",
|
|
6726
|
+
folderResolve: "/folders/resolve",
|
|
6727
|
+
folders: "/folders",
|
|
6728
|
+
health: "/health",
|
|
6729
|
+
meetingResolve: "/meetings/resolve",
|
|
6730
|
+
meetings: "/meetings",
|
|
6731
|
+
root: "/",
|
|
6732
|
+
serverInfo: "/server/info",
|
|
6733
|
+
syncRun: "/sync",
|
|
6734
|
+
syncEvents: "/sync/events",
|
|
6735
|
+
state: "/state"
|
|
6736
|
+
};
|
|
6737
|
+
function appendSearchParams(path, params) {
|
|
6738
|
+
const url = new URL(path, "http://localhost");
|
|
6739
|
+
for (const [key, value] of Object.entries(params)) {
|
|
6740
|
+
if (value === void 0 || value === false || value === "") continue;
|
|
6741
|
+
url.searchParams.set(key, String(value));
|
|
6742
|
+
}
|
|
6743
|
+
return \`\${url.pathname}\${url.search}\`;
|
|
6546
6744
|
}
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
display: flex;
|
|
6550
|
-
align-items: center;
|
|
6551
|
-
justify-content: space-between;
|
|
6552
|
-
gap: 18px;
|
|
6745
|
+
function granolaMeetingPath(id) {
|
|
6746
|
+
return \`\${granolaTransportPaths.meetings}/\${encodeURIComponent(id)}\`;
|
|
6553
6747
|
}
|
|
6554
|
-
|
|
6555
|
-
.
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
font-weight: 600;
|
|
6748
|
+
function granolaMeetingResolvePath(query, options = {}) {
|
|
6749
|
+
return appendSearchParams(granolaTransportPaths.meetingResolve, {
|
|
6750
|
+
includeTranscript: options.includeTranscript ? "true" : void 0,
|
|
6751
|
+
q: query
|
|
6752
|
+
});
|
|
6560
6753
|
}
|
|
6561
|
-
|
|
6562
|
-
.
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6754
|
+
function granolaMeetingsPath(options = {}) {
|
|
6755
|
+
return appendSearchParams(granolaTransportPaths.meetings, {
|
|
6756
|
+
folderId: options.folderId,
|
|
6757
|
+
limit: options.limit,
|
|
6758
|
+
refresh: options.forceRefresh ? "true" : void 0,
|
|
6759
|
+
search: options.search,
|
|
6760
|
+
sort: options.sort,
|
|
6761
|
+
updatedFrom: options.updatedFrom,
|
|
6762
|
+
updatedTo: options.updatedTo
|
|
6763
|
+
});
|
|
6569
6764
|
}
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
.state-badge[data-tone="error"] { color: var(--error); background: rgba(157, 44, 44, 0.12); }
|
|
6573
|
-
.state-badge[data-tone="ok"] { color: var(--ok); background: rgba(36, 107, 79, 0.12); }
|
|
6574
|
-
|
|
6575
|
-
.toolbar {
|
|
6576
|
-
display: flex;
|
|
6577
|
-
flex-wrap: wrap;
|
|
6578
|
-
align-items: center;
|
|
6579
|
-
justify-content: space-between;
|
|
6580
|
-
gap: 14px;
|
|
6765
|
+
function granolaFolderPath(id) {
|
|
6766
|
+
return \`\${granolaTransportPaths.folders}/\${encodeURIComponent(id)}\`;
|
|
6581
6767
|
}
|
|
6582
|
-
|
|
6583
|
-
.
|
|
6584
|
-
display: flex;
|
|
6585
|
-
flex-wrap: wrap;
|
|
6586
|
-
gap: 10px;
|
|
6768
|
+
function granolaFolderResolvePath(query) {
|
|
6769
|
+
return appendSearchParams(granolaTransportPaths.folderResolve, { q: query });
|
|
6587
6770
|
}
|
|
6588
|
-
|
|
6589
|
-
.
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6771
|
+
function granolaFoldersPath(options = {}) {
|
|
6772
|
+
return appendSearchParams(granolaTransportPaths.folders, {
|
|
6773
|
+
limit: options.limit,
|
|
6774
|
+
refresh: options.forceRefresh ? "true" : void 0,
|
|
6775
|
+
search: options.search
|
|
6776
|
+
});
|
|
6594
6777
|
}
|
|
6595
|
-
|
|
6596
|
-
.
|
|
6597
|
-
.security-panel,
|
|
6598
|
-
.jobs-panel {
|
|
6599
|
-
padding: 0 24px 18px;
|
|
6778
|
+
function granolaExportJobsPath(options = {}) {
|
|
6779
|
+
return appendSearchParams(granolaTransportPaths.exportJobs, { limit: options.limit });
|
|
6600
6780
|
}
|
|
6601
|
-
|
|
6602
|
-
.
|
|
6603
|
-
.auth-panel__head h3,
|
|
6604
|
-
.jobs-panel__head h3 {
|
|
6605
|
-
margin: 0;
|
|
6606
|
-
font-size: 0.92rem;
|
|
6607
|
-
letter-spacing: 0.08em;
|
|
6608
|
-
text-transform: uppercase;
|
|
6781
|
+
function granolaExportJobRerunPath(id) {
|
|
6782
|
+
return \`\${granolaTransportPaths.exportJobs}/\${encodeURIComponent(id)}/rerun\`;
|
|
6609
6783
|
}
|
|
6610
|
-
|
|
6611
|
-
.
|
|
6612
|
-
|
|
6613
|
-
.
|
|
6614
|
-
margin: 6px 0 0;
|
|
6615
|
-
color: var(--muted);
|
|
6616
|
-
font-size: 0.9rem;
|
|
6784
|
+
//#endregion
|
|
6785
|
+
//#region \0@oxc-project+runtime@0.122.0/helpers/checkPrivateRedeclaration.js
|
|
6786
|
+
function _checkPrivateRedeclaration(e, t) {
|
|
6787
|
+
if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object");
|
|
6617
6788
|
}
|
|
6618
|
-
|
|
6619
|
-
.
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
gap: 12px;
|
|
6623
|
-
margin-top: 14px;
|
|
6789
|
+
//#endregion
|
|
6790
|
+
//#region \0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldInitSpec.js
|
|
6791
|
+
function _classPrivateFieldInitSpec(e, t, a) {
|
|
6792
|
+
_checkPrivateRedeclaration(e, t), t.set(e, a);
|
|
6624
6793
|
}
|
|
6625
|
-
|
|
6626
|
-
.
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6794
|
+
//#endregion
|
|
6795
|
+
//#region \0@oxc-project+runtime@0.122.0/helpers/typeof.js
|
|
6796
|
+
function _typeof(o) {
|
|
6797
|
+
"@babel/helpers - typeof";
|
|
6798
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
6799
|
+
return typeof o;
|
|
6800
|
+
} : function(o) {
|
|
6801
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
6802
|
+
}, _typeof(o);
|
|
6633
6803
|
}
|
|
6634
|
-
|
|
6635
|
-
.
|
|
6636
|
-
|
|
6637
|
-
|
|
6804
|
+
//#endregion
|
|
6805
|
+
//#region \0@oxc-project+runtime@0.122.0/helpers/toPrimitive.js
|
|
6806
|
+
function toPrimitive(t, r) {
|
|
6807
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
6808
|
+
var e = t[Symbol.toPrimitive];
|
|
6809
|
+
if (void 0 !== e) {
|
|
6810
|
+
var i = e.call(t, r || "default");
|
|
6811
|
+
if ("object" != _typeof(i)) return i;
|
|
6812
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
6813
|
+
}
|
|
6814
|
+
return ("string" === r ? String : Number)(t);
|
|
6638
6815
|
}
|
|
6639
|
-
|
|
6640
|
-
.
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6816
|
+
//#endregion
|
|
6817
|
+
//#region \0@oxc-project+runtime@0.122.0/helpers/toPropertyKey.js
|
|
6818
|
+
function toPropertyKey(t) {
|
|
6819
|
+
var i = toPrimitive(t, "string");
|
|
6820
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
6644
6821
|
}
|
|
6645
|
-
|
|
6646
|
-
.
|
|
6647
|
-
|
|
6822
|
+
//#endregion
|
|
6823
|
+
//#region \0@oxc-project+runtime@0.122.0/helpers/defineProperty.js
|
|
6824
|
+
function _defineProperty(e, r, t) {
|
|
6825
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
6826
|
+
value: t,
|
|
6827
|
+
enumerable: !0,
|
|
6828
|
+
configurable: !0,
|
|
6829
|
+
writable: !0
|
|
6830
|
+
}) : e[r] = t, e;
|
|
6648
6831
|
}
|
|
6649
|
-
|
|
6650
|
-
.
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6832
|
+
//#endregion
|
|
6833
|
+
//#region \0@oxc-project+runtime@0.122.0/helpers/assertClassBrand.js
|
|
6834
|
+
function _assertClassBrand(e, t, n) {
|
|
6835
|
+
if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
|
|
6836
|
+
throw new TypeError("Private element is not present on this object");
|
|
6654
6837
|
}
|
|
6655
|
-
|
|
6656
|
-
.
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
padding: 14px 16px;
|
|
6660
|
-
border: 1px solid var(--line);
|
|
6661
|
-
border-radius: 18px;
|
|
6662
|
-
background: rgba(255, 255, 255, 0.72);
|
|
6838
|
+
//#endregion
|
|
6839
|
+
//#region \0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldSet2.js
|
|
6840
|
+
function _classPrivateFieldSet2(s, a, r) {
|
|
6841
|
+
return s.set(_assertClassBrand(s, a), r), r;
|
|
6663
6842
|
}
|
|
6664
|
-
|
|
6665
|
-
.
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
align-items: center;
|
|
6669
|
-
justify-content: space-between;
|
|
6670
|
-
gap: 10px;
|
|
6843
|
+
//#endregion
|
|
6844
|
+
//#region \0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldGet2.js
|
|
6845
|
+
function _classPrivateFieldGet2(s, a) {
|
|
6846
|
+
return s.get(_assertClassBrand(s, a));
|
|
6671
6847
|
}
|
|
6672
|
-
|
|
6673
|
-
.
|
|
6674
|
-
|
|
6848
|
+
//#endregion
|
|
6849
|
+
//#region src/server/client.ts
|
|
6850
|
+
function cloneValue(value) {
|
|
6851
|
+
return structuredClone(value);
|
|
6675
6852
|
}
|
|
6676
|
-
|
|
6677
|
-
.
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
.
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
color: var(--accent);
|
|
6687
|
-
font-size: 0.82rem;
|
|
6688
|
-
font-weight: 700;
|
|
6689
|
-
}
|
|
6690
|
-
|
|
6691
|
-
.job-card__status[data-status="running"] {
|
|
6692
|
-
background: rgba(163, 79, 47, 0.12);
|
|
6693
|
-
color: var(--warm);
|
|
6694
|
-
}
|
|
6695
|
-
|
|
6696
|
-
.job-card__status[data-status="failed"] {
|
|
6697
|
-
background: rgba(157, 44, 44, 0.12);
|
|
6698
|
-
color: var(--error);
|
|
6699
|
-
}
|
|
6700
|
-
|
|
6701
|
-
.job-card__status[data-status="completed"] {
|
|
6702
|
-
background: rgba(36, 107, 79, 0.12);
|
|
6703
|
-
color: var(--ok);
|
|
6704
|
-
}
|
|
6705
|
-
|
|
6706
|
-
.job-card__actions {
|
|
6707
|
-
display: flex;
|
|
6708
|
-
flex-wrap: wrap;
|
|
6709
|
-
gap: 8px;
|
|
6710
|
-
}
|
|
6711
|
-
|
|
6712
|
-
.job-empty {
|
|
6713
|
-
color: var(--muted);
|
|
6714
|
-
font-size: 0.92rem;
|
|
6715
|
-
}
|
|
6716
|
-
|
|
6717
|
-
.workspace-tabs {
|
|
6718
|
-
display: flex;
|
|
6719
|
-
flex-wrap: wrap;
|
|
6720
|
-
align-items: center;
|
|
6721
|
-
gap: 10px;
|
|
6722
|
-
padding: 0 24px 18px;
|
|
6723
|
-
}
|
|
6724
|
-
|
|
6725
|
-
.workspace-tab {
|
|
6726
|
-
border: 1px solid var(--line);
|
|
6727
|
-
border-radius: 999px;
|
|
6728
|
-
padding: 10px 14px;
|
|
6729
|
-
background: rgba(255, 255, 255, 0.72);
|
|
6730
|
-
color: var(--muted);
|
|
6731
|
-
cursor: pointer;
|
|
6732
|
-
font: inherit;
|
|
6733
|
-
font-weight: 700;
|
|
6734
|
-
}
|
|
6735
|
-
|
|
6736
|
-
.workspace-tab[data-selected="true"] {
|
|
6737
|
-
background: var(--ink);
|
|
6738
|
-
color: white;
|
|
6739
|
-
border-color: var(--ink);
|
|
6853
|
+
function normaliseServerUrl(serverUrl) {
|
|
6854
|
+
const raw = serverUrl instanceof URL ? serverUrl.href : serverUrl.trim();
|
|
6855
|
+
if (!raw) throw new Error("server URL is required");
|
|
6856
|
+
const withProtocol = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw) ? raw : \`http://\${raw}\`;
|
|
6857
|
+
const parsed = new URL(withProtocol);
|
|
6858
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("server URL must use http or https");
|
|
6859
|
+
parsed.pathname = "/";
|
|
6860
|
+
parsed.search = "";
|
|
6861
|
+
parsed.hash = "";
|
|
6862
|
+
return parsed;
|
|
6740
6863
|
}
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6864
|
+
function mergeHeaders(...values) {
|
|
6865
|
+
const headers = new Headers();
|
|
6866
|
+
for (const value of values) {
|
|
6867
|
+
if (!value) continue;
|
|
6868
|
+
new Headers(value).forEach((headerValue, headerName) => {
|
|
6869
|
+
headers.set(headerName, headerValue);
|
|
6870
|
+
});
|
|
6871
|
+
}
|
|
6872
|
+
return headers;
|
|
6746
6873
|
}
|
|
6747
|
-
|
|
6748
|
-
.
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6874
|
+
async function responseError(response) {
|
|
6875
|
+
let message = \`\${response.status} \${response.statusText}\`.trim();
|
|
6876
|
+
try {
|
|
6877
|
+
const payload = await response.json();
|
|
6878
|
+
if (typeof payload.error === "string" && payload.error.trim()) message = payload.error;
|
|
6879
|
+
else if (typeof payload.message === "string" && payload.message.trim()) message = payload.message;
|
|
6880
|
+
} catch {
|
|
6881
|
+
const text = (await response.text()).trim();
|
|
6882
|
+
if (text) message = text;
|
|
6883
|
+
}
|
|
6884
|
+
return new Error(message);
|
|
6755
6885
|
}
|
|
6756
|
-
|
|
6757
|
-
.
|
|
6758
|
-
|
|
6759
|
-
|
|
6886
|
+
function parseSseEvent(payload) {
|
|
6887
|
+
const data = payload.replaceAll("\r\n", "\n").split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
6888
|
+
if (!data) return;
|
|
6889
|
+
return JSON.parse(data);
|
|
6760
6890
|
}
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6891
|
+
var _closed = /* @__PURE__ */ new WeakMap();
|
|
6892
|
+
var _eventLoop = /* @__PURE__ */ new WeakMap();
|
|
6893
|
+
var _listeners = /* @__PURE__ */ new WeakMap();
|
|
6894
|
+
var _fetchImpl = /* @__PURE__ */ new WeakMap();
|
|
6895
|
+
var _password = /* @__PURE__ */ new WeakMap();
|
|
6896
|
+
var _reconnectDelayMs = /* @__PURE__ */ new WeakMap();
|
|
6897
|
+
var _streamAbortController = /* @__PURE__ */ new WeakMap();
|
|
6898
|
+
var _state = /* @__PURE__ */ new WeakMap();
|
|
6899
|
+
var GranolaServerClient = class GranolaServerClient {
|
|
6900
|
+
constructor(info, url, initialState, options = {}) {
|
|
6901
|
+
_classPrivateFieldInitSpec(this, _closed, false);
|
|
6902
|
+
_classPrivateFieldInitSpec(this, _eventLoop, void 0);
|
|
6903
|
+
_classPrivateFieldInitSpec(this, _listeners, /* @__PURE__ */ new Set());
|
|
6904
|
+
_classPrivateFieldInitSpec(this, _fetchImpl, void 0);
|
|
6905
|
+
_classPrivateFieldInitSpec(this, _password, void 0);
|
|
6906
|
+
_classPrivateFieldInitSpec(this, _reconnectDelayMs, void 0);
|
|
6907
|
+
_defineProperty(this, "info", void 0);
|
|
6908
|
+
_classPrivateFieldInitSpec(this, _streamAbortController, void 0);
|
|
6909
|
+
_classPrivateFieldInitSpec(this, _state, void 0);
|
|
6910
|
+
this.url = url;
|
|
6911
|
+
_classPrivateFieldSet2(_fetchImpl, this, options.fetchImpl ?? fetch);
|
|
6912
|
+
this.info = cloneValue(info);
|
|
6913
|
+
_classPrivateFieldSet2(_password, this, options.password?.trim() || void 0);
|
|
6914
|
+
_classPrivateFieldSet2(_reconnectDelayMs, this, options.reconnectDelayMs ?? 1e3);
|
|
6915
|
+
_classPrivateFieldSet2(_state, this, cloneValue(initialState));
|
|
6916
|
+
}
|
|
6917
|
+
static async connect(serverUrl, options = {}) {
|
|
6918
|
+
const url = normaliseServerUrl(serverUrl);
|
|
6919
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
6920
|
+
const infoResponse = await fetchImpl(new URL(granolaTransportPaths.serverInfo, url), { headers: mergeHeaders({
|
|
6921
|
+
...options.password?.trim() ? { "x-granola-password": options.password.trim() } : {},
|
|
6922
|
+
accept: "application/json"
|
|
6923
|
+
}) });
|
|
6924
|
+
if (!infoResponse.ok) throw await responseError(infoResponse);
|
|
6925
|
+
const info = await infoResponse.json();
|
|
6926
|
+
if (info.protocolVersion !== 2) throw new Error(\`unsupported Granola transport protocol: expected 2, got \${info.protocolVersion}\`);
|
|
6927
|
+
const response = await fetchImpl(new URL(granolaTransportPaths.state, url), { headers: mergeHeaders({
|
|
6928
|
+
...options.password?.trim() ? { "x-granola-password": options.password.trim() } : {},
|
|
6929
|
+
accept: "application/json"
|
|
6930
|
+
}) });
|
|
6931
|
+
if (!response.ok) throw await responseError(response);
|
|
6932
|
+
const client = new GranolaServerClient(info, url, await response.json(), options);
|
|
6933
|
+
client.startEvents();
|
|
6934
|
+
return client;
|
|
6935
|
+
}
|
|
6936
|
+
async close() {
|
|
6937
|
+
_classPrivateFieldSet2(_closed, this, true);
|
|
6938
|
+
_classPrivateFieldGet2(_streamAbortController, this)?.abort();
|
|
6939
|
+
try {
|
|
6940
|
+
await _classPrivateFieldGet2(_eventLoop, this);
|
|
6941
|
+
} catch {}
|
|
6942
|
+
}
|
|
6943
|
+
getState() {
|
|
6944
|
+
return cloneValue(_classPrivateFieldGet2(_state, this));
|
|
6945
|
+
}
|
|
6946
|
+
subscribe(listener) {
|
|
6947
|
+
_classPrivateFieldGet2(_listeners, this).add(listener);
|
|
6948
|
+
return () => {
|
|
6949
|
+
_classPrivateFieldGet2(_listeners, this).delete(listener);
|
|
6950
|
+
};
|
|
6951
|
+
}
|
|
6952
|
+
async inspectAuth() {
|
|
6953
|
+
return await this.requestJson(granolaTransportPaths.authStatus);
|
|
6954
|
+
}
|
|
6955
|
+
async inspectSync() {
|
|
6956
|
+
return cloneValue(_classPrivateFieldGet2(_state, this).sync);
|
|
6957
|
+
}
|
|
6958
|
+
async listSyncEvents(options = {}) {
|
|
6959
|
+
const path = options.limit ? \`\${granolaTransportPaths.syncEvents}?limit=\${encodeURIComponent(String(options.limit))}\` : granolaTransportPaths.syncEvents;
|
|
6960
|
+
return await this.requestJson(path);
|
|
6961
|
+
}
|
|
6962
|
+
async loginAuth(options = {}) {
|
|
6963
|
+
return await this.requestJson(granolaTransportPaths.authLogin, {
|
|
6964
|
+
body: JSON.stringify(options),
|
|
6965
|
+
headers: { "content-type": "application/json" },
|
|
6966
|
+
method: "POST"
|
|
6967
|
+
});
|
|
6968
|
+
}
|
|
6969
|
+
async logoutAuth() {
|
|
6970
|
+
return await this.requestJson(granolaTransportPaths.authLogout, { method: "POST" });
|
|
6971
|
+
}
|
|
6972
|
+
async refreshAuth() {
|
|
6973
|
+
return await this.requestJson(granolaTransportPaths.authRefresh, { method: "POST" });
|
|
6974
|
+
}
|
|
6975
|
+
async switchAuthMode(mode) {
|
|
6976
|
+
return await this.requestJson(granolaTransportPaths.authMode, {
|
|
6977
|
+
body: JSON.stringify({ mode }),
|
|
6978
|
+
headers: { "content-type": "application/json" },
|
|
6979
|
+
method: "POST"
|
|
6980
|
+
});
|
|
6981
|
+
}
|
|
6982
|
+
async sync(options = {}) {
|
|
6983
|
+
return await this.requestJson(granolaTransportPaths.syncRun, {
|
|
6984
|
+
body: JSON.stringify(options),
|
|
6985
|
+
headers: { "content-type": "application/json" },
|
|
6986
|
+
method: "POST"
|
|
6987
|
+
});
|
|
6988
|
+
}
|
|
6989
|
+
async listFolders(options = {}) {
|
|
6990
|
+
return await this.requestJson(granolaFoldersPath(options));
|
|
6991
|
+
}
|
|
6992
|
+
async getFolder(id) {
|
|
6993
|
+
return await this.requestJson(granolaFolderPath(id));
|
|
6994
|
+
}
|
|
6995
|
+
async findFolder(query) {
|
|
6996
|
+
return await this.requestJson(granolaFolderResolvePath(query));
|
|
6997
|
+
}
|
|
6998
|
+
async listMeetings(options = {}) {
|
|
6999
|
+
return await this.requestJson(granolaMeetingsPath(options));
|
|
7000
|
+
}
|
|
7001
|
+
async getMeeting(id, options = {}) {
|
|
7002
|
+
return await this.requestJson(\`\${granolaMeetingPath(id)}\${options.requireCache ? "?includeTranscript=true" : ""}\`);
|
|
7003
|
+
}
|
|
7004
|
+
async findMeeting(query, options = {}) {
|
|
7005
|
+
return await this.requestJson(granolaMeetingResolvePath(query, { includeTranscript: options.requireCache }));
|
|
7006
|
+
}
|
|
7007
|
+
async listExportJobs(options = {}) {
|
|
7008
|
+
return await this.requestJson(granolaExportJobsPath(options));
|
|
7009
|
+
}
|
|
7010
|
+
async exportNotes(format = "markdown", options = {}) {
|
|
7011
|
+
return await this.requestJson(granolaTransportPaths.exportNotes, {
|
|
7012
|
+
body: JSON.stringify({
|
|
7013
|
+
folderId: options.folderId,
|
|
7014
|
+
format
|
|
7015
|
+
}),
|
|
7016
|
+
headers: { "content-type": "application/json" },
|
|
7017
|
+
method: "POST"
|
|
7018
|
+
});
|
|
7019
|
+
}
|
|
7020
|
+
async exportTranscripts(format = "text", options = {}) {
|
|
7021
|
+
return await this.requestJson(granolaTransportPaths.exportTranscripts, {
|
|
7022
|
+
body: JSON.stringify({
|
|
7023
|
+
folderId: options.folderId,
|
|
7024
|
+
format
|
|
7025
|
+
}),
|
|
7026
|
+
headers: { "content-type": "application/json" },
|
|
7027
|
+
method: "POST"
|
|
7028
|
+
});
|
|
7029
|
+
}
|
|
7030
|
+
async rerunExportJob(id) {
|
|
7031
|
+
return await this.requestJson(granolaExportJobRerunPath(id), { method: "POST" });
|
|
7032
|
+
}
|
|
7033
|
+
async request(path, init = {}) {
|
|
7034
|
+
const response = await _classPrivateFieldGet2(_fetchImpl, this).call(this, new URL(path, this.url), {
|
|
7035
|
+
...init,
|
|
7036
|
+
headers: mergeHeaders({
|
|
7037
|
+
..._classPrivateFieldGet2(_password, this) ? { "x-granola-password": _classPrivateFieldGet2(_password, this) } : {},
|
|
7038
|
+
accept: "application/json"
|
|
7039
|
+
}, init.headers)
|
|
7040
|
+
});
|
|
7041
|
+
if (!response.ok) throw await responseError(response);
|
|
7042
|
+
return response;
|
|
7043
|
+
}
|
|
7044
|
+
async requestJson(path, init = {}) {
|
|
7045
|
+
return cloneValue(await (await this.request(path, init)).json());
|
|
7046
|
+
}
|
|
7047
|
+
emit(event) {
|
|
7048
|
+
_classPrivateFieldSet2(_state, this, cloneValue(event.state));
|
|
7049
|
+
const nextEvent = cloneValue(event);
|
|
7050
|
+
for (const listener of _classPrivateFieldGet2(_listeners, this)) listener(nextEvent);
|
|
7051
|
+
}
|
|
7052
|
+
startEvents() {
|
|
7053
|
+
if (_classPrivateFieldGet2(_eventLoop, this)) return;
|
|
7054
|
+
_classPrivateFieldSet2(_eventLoop, this, this.runEventsLoop());
|
|
7055
|
+
}
|
|
7056
|
+
async runEventsLoop() {
|
|
7057
|
+
while (!_classPrivateFieldGet2(_closed, this)) {
|
|
7058
|
+
const controller = new AbortController();
|
|
7059
|
+
_classPrivateFieldSet2(_streamAbortController, this, controller);
|
|
7060
|
+
try {
|
|
7061
|
+
const response = await this.request(granolaTransportPaths.events, {
|
|
7062
|
+
headers: { accept: "text/event-stream" },
|
|
7063
|
+
signal: controller.signal
|
|
7064
|
+
});
|
|
7065
|
+
await this.consumeEventStream(response);
|
|
7066
|
+
} catch {
|
|
7067
|
+
if (_classPrivateFieldGet2(_closed, this) || controller.signal.aborted) break;
|
|
7068
|
+
await new Promise((resolve) => {
|
|
7069
|
+
setTimeout(resolve, _classPrivateFieldGet2(_reconnectDelayMs, this));
|
|
7070
|
+
});
|
|
7071
|
+
}
|
|
7072
|
+
}
|
|
7073
|
+
}
|
|
7074
|
+
async consumeEventStream(response) {
|
|
7075
|
+
const reader = response.body?.getReader();
|
|
7076
|
+
if (!reader) throw new Error("server did not provide an event stream");
|
|
7077
|
+
const decoder = new TextDecoder();
|
|
7078
|
+
let buffer = "";
|
|
7079
|
+
while (!_classPrivateFieldGet2(_closed, this)) {
|
|
7080
|
+
const { done, value } = await reader.read();
|
|
7081
|
+
if (done) return;
|
|
7082
|
+
buffer += decoder.decode(value, { stream: true });
|
|
7083
|
+
buffer = buffer.replaceAll("\r\n", "\n");
|
|
7084
|
+
while (true) {
|
|
7085
|
+
const boundary = buffer.indexOf("\n\n");
|
|
7086
|
+
if (boundary < 0) break;
|
|
7087
|
+
const chunk = buffer.slice(0, boundary);
|
|
7088
|
+
buffer = buffer.slice(boundary + 2);
|
|
7089
|
+
const event = parseSseEvent(chunk);
|
|
7090
|
+
if (event) this.emit(event);
|
|
7091
|
+
}
|
|
7092
|
+
}
|
|
7093
|
+
}
|
|
7094
|
+
};
|
|
7095
|
+
async function createGranolaServerClient(serverUrl, options = {}) {
|
|
7096
|
+
return await GranolaServerClient.connect(serverUrl, options);
|
|
6765
7097
|
}
|
|
6766
|
-
|
|
6767
|
-
.
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
7098
|
+
//#endregion
|
|
7099
|
+
//#region src/web/client-state.ts
|
|
7100
|
+
function parseWorkspaceTab(value) {
|
|
7101
|
+
switch (value) {
|
|
7102
|
+
case "metadata":
|
|
7103
|
+
case "raw":
|
|
7104
|
+
case "transcript": return value;
|
|
7105
|
+
default: return "notes";
|
|
7106
|
+
}
|
|
6771
7107
|
}
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
7108
|
+
function startupSelectionFromSearch(search) {
|
|
7109
|
+
const params = new URLSearchParams(search);
|
|
7110
|
+
return {
|
|
7111
|
+
folderId: params.get("folder")?.trim() || "",
|
|
7112
|
+
meetingId: params.get("meeting")?.trim() || "",
|
|
7113
|
+
workspaceTab: parseWorkspaceTab(params.get("tab"))
|
|
7114
|
+
};
|
|
6777
7115
|
}
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
7116
|
+
function buildBrowserUrlPath(currentHref, selection) {
|
|
7117
|
+
const url = new URL(currentHref);
|
|
7118
|
+
if (selection.selectedFolderId) url.searchParams.set("folder", selection.selectedFolderId);
|
|
7119
|
+
else url.searchParams.delete("folder");
|
|
7120
|
+
if (selection.selectedMeetingId) url.searchParams.set("meeting", selection.selectedMeetingId);
|
|
7121
|
+
else url.searchParams.delete("meeting");
|
|
7122
|
+
if (parseWorkspaceTab(selection.workspaceTab) !== "notes") url.searchParams.set("tab", parseWorkspaceTab(selection.workspaceTab));
|
|
7123
|
+
else url.searchParams.delete("tab");
|
|
7124
|
+
return \`\${url.pathname}\${url.search}\${url.hash}\`;
|
|
6786
7125
|
}
|
|
6787
|
-
|
|
6788
|
-
.
|
|
6789
|
-
display: flex;
|
|
6790
|
-
flex-wrap: wrap;
|
|
6791
|
-
gap: 10px;
|
|
6792
|
-
padding: 0 24px 18px;
|
|
7126
|
+
function exportScopeLabel(scope) {
|
|
7127
|
+
return scope && scope.mode === "folder" ? \`Folder: \${scope.folderName || scope.folderId}\` : "Scope: All meetings";
|
|
6793
7128
|
}
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
7129
|
+
function currentFilterSummary(filters) {
|
|
7130
|
+
const parts = [];
|
|
7131
|
+
if (filters.selectedFolderId) {
|
|
7132
|
+
const folder = filters.folders.find((candidate) => candidate.id === filters.selectedFolderId);
|
|
7133
|
+
parts.push(\`folder "\${folder ? folder.name : filters.selectedFolderId}"\`);
|
|
7134
|
+
}
|
|
7135
|
+
if (filters.search) parts.push(\`search "\${filters.search}"\`);
|
|
7136
|
+
if (filters.updatedFrom) parts.push(\`from \${filters.updatedFrom}\`);
|
|
7137
|
+
if (filters.updatedTo) parts.push(\`to \${filters.updatedTo}\`);
|
|
7138
|
+
return parts.join(", ");
|
|
6802
7139
|
}
|
|
6803
|
-
|
|
6804
|
-
.
|
|
6805
|
-
|
|
6806
|
-
overflow: auto;
|
|
7140
|
+
function selectMeetingId(meetings, selectedMeetingId) {
|
|
7141
|
+
if (selectedMeetingId && meetings.some((meeting) => meeting.id === selectedMeetingId)) return selectedMeetingId;
|
|
7142
|
+
return meetings[0]?.id ?? null;
|
|
6807
7143
|
}
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
7144
|
+
function nextWorkspaceTab(currentTab, key) {
|
|
7145
|
+
const current = parseWorkspaceTab(currentTab);
|
|
7146
|
+
switch (key) {
|
|
7147
|
+
case "1": return "notes";
|
|
7148
|
+
case "2": return "transcript";
|
|
7149
|
+
case "3": return "metadata";
|
|
7150
|
+
case "4": return "raw";
|
|
7151
|
+
case "]":
|
|
7152
|
+
switch (current) {
|
|
7153
|
+
case "notes": return "transcript";
|
|
7154
|
+
case "transcript": return "metadata";
|
|
7155
|
+
case "metadata": return "raw";
|
|
7156
|
+
case "raw": return "notes";
|
|
7157
|
+
}
|
|
7158
|
+
break;
|
|
7159
|
+
case "[":
|
|
7160
|
+
switch (current) {
|
|
7161
|
+
case "notes": return "raw";
|
|
7162
|
+
case "transcript": return "notes";
|
|
7163
|
+
case "metadata": return "transcript";
|
|
7164
|
+
case "raw": return "metadata";
|
|
7165
|
+
}
|
|
7166
|
+
break;
|
|
7167
|
+
default: return;
|
|
7168
|
+
}
|
|
6813
7169
|
}
|
|
6814
|
-
|
|
6815
|
-
.
|
|
6816
|
-
|
|
6817
|
-
|
|
7170
|
+
//#endregion
|
|
7171
|
+
//#region src/web-app/components.tsx
|
|
7172
|
+
/** @jsxImportSource solid-js */
|
|
7173
|
+
var _tmpl$$1 = /* @__PURE__ */ template(\`<section class=hero><h1>Granola Toolkit</h1><p>Browser workspace for folders, meetings, notes, transcripts, and export flows on top of one local server instance.</p><input class=search placeholder="Search meetings, ids, or tags"><div class="field-row field-row--inline"><label><span class=field-label>Sort</span><select class=select><option value=updated-desc>Newest first</option><option value=updated-asc>Oldest first</option><option value=title-asc>Title A-Z</option><option value=title-desc>Title Z-A</option></select></label><label><span class=field-label>Updated From</span><input class=field-input type=date></label></div><label class=field-row><span class=field-label>Updated To</span><input class=field-input type=date>\`), _tmpl$2 = /* @__PURE__ */ template(\`<section class=toolbar><div><p>Meetings are loaded from the shared server state so this view can stay aligned with the terminal UI and sync loop.</p></div><div class=toolbar-form><input class=field-input placeholder="Quick open by id or title"><button class="button button--secondary"type=button>Open\`), _tmpl$3 = /* @__PURE__ */ template(\`<div class="folder-empty folder-empty--error">\`), _tmpl$4 = /* @__PURE__ */ template(\`<section class=folder-panel><div class=folder-panel__head><h2>Folders</h2><p>Pick a folder to scope the meeting browser, or stay on All meetings.</p></div><div class=folder-list>\`), _tmpl$5 = /* @__PURE__ */ template(\`<button class=folder-row type=button><span class=folder-row__title>All meetings</span><span class=folder-row__meta>Browse the full meeting list.\`), _tmpl$6 = /* @__PURE__ */ template(\`<div class=folder-empty>No folders found.\`), _tmpl$7 = /* @__PURE__ */ template(\`<button class=folder-row type=button><span class=folder-row__title></span><span class=folder-row__meta>\`), _tmpl$8 = /* @__PURE__ */ template(\`<div class="meeting-empty meeting-empty--error">\`), _tmpl$9 = /* @__PURE__ */ template(\`<section class=meeting-list>\`), _tmpl$0 = /* @__PURE__ */ template(\`<div class=meeting-empty>\`), _tmpl$1 = /* @__PURE__ */ template(\`<button class=meeting-row type=button><span class=meeting-row__title></span><span class=meeting-row__meta></span><span class=meeting-row__meta>\`), _tmpl$10 = /* @__PURE__ */ template(\`<section class=detail-head><div><h2>Meeting Workspace</h2></div><div class=state-badge>\`), _tmpl$11 = /* @__PURE__ */ template(\`<p>Waiting for server state…\`), _tmpl$12 = /* @__PURE__ */ template(\`<div class=status-grid><div><span class=status-label>Surface</span><strong></strong></div><div><span class=status-label>View</span><strong></strong></div><div><span class=status-label>Auth</span><strong></strong></div><div><span class=status-label>Sync</span><strong></strong></div><div><span class=status-label>Documents</span><strong></strong></div><div><span class=status-label>Folders</span><strong></strong></div><div><span class=status-label>Cache</span><strong></strong></div><div><span class=status-label>Index</span><strong>\`), _tmpl$13 = /* @__PURE__ */ template(\`<section class=security-panel><div class=security-panel__head><h3>Server Access</h3><p>This server is locked with a password. Unlock it to load meetings and live state.</p></div><div class=security-panel__body><input class=field-input placeholder="Server password"type=password><div class=toolbar-actions><button class="button button--primary"type=button>Unlock</button><button class="button button--secondary"type=button>Lock\`), _tmpl$14 = /* @__PURE__ */ template(\`<section class=auth-panel><div class=auth-panel__head><h3>Auth Session</h3><p>Inspect, refresh, and switch between API key, stored session, and <code>supabase.json</code>.</p></div><div class=auth-panel__body>\`), _tmpl$15 = /* @__PURE__ */ template(\`<div class=auth-card><div class=auth-card__meta>Auth state unavailable.\`), _tmpl$16 = /* @__PURE__ */ template(\`<div class=auth-card__meta>Client ID: \`), _tmpl$17 = /* @__PURE__ */ template(\`<div class=auth-card__meta>Sign-in method: \`), _tmpl$18 = /* @__PURE__ */ template(\`<div class=auth-card__meta>supabase path: \`), _tmpl$19 = /* @__PURE__ */ template(\`<div class="auth-card__meta auth-card__error">\`), _tmpl$20 = /* @__PURE__ */ template(\`<div class=auth-card><div class=status-grid><div><span class=status-label>Active</span><strong></strong></div><div><span class=status-label>API key</span><strong></strong></div><div><span class=status-label>Stored</span><strong></strong></div><div><span class=status-label>supabase.json</span><strong></strong></div><div><span class=status-label>Refresh</span><strong></strong></div></div><div class=auth-card__meta>Store a Granola Personal API key here or use <code>granola auth login --api-key <token></code>.</div><div class=auth-card__actions><input class=input placeholder=grn_... type=password><button class="button button--secondary"type=button>Save API key</button><button class="button button--secondary"type=button>Import desktop session</button><button class="button button--secondary"type=button>Refresh stored session</button><button class="button button--secondary"type=button>Use API key</button><button class="button button--secondary"type=button>Use stored session</button><button class="button button--secondary"type=button>Use supabase.json</button><button class="button button--secondary"type=button>Sign out\`), _tmpl$21 = /* @__PURE__ */ template(\`<section class=jobs-panel><div class=jobs-panel__head><h3>Recent Export Jobs</h3><p>Tracked across CLI and web runs.</p></div><div class=jobs-list>\`), _tmpl$22 = /* @__PURE__ */ template(\`<div class=job-empty>No export jobs yet.\`), _tmpl$23 = /* @__PURE__ */ template(\`<div class=job-card__meta>\`), _tmpl$24 = /* @__PURE__ */ template(\`<button class="button button--secondary"type=button>Rerun\`), _tmpl$25 = /* @__PURE__ */ template(\`<article class=job-card><div class=job-card__head><div><div class=job-card__title> export</div><div class=job-card__meta></div></div><div class=job-card__status></div></div><div class=job-card__meta></div><div class=job-card__meta>Started: </div><div class=job-card__meta>Output: </div><div class=job-card__actions>\`), _tmpl$26 = /* @__PURE__ */ template(\`<nav class=workspace-tabs><span class=workspace-hint>1-4 switch tabs, [ and ] cycle\`), _tmpl$27 = /* @__PURE__ */ template(\`<button class=workspace-tab type=button>\`), _tmpl$28 = /* @__PURE__ */ template(\`<div class=empty>\`), _tmpl$29 = /* @__PURE__ */ template(\`<div class=detail-meta><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip>\`), _tmpl$30 = /* @__PURE__ */ template(\`<div class=detail-body><div class=workspace-grid><aside class="detail-section workspace-sidebar"><h2>Meeting Metadata</h2><pre class=detail-pre></pre></aside><section class="detail-section workspace-main"><h2></h2><pre class=detail-pre>\`);
|
|
7174
|
+
function authModeLabel(mode) {
|
|
7175
|
+
switch (mode) {
|
|
7176
|
+
case "api-key": return "API key";
|
|
7177
|
+
case "stored-session": return "Stored session";
|
|
7178
|
+
default: return "supabase.json";
|
|
7179
|
+
}
|
|
6818
7180
|
}
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
7181
|
+
function metadataLines(record) {
|
|
7182
|
+
return [
|
|
7183
|
+
\`Title: \${record.meeting.title || record.meeting.id}\`,
|
|
7184
|
+
\`Created: \${record.meeting.createdAt}\`,
|
|
7185
|
+
\`Updated: \${record.meeting.updatedAt}\`,
|
|
7186
|
+
\`Folders: \${record.meeting.folders.length ? record.meeting.folders.map((folder) => folder.name).join(", ") : "none"}\`,
|
|
7187
|
+
\`Tags: \${record.meeting.tags.length ? record.meeting.tags.join(", ") : "none"}\`,
|
|
7188
|
+
\`Transcript loaded: \${record.meeting.transcriptLoaded ? "yes" : "no"}\`
|
|
7189
|
+
].join("\n");
|
|
7190
|
+
}
|
|
7191
|
+
function workspaceBody(bundle, record, tab) {
|
|
7192
|
+
switch (tab) {
|
|
7193
|
+
case "transcript": return {
|
|
7194
|
+
body: record.transcriptText || "(Transcript unavailable)",
|
|
7195
|
+
title: "Transcript"
|
|
7196
|
+
};
|
|
7197
|
+
case "metadata": return {
|
|
7198
|
+
body: metadataLines(record),
|
|
7199
|
+
title: "Metadata"
|
|
7200
|
+
};
|
|
7201
|
+
case "raw": return {
|
|
7202
|
+
body: JSON.stringify(bundle || record, null, 2),
|
|
7203
|
+
title: "Raw Bundle"
|
|
7204
|
+
};
|
|
7205
|
+
default: return {
|
|
7206
|
+
body: record.noteMarkdown || "(No notes available)",
|
|
7207
|
+
title: "Notes"
|
|
7208
|
+
};
|
|
7209
|
+
}
|
|
6826
7210
|
}
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
margin: 0 0 14px;
|
|
6830
|
-
font-size: 1rem;
|
|
6831
|
-
letter-spacing: 0.08em;
|
|
6832
|
-
text-transform: uppercase;
|
|
7211
|
+
function scopeLabel(scope) {
|
|
7212
|
+
return exportScopeLabel(scope);
|
|
6833
7213
|
}
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
7214
|
+
function ToolbarFilters(props) {
|
|
7215
|
+
return [(() => {
|
|
7216
|
+
var _el$ = _tmpl$$1(), _el$4 = _el$.firstChild.nextSibling.nextSibling, _el$5 = _el$4.nextSibling, _el$6 = _el$5.firstChild, _el$8 = _el$6.firstChild.nextSibling, _el$1 = _el$6.nextSibling.firstChild.nextSibling, _el$12 = _el$5.nextSibling.firstChild.nextSibling;
|
|
7217
|
+
_el$4.$$input = (event) => {
|
|
7218
|
+
props.onSearchInput(event.currentTarget.value);
|
|
7219
|
+
};
|
|
7220
|
+
_el$8.addEventListener("change", (event) => {
|
|
7221
|
+
props.onSortChange(event.currentTarget.value);
|
|
7222
|
+
});
|
|
7223
|
+
_el$1.addEventListener("change", (event) => {
|
|
7224
|
+
props.onUpdatedFromChange(event.currentTarget.value);
|
|
7225
|
+
});
|
|
7226
|
+
_el$12.addEventListener("change", (event) => {
|
|
7227
|
+
props.onUpdatedToChange(event.currentTarget.value);
|
|
7228
|
+
});
|
|
7229
|
+
createRenderEffect(() => _el$4.value = props.search);
|
|
7230
|
+
createRenderEffect(() => _el$8.value = props.sort);
|
|
7231
|
+
createRenderEffect(() => _el$1.value = props.updatedFrom);
|
|
7232
|
+
createRenderEffect(() => _el$12.value = props.updatedTo);
|
|
7233
|
+
return _el$;
|
|
7234
|
+
})(), (() => {
|
|
7235
|
+
var _el$13 = _tmpl$2(), _el$16 = _el$13.firstChild.nextSibling.firstChild, _el$17 = _el$16.nextSibling;
|
|
7236
|
+
_el$16.$$keydown = (event) => {
|
|
7237
|
+
if (event.key === "Enter") {
|
|
7238
|
+
event.preventDefault();
|
|
7239
|
+
props.onQuickOpen();
|
|
7240
|
+
}
|
|
7241
|
+
};
|
|
7242
|
+
_el$16.$$input = (event) => {
|
|
7243
|
+
props.onQuickOpenInput(event.currentTarget.value);
|
|
7244
|
+
};
|
|
7245
|
+
addEventListener(_el$17, "click", props.onQuickOpen, true);
|
|
7246
|
+
createRenderEffect(() => _el$16.value = props.quickOpen);
|
|
7247
|
+
return _el$13;
|
|
7248
|
+
})()];
|
|
7249
|
+
}
|
|
7250
|
+
function FolderList(props) {
|
|
7251
|
+
return (() => {
|
|
7252
|
+
var _el$18 = _tmpl$4(), _el$20 = _el$18.firstChild.nextSibling;
|
|
7253
|
+
insert(_el$20, createComponent(Show, {
|
|
7254
|
+
get fallback() {
|
|
7255
|
+
return [
|
|
7256
|
+
(() => {
|
|
7257
|
+
var _el$22 = _tmpl$5();
|
|
7258
|
+
_el$22.$$click = () => {
|
|
7259
|
+
props.onSelect(null);
|
|
7260
|
+
};
|
|
7261
|
+
createRenderEffect(() => setAttribute(_el$22, "data-selected", !props.selectedFolderId ? "true" : void 0));
|
|
7262
|
+
return _el$22;
|
|
7263
|
+
})(),
|
|
7264
|
+
createComponent(For, {
|
|
7265
|
+
get each() {
|
|
7266
|
+
return props.folders;
|
|
7267
|
+
},
|
|
7268
|
+
children: (folder) => (() => {
|
|
7269
|
+
var _el$24 = _tmpl$7(), _el$25 = _el$24.firstChild, _el$26 = _el$25.nextSibling;
|
|
7270
|
+
_el$24.$$click = () => {
|
|
7271
|
+
props.onSelect(folder.id);
|
|
7272
|
+
};
|
|
7273
|
+
insert(_el$25, () => (folder.isFavourite ? "★ " : "") + (folder.name || folder.id));
|
|
7274
|
+
insert(_el$26, () => \`\${folder.documentCount} meetings\`);
|
|
7275
|
+
createRenderEffect(() => setAttribute(_el$24, "data-selected", folder.id === props.selectedFolderId ? "true" : void 0));
|
|
7276
|
+
return _el$24;
|
|
7277
|
+
})()
|
|
7278
|
+
}),
|
|
7279
|
+
createComponent(Show, {
|
|
7280
|
+
get when() {
|
|
7281
|
+
return props.folders.length === 0;
|
|
7282
|
+
},
|
|
7283
|
+
get children() {
|
|
7284
|
+
return _tmpl$6();
|
|
7285
|
+
}
|
|
7286
|
+
})
|
|
7287
|
+
];
|
|
7288
|
+
},
|
|
7289
|
+
get when() {
|
|
7290
|
+
return !props.error;
|
|
7291
|
+
},
|
|
7292
|
+
get children() {
|
|
7293
|
+
var _el$21 = _tmpl$3();
|
|
7294
|
+
insert(_el$21, () => props.error);
|
|
7295
|
+
return _el$21;
|
|
7296
|
+
}
|
|
7297
|
+
}));
|
|
7298
|
+
return _el$18;
|
|
7299
|
+
})();
|
|
7300
|
+
}
|
|
7301
|
+
function MeetingList(props) {
|
|
7302
|
+
const summary = () => currentFilterSummary({
|
|
7303
|
+
folders: props.folders,
|
|
7304
|
+
search: props.search,
|
|
7305
|
+
selectedFolderId: props.selectedFolderId,
|
|
7306
|
+
updatedFrom: props.updatedFrom,
|
|
7307
|
+
updatedTo: props.updatedTo
|
|
7308
|
+
});
|
|
7309
|
+
return (() => {
|
|
7310
|
+
var _el$27 = _tmpl$9();
|
|
7311
|
+
insert(_el$27, createComponent(Show, {
|
|
7312
|
+
get fallback() {
|
|
7313
|
+
return createComponent(Show, {
|
|
7314
|
+
get fallback() {
|
|
7315
|
+
return (() => {
|
|
7316
|
+
var _el$29 = _tmpl$0();
|
|
7317
|
+
insert(_el$29, (() => {
|
|
7318
|
+
var _c$ = memo(() => !!summary());
|
|
7319
|
+
return () => _c$() ? \`No meetings match \${summary()}.\` : "No meetings yet. Try Sync now.";
|
|
7320
|
+
})());
|
|
7321
|
+
return _el$29;
|
|
7322
|
+
})();
|
|
7323
|
+
},
|
|
7324
|
+
get when() {
|
|
7325
|
+
return props.meetings.length > 0;
|
|
7326
|
+
},
|
|
7327
|
+
get children() {
|
|
7328
|
+
return createComponent(For, {
|
|
7329
|
+
get each() {
|
|
7330
|
+
return props.meetings;
|
|
7331
|
+
},
|
|
7332
|
+
children: (meeting) => (() => {
|
|
7333
|
+
var _el$30 = _tmpl$1(), _el$31 = _el$30.firstChild, _el$32 = _el$31.nextSibling, _el$33 = _el$32.nextSibling;
|
|
7334
|
+
_el$30.$$click = () => {
|
|
7335
|
+
props.onSelect(meeting.id);
|
|
7336
|
+
};
|
|
7337
|
+
insert(_el$31, () => meeting.title || meeting.id);
|
|
7338
|
+
insert(_el$32, (() => {
|
|
7339
|
+
var _c$2 = memo(() => !!meeting.tags.length);
|
|
7340
|
+
return () => _c$2() ? meeting.tags.map((tag) => \`#\${tag}\`).join(" ") : "untagged";
|
|
7341
|
+
})());
|
|
7342
|
+
insert(_el$33, (() => {
|
|
7343
|
+
var _c$3 = memo(() => !!meeting.updatedAt);
|
|
7344
|
+
return () => _c$3() ? meeting.updatedAt.slice(0, 10) : "unknown";
|
|
7345
|
+
})());
|
|
7346
|
+
createRenderEffect(() => setAttribute(_el$30, "data-selected", meeting.id === props.selectedMeetingId ? "true" : void 0));
|
|
7347
|
+
return _el$30;
|
|
7348
|
+
})()
|
|
7349
|
+
});
|
|
7350
|
+
}
|
|
7351
|
+
});
|
|
7352
|
+
},
|
|
7353
|
+
get when() {
|
|
7354
|
+
return props.error;
|
|
7355
|
+
},
|
|
7356
|
+
get children() {
|
|
7357
|
+
var _el$28 = _tmpl$8();
|
|
7358
|
+
insert(_el$28, () => props.error);
|
|
7359
|
+
return _el$28;
|
|
7360
|
+
}
|
|
7361
|
+
}));
|
|
7362
|
+
return _el$27;
|
|
7363
|
+
})();
|
|
7364
|
+
}
|
|
7365
|
+
function AppStatePanel(props) {
|
|
7366
|
+
const syncStatus = () => {
|
|
7367
|
+
const sync = props.appState?.sync;
|
|
7368
|
+
if (!sync) return "idle";
|
|
7369
|
+
if (sync.running) return "running";
|
|
7370
|
+
if (sync.lastError) return "error";
|
|
7371
|
+
if (sync.lastCompletedAt) return \`last \${sync.lastCompletedAt.slice(11, 19)}\`;
|
|
7372
|
+
return "idle";
|
|
7373
|
+
};
|
|
7374
|
+
const authStatus = () => {
|
|
7375
|
+
if (!props.appState) return "Waiting for auth state…";
|
|
7376
|
+
return authModeLabel(props.appState.auth.mode);
|
|
7377
|
+
};
|
|
7378
|
+
return (() => {
|
|
7379
|
+
var _el$34 = _tmpl$10(), _el$35 = _el$34.firstChild;
|
|
7380
|
+
_el$35.firstChild;
|
|
7381
|
+
var _el$37 = _el$35.nextSibling;
|
|
7382
|
+
insert(_el$35, createComponent(Show, {
|
|
7383
|
+
get fallback() {
|
|
7384
|
+
return _tmpl$11();
|
|
7385
|
+
},
|
|
7386
|
+
get when() {
|
|
7387
|
+
return props.appState;
|
|
7388
|
+
},
|
|
7389
|
+
children: (appState) => (() => {
|
|
7390
|
+
var _el$39 = _tmpl$12(), _el$40 = _el$39.firstChild, _el$42 = _el$40.firstChild.nextSibling, _el$43 = _el$40.nextSibling, _el$45 = _el$43.firstChild.nextSibling, _el$46 = _el$43.nextSibling, _el$48 = _el$46.firstChild.nextSibling, _el$49 = _el$46.nextSibling, _el$51 = _el$49.firstChild.nextSibling, _el$52 = _el$49.nextSibling, _el$54 = _el$52.firstChild.nextSibling, _el$55 = _el$52.nextSibling, _el$57 = _el$55.firstChild.nextSibling, _el$58 = _el$55.nextSibling, _el$60 = _el$58.firstChild.nextSibling, _el$63 = _el$58.nextSibling.firstChild.nextSibling;
|
|
7391
|
+
insert(_el$42, () => appState().ui.surface);
|
|
7392
|
+
insert(_el$45, () => appState().ui.view);
|
|
7393
|
+
insert(_el$48, authStatus);
|
|
7394
|
+
insert(_el$51, syncStatus);
|
|
7395
|
+
insert(_el$54, (() => {
|
|
7396
|
+
var _c$4 = memo(() => !!appState().documents.loaded);
|
|
7397
|
+
return () => _c$4() ? String(appState().documents.count) : "not loaded";
|
|
7398
|
+
})());
|
|
7399
|
+
insert(_el$57, (() => {
|
|
7400
|
+
var _c$5 = memo(() => !!appState().folders.loaded);
|
|
7401
|
+
return () => _c$5() ? String(appState().folders.count) : "not loaded";
|
|
7402
|
+
})());
|
|
7403
|
+
insert(_el$60, (() => {
|
|
7404
|
+
var _c$6 = memo(() => !!appState().cache.loaded);
|
|
7405
|
+
return () => _c$6() ? \`\${appState().cache.transcriptCount} transcript sets\` : appState().cache.configured ? "configured" : "not configured";
|
|
7406
|
+
})());
|
|
7407
|
+
insert(_el$63, (() => {
|
|
7408
|
+
var _c$7 = memo(() => !!appState().index.loaded);
|
|
7409
|
+
return () => _c$7() ? \`\${appState().index.meetingCount} meetings\` : appState().index.available ? "available" : "not built";
|
|
7410
|
+
})());
|
|
7411
|
+
return _el$39;
|
|
7412
|
+
})()
|
|
7413
|
+
}), null);
|
|
7414
|
+
insert(_el$37, () => props.statusLabel);
|
|
7415
|
+
createRenderEffect(() => setAttribute(_el$37, "data-tone", props.statusTone));
|
|
7416
|
+
return _el$34;
|
|
7417
|
+
})();
|
|
7418
|
+
}
|
|
7419
|
+
function SecurityPanel(props) {
|
|
7420
|
+
return createComponent(Show, {
|
|
7421
|
+
get when() {
|
|
7422
|
+
return props.visible;
|
|
7423
|
+
},
|
|
7424
|
+
get children() {
|
|
7425
|
+
var _el$64 = _tmpl$13(), _el$67 = _el$64.firstChild.nextSibling.firstChild, _el$69 = _el$67.nextSibling.firstChild, _el$70 = _el$69.nextSibling;
|
|
7426
|
+
_el$67.$$keydown = (event) => {
|
|
7427
|
+
if (event.key === "Enter") {
|
|
7428
|
+
event.preventDefault();
|
|
7429
|
+
props.onUnlock();
|
|
7430
|
+
}
|
|
7431
|
+
};
|
|
7432
|
+
_el$67.$$input = (event) => {
|
|
7433
|
+
props.onPasswordChange(event.currentTarget.value);
|
|
7434
|
+
};
|
|
7435
|
+
addEventListener(_el$69, "click", props.onUnlock, true);
|
|
7436
|
+
addEventListener(_el$70, "click", props.onLock, true);
|
|
7437
|
+
createRenderEffect(() => _el$67.value = props.password);
|
|
7438
|
+
return _el$64;
|
|
7439
|
+
}
|
|
7440
|
+
});
|
|
6841
7441
|
}
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
7442
|
+
function AuthPanel(props) {
|
|
7443
|
+
return (() => {
|
|
7444
|
+
var _el$71 = _tmpl$14(), _el$73 = _el$71.firstChild.nextSibling;
|
|
7445
|
+
insert(_el$73, createComponent(Show, {
|
|
7446
|
+
get fallback() {
|
|
7447
|
+
return _tmpl$15();
|
|
7448
|
+
},
|
|
7449
|
+
get when() {
|
|
7450
|
+
return props.auth;
|
|
7451
|
+
},
|
|
7452
|
+
children: (auth) => (() => {
|
|
7453
|
+
var _el$75 = _tmpl$20(), _el$76 = _el$75.firstChild, _el$77 = _el$76.firstChild, _el$79 = _el$77.firstChild.nextSibling, _el$80 = _el$77.nextSibling, _el$82 = _el$80.firstChild.nextSibling, _el$83 = _el$80.nextSibling, _el$85 = _el$83.firstChild.nextSibling, _el$86 = _el$83.nextSibling, _el$88 = _el$86.firstChild.nextSibling, _el$91 = _el$86.nextSibling.firstChild.nextSibling, _el$99 = _el$76.nextSibling, _el$101 = _el$99.nextSibling.firstChild, _el$102 = _el$101.nextSibling, _el$103 = _el$102.nextSibling, _el$104 = _el$103.nextSibling, _el$105 = _el$104.nextSibling, _el$106 = _el$105.nextSibling, _el$107 = _el$106.nextSibling, _el$108 = _el$107.nextSibling;
|
|
7454
|
+
insert(_el$79, () => authModeLabel(auth().mode));
|
|
7455
|
+
insert(_el$82, () => auth().apiKeyAvailable ? "available" : "missing");
|
|
7456
|
+
insert(_el$85, () => auth().storedSessionAvailable ? "available" : "missing");
|
|
7457
|
+
insert(_el$88, () => auth().supabaseAvailable ? "available" : "missing");
|
|
7458
|
+
insert(_el$91, () => auth().refreshAvailable ? "available" : "missing");
|
|
7459
|
+
insert(_el$75, createComponent(Show, {
|
|
7460
|
+
get when() {
|
|
7461
|
+
return auth().clientId;
|
|
7462
|
+
},
|
|
7463
|
+
get children() {
|
|
7464
|
+
var _el$92 = _tmpl$16();
|
|
7465
|
+
_el$92.firstChild;
|
|
7466
|
+
insert(_el$92, () => auth().clientId, null);
|
|
7467
|
+
return _el$92;
|
|
7468
|
+
}
|
|
7469
|
+
}), _el$99);
|
|
7470
|
+
insert(_el$75, createComponent(Show, {
|
|
7471
|
+
get when() {
|
|
7472
|
+
return auth().signInMethod;
|
|
7473
|
+
},
|
|
7474
|
+
get children() {
|
|
7475
|
+
var _el$94 = _tmpl$17();
|
|
7476
|
+
_el$94.firstChild;
|
|
7477
|
+
insert(_el$94, () => auth().signInMethod, null);
|
|
7478
|
+
return _el$94;
|
|
7479
|
+
}
|
|
7480
|
+
}), _el$99);
|
|
7481
|
+
insert(_el$75, createComponent(Show, {
|
|
7482
|
+
get when() {
|
|
7483
|
+
return auth().supabasePath;
|
|
7484
|
+
},
|
|
7485
|
+
get children() {
|
|
7486
|
+
var _el$96 = _tmpl$18();
|
|
7487
|
+
_el$96.firstChild;
|
|
7488
|
+
insert(_el$96, () => auth().supabasePath, null);
|
|
7489
|
+
return _el$96;
|
|
7490
|
+
}
|
|
7491
|
+
}), _el$99);
|
|
7492
|
+
insert(_el$75, createComponent(Show, {
|
|
7493
|
+
get when() {
|
|
7494
|
+
return auth().lastError;
|
|
7495
|
+
},
|
|
7496
|
+
get children() {
|
|
7497
|
+
var _el$98 = _tmpl$19();
|
|
7498
|
+
insert(_el$98, () => auth().lastError);
|
|
7499
|
+
return _el$98;
|
|
7500
|
+
}
|
|
7501
|
+
}), _el$99);
|
|
7502
|
+
_el$101.$$input = (event) => {
|
|
7503
|
+
props.onApiKeyDraftChange(event.currentTarget.value);
|
|
7504
|
+
};
|
|
7505
|
+
addEventListener(_el$102, "click", props.onSaveApiKey, true);
|
|
7506
|
+
addEventListener(_el$103, "click", props.onImportDesktopSession, true);
|
|
7507
|
+
addEventListener(_el$104, "click", props.onRefresh, true);
|
|
7508
|
+
_el$105.$$click = () => {
|
|
7509
|
+
props.onSwitchMode("api-key");
|
|
7510
|
+
};
|
|
7511
|
+
_el$106.$$click = () => {
|
|
7512
|
+
props.onSwitchMode("stored-session");
|
|
7513
|
+
};
|
|
7514
|
+
_el$107.$$click = () => {
|
|
7515
|
+
props.onSwitchMode("supabase-file");
|
|
7516
|
+
};
|
|
7517
|
+
addEventListener(_el$108, "click", props.onLogout, true);
|
|
7518
|
+
createRenderEffect((_p$) => {
|
|
7519
|
+
var _v$ = !auth().supabaseAvailable, _v$2 = !auth().storedSessionAvailable || !auth().refreshAvailable, _v$3 = !auth().apiKeyAvailable || auth().mode === "api-key", _v$4 = !auth().storedSessionAvailable || auth().mode === "stored-session", _v$5 = !auth().supabaseAvailable || auth().mode === "supabase-file", _v$6 = !auth().apiKeyAvailable && !auth().storedSessionAvailable;
|
|
7520
|
+
_v$ !== _p$.e && (_el$103.disabled = _p$.e = _v$);
|
|
7521
|
+
_v$2 !== _p$.t && (_el$104.disabled = _p$.t = _v$2);
|
|
7522
|
+
_v$3 !== _p$.a && (_el$105.disabled = _p$.a = _v$3);
|
|
7523
|
+
_v$4 !== _p$.o && (_el$106.disabled = _p$.o = _v$4);
|
|
7524
|
+
_v$5 !== _p$.i && (_el$107.disabled = _p$.i = _v$5);
|
|
7525
|
+
_v$6 !== _p$.n && (_el$108.disabled = _p$.n = _v$6);
|
|
7526
|
+
return _p$;
|
|
7527
|
+
}, {
|
|
7528
|
+
e: void 0,
|
|
7529
|
+
t: void 0,
|
|
7530
|
+
a: void 0,
|
|
7531
|
+
o: void 0,
|
|
7532
|
+
i: void 0,
|
|
7533
|
+
n: void 0
|
|
7534
|
+
});
|
|
7535
|
+
createRenderEffect(() => _el$101.value = props.apiKeyDraft);
|
|
7536
|
+
return _el$75;
|
|
7537
|
+
})()
|
|
7538
|
+
}));
|
|
7539
|
+
return _el$71;
|
|
7540
|
+
})();
|
|
7541
|
+
}
|
|
7542
|
+
function ExportJobsPanel(props) {
|
|
7543
|
+
return (() => {
|
|
7544
|
+
var _el$109 = _tmpl$21(), _el$111 = _el$109.firstChild.nextSibling;
|
|
7545
|
+
insert(_el$111, createComponent(Show, {
|
|
7546
|
+
get when() {
|
|
7547
|
+
return props.jobs.length > 0;
|
|
7548
|
+
},
|
|
7549
|
+
get fallback() {
|
|
7550
|
+
return _tmpl$22();
|
|
7551
|
+
},
|
|
7552
|
+
get children() {
|
|
7553
|
+
return createComponent(For, {
|
|
7554
|
+
get each() {
|
|
7555
|
+
return props.jobs.slice(0, 6);
|
|
7556
|
+
},
|
|
7557
|
+
children: (job) => (() => {
|
|
7558
|
+
var _el$113 = _tmpl$25(), _el$114 = _el$113.firstChild, _el$115 = _el$114.firstChild, _el$116 = _el$115.firstChild, _el$117 = _el$116.firstChild, _el$118 = _el$116.nextSibling, _el$119 = _el$115.nextSibling, _el$120 = _el$114.nextSibling, _el$121 = _el$120.nextSibling;
|
|
7559
|
+
_el$121.firstChild;
|
|
7560
|
+
var _el$123 = _el$121.nextSibling;
|
|
7561
|
+
_el$123.firstChild;
|
|
7562
|
+
var _el$126 = _el$123.nextSibling;
|
|
7563
|
+
insert(_el$116, () => job.kind, _el$117);
|
|
7564
|
+
insert(_el$118, () => job.id);
|
|
7565
|
+
insert(_el$119, () => job.status);
|
|
7566
|
+
insert(_el$120, () => \`Format: \${job.format} • \${scopeLabel(job.scope)} • \${job.itemCount > 0 ? \`\${job.completedCount}/\${job.itemCount} items\` : "0 items"} • Written: \${job.written}\`);
|
|
7567
|
+
insert(_el$121, () => job.startedAt.slice(0, 19), null);
|
|
7568
|
+
insert(_el$123, () => job.outputDir, null);
|
|
7569
|
+
insert(_el$113, createComponent(Show, {
|
|
7570
|
+
get when() {
|
|
7571
|
+
return job.error;
|
|
7572
|
+
},
|
|
7573
|
+
get children() {
|
|
7574
|
+
var _el$125 = _tmpl$23();
|
|
7575
|
+
insert(_el$125, () => job.error);
|
|
7576
|
+
return _el$125;
|
|
7577
|
+
}
|
|
7578
|
+
}), _el$126);
|
|
7579
|
+
insert(_el$126, createComponent(Show, {
|
|
7580
|
+
get when() {
|
|
7581
|
+
return job.status !== "running";
|
|
7582
|
+
},
|
|
7583
|
+
get children() {
|
|
7584
|
+
var _el$127 = _tmpl$24();
|
|
7585
|
+
_el$127.$$click = () => {
|
|
7586
|
+
props.onRerun(job.id);
|
|
7587
|
+
};
|
|
7588
|
+
return _el$127;
|
|
7589
|
+
}
|
|
7590
|
+
}));
|
|
7591
|
+
createRenderEffect(() => setAttribute(_el$119, "data-status", job.status));
|
|
7592
|
+
return _el$113;
|
|
7593
|
+
})()
|
|
7594
|
+
});
|
|
7595
|
+
}
|
|
7596
|
+
}));
|
|
7597
|
+
return _el$109;
|
|
7598
|
+
})();
|
|
7599
|
+
}
|
|
7600
|
+
function Workspace(props) {
|
|
7601
|
+
const parsedTab = () => parseWorkspaceTab(props.tab);
|
|
7602
|
+
const details = () => {
|
|
7603
|
+
if (!props.selectedMeeting) return null;
|
|
7604
|
+
return workspaceBody(props.bundle, props.selectedMeeting, parsedTab());
|
|
7605
|
+
};
|
|
7606
|
+
return [(() => {
|
|
7607
|
+
var _el$128 = _tmpl$26(), _el$129 = _el$128.firstChild;
|
|
7608
|
+
insert(_el$128, createComponent(For, {
|
|
7609
|
+
each: [
|
|
7610
|
+
"notes",
|
|
7611
|
+
"transcript",
|
|
7612
|
+
"metadata",
|
|
7613
|
+
"raw"
|
|
7614
|
+
],
|
|
7615
|
+
children: (tab) => (() => {
|
|
7616
|
+
var _el$130 = _tmpl$27();
|
|
7617
|
+
_el$130.$$click = () => {
|
|
7618
|
+
props.onSelectTab(tab);
|
|
7619
|
+
};
|
|
7620
|
+
insert(_el$130, tab === "notes" ? "Notes" : tab === "transcript" ? "Transcript" : tab === "metadata" ? "Metadata" : "Raw");
|
|
7621
|
+
createRenderEffect(() => setAttribute(_el$130, "data-selected", parsedTab() === tab ? "true" : void 0));
|
|
7622
|
+
return _el$130;
|
|
7623
|
+
})()
|
|
7624
|
+
}), _el$129);
|
|
7625
|
+
return _el$128;
|
|
7626
|
+
})(), createComponent(Show, {
|
|
7627
|
+
get when() {
|
|
7628
|
+
return props.selectedMeeting;
|
|
7629
|
+
},
|
|
7630
|
+
get fallback() {
|
|
7631
|
+
return (() => {
|
|
7632
|
+
var _el$131 = _tmpl$28();
|
|
7633
|
+
insert(_el$131, () => props.detailError || "Select a meeting to inspect its notes and transcript.");
|
|
7634
|
+
return _el$131;
|
|
7635
|
+
})();
|
|
7636
|
+
},
|
|
7637
|
+
children: (meeting) => [(() => {
|
|
7638
|
+
var _el$132 = _tmpl$29(), _el$133 = _el$132.firstChild, _el$134 = _el$133.nextSibling, _el$135 = _el$134.nextSibling;
|
|
7639
|
+
insert(_el$133, () => \`ID: \${meeting().meeting.id}\`);
|
|
7640
|
+
insert(_el$134, () => \`Source: \${meeting().meeting.noteContentSource}\`);
|
|
7641
|
+
insert(_el$135, () => \`Transcript: \${meeting().meeting.transcriptSegmentCount} segments\`);
|
|
7642
|
+
return _el$132;
|
|
7643
|
+
})(), createComponent(Show, {
|
|
7644
|
+
get when() {
|
|
7645
|
+
return !props.detailError;
|
|
7646
|
+
},
|
|
7647
|
+
get fallback() {
|
|
7648
|
+
return (() => {
|
|
7649
|
+
var _el$144 = _tmpl$28();
|
|
7650
|
+
insert(_el$144, () => props.detailError);
|
|
7651
|
+
return _el$144;
|
|
7652
|
+
})();
|
|
7653
|
+
},
|
|
7654
|
+
get children() {
|
|
7655
|
+
var _el$136 = _tmpl$30(), _el$138 = _el$136.firstChild.firstChild, _el$140 = _el$138.firstChild.nextSibling, _el$142 = _el$138.nextSibling.firstChild, _el$143 = _el$142.nextSibling;
|
|
7656
|
+
insert(_el$140, () => metadataLines(meeting()));
|
|
7657
|
+
insert(_el$142, () => details()?.title);
|
|
7658
|
+
insert(_el$143, () => details()?.body);
|
|
7659
|
+
return _el$136;
|
|
7660
|
+
}
|
|
7661
|
+
})]
|
|
7662
|
+
})];
|
|
6850
7663
|
}
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
7664
|
+
delegateEvents([
|
|
7665
|
+
"input",
|
|
7666
|
+
"keydown",
|
|
7667
|
+
"click"
|
|
7668
|
+
]);
|
|
7669
|
+
//#endregion
|
|
7670
|
+
//#region src/web-app/App.tsx
|
|
7671
|
+
/** @jsxImportSource solid-js */
|
|
7672
|
+
var _tmpl$ = /* @__PURE__ */ template(\`<div class=shell><aside class="pane sidebar"></aside><main class="pane detail"><section class=toolbar><div class=toolbar-actions><button class="button button--primary"type=button>Sync now</button><button class="button button--secondary"type=button>Export Notes</button><button class="button button--secondary"type=button>Export Transcripts</button></div><p>Solid-powered web workspace on top of the same local server, sync loop, and shared app contracts.\`);
|
|
7673
|
+
function browserConfig() {
|
|
7674
|
+
return { passwordRequired: Boolean(window.__GRANOLA_SERVER__?.passwordRequired) };
|
|
7675
|
+
}
|
|
7676
|
+
async function requestJson(path, init) {
|
|
7677
|
+
const response = await fetch(path, init);
|
|
7678
|
+
const payload = await response.json().catch(() => ({}));
|
|
7679
|
+
if (!response.ok) {
|
|
7680
|
+
const error = typeof payload.error === "string" && payload.error.trim() ? payload.error : response.statusText || "Request failed";
|
|
7681
|
+
throw new Error(error);
|
|
7682
|
+
}
|
|
7683
|
+
return payload;
|
|
7684
|
+
}
|
|
7685
|
+
function App() {
|
|
7686
|
+
const startup = startupSelectionFromSearch(window.location.search);
|
|
7687
|
+
const [state, setState] = createStore({
|
|
7688
|
+
apiKeyDraft: "",
|
|
7689
|
+
appState: null,
|
|
7690
|
+
detailError: "",
|
|
7691
|
+
folderError: "",
|
|
7692
|
+
folders: [],
|
|
7693
|
+
listError: "",
|
|
7694
|
+
meetingSource: "live",
|
|
7695
|
+
meetings: [],
|
|
7696
|
+
quickOpen: "",
|
|
7697
|
+
search: "",
|
|
7698
|
+
selectedFolderId: startup.folderId || null,
|
|
7699
|
+
selectedMeetingBundle: null,
|
|
7700
|
+
selectedMeetingId: startup.meetingId || null,
|
|
7701
|
+
selectedMeeting: null,
|
|
7702
|
+
serverLocked: browserConfig().passwordRequired,
|
|
7703
|
+
serverPassword: "",
|
|
7704
|
+
sort: "updated-desc",
|
|
7705
|
+
statusLabel: browserConfig().passwordRequired ? "Server locked" : "Connecting…",
|
|
7706
|
+
statusTone: browserConfig().passwordRequired ? "error" : "idle",
|
|
7707
|
+
updatedFrom: "",
|
|
7708
|
+
updatedTo: "",
|
|
7709
|
+
workspaceTab: parseWorkspaceTab(startup.workspaceTab)
|
|
7710
|
+
});
|
|
7711
|
+
let client = null;
|
|
7712
|
+
let unsubscribe;
|
|
7713
|
+
const setStatus = (label, tone = "idle") => {
|
|
7714
|
+
setState({
|
|
7715
|
+
statusLabel: label,
|
|
7716
|
+
statusTone: tone
|
|
7717
|
+
});
|
|
7718
|
+
};
|
|
7719
|
+
const mergeAuthState = async (authState) => {
|
|
7720
|
+
if (!client) return;
|
|
7721
|
+
setState("appState", {
|
|
7722
|
+
...client.getState(),
|
|
7723
|
+
auth: authState ?? await client.inspectAuth()
|
|
7724
|
+
});
|
|
7725
|
+
};
|
|
7726
|
+
const detachClient = async () => {
|
|
7727
|
+
unsubscribe?.();
|
|
7728
|
+
unsubscribe = void 0;
|
|
7729
|
+
if (client) {
|
|
7730
|
+
await client.close().catch(() => void 0);
|
|
7731
|
+
client = null;
|
|
7732
|
+
}
|
|
7733
|
+
};
|
|
7734
|
+
const attachClient = async () => {
|
|
7735
|
+
await detachClient();
|
|
7736
|
+
client = await createGranolaServerClient(window.location.origin);
|
|
7737
|
+
unsubscribe = client.subscribe((event) => {
|
|
7738
|
+
setState("appState", event.state);
|
|
7739
|
+
});
|
|
7740
|
+
await mergeAuthState();
|
|
7741
|
+
};
|
|
7742
|
+
const loadFolders = async (refresh = false) => {
|
|
7743
|
+
if (!client) return;
|
|
7744
|
+
try {
|
|
7745
|
+
setState("folderError", "");
|
|
7746
|
+
const result = await client.listFolders({
|
|
7747
|
+
forceRefresh: refresh,
|
|
7748
|
+
limit: 100
|
|
7749
|
+
});
|
|
7750
|
+
setState("folders", result.folders);
|
|
7751
|
+
if (state.selectedFolderId && !result.folders.some((folder) => folder.id === state.selectedFolderId)) setState("selectedFolderId", null);
|
|
7752
|
+
} catch (error) {
|
|
7753
|
+
setState("folderError", error instanceof Error ? error.message : String(error));
|
|
7754
|
+
setState("folders", []);
|
|
7755
|
+
setState("selectedFolderId", null);
|
|
7756
|
+
}
|
|
7757
|
+
};
|
|
7758
|
+
const loadMeeting = async (meetingId) => {
|
|
7759
|
+
if (!client) return;
|
|
7760
|
+
setState("selectedMeetingId", meetingId);
|
|
7761
|
+
try {
|
|
7762
|
+
setState("detailError", "");
|
|
7763
|
+
const bundle = await client.getMeeting(meetingId);
|
|
7764
|
+
setState("selectedMeetingBundle", bundle);
|
|
7765
|
+
setState("selectedMeeting", bundle.meeting);
|
|
7766
|
+
} catch (error) {
|
|
7767
|
+
setState("selectedMeetingBundle", null);
|
|
7768
|
+
setState("selectedMeeting", null);
|
|
7769
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7770
|
+
}
|
|
7771
|
+
};
|
|
7772
|
+
const loadMeetings = async (options = {}) => {
|
|
7773
|
+
if (!client) return;
|
|
7774
|
+
try {
|
|
7775
|
+
setState("listError", "");
|
|
7776
|
+
const result = await client.listMeetings({
|
|
7777
|
+
folderId: state.selectedFolderId || void 0,
|
|
7778
|
+
forceRefresh: options.refresh,
|
|
7779
|
+
limit: 100,
|
|
7780
|
+
search: state.search || void 0,
|
|
7781
|
+
sort: state.sort,
|
|
7782
|
+
updatedFrom: state.updatedFrom || void 0,
|
|
7783
|
+
updatedTo: state.updatedTo || void 0
|
|
7784
|
+
});
|
|
7785
|
+
const preferredMeetingId = options.preferredMeetingId ?? state.selectedMeetingId;
|
|
7786
|
+
const nextMeetingId = selectMeetingId(result.meetings, preferredMeetingId);
|
|
7787
|
+
setState("meetings", result.meetings);
|
|
7788
|
+
setState("meetingSource", result.source);
|
|
7789
|
+
setState("selectedMeetingId", nextMeetingId);
|
|
7790
|
+
if (nextMeetingId) await loadMeeting(nextMeetingId);
|
|
7791
|
+
else {
|
|
7792
|
+
setState("selectedMeeting", null);
|
|
7793
|
+
setState("selectedMeetingBundle", null);
|
|
7794
|
+
setState("detailError", "");
|
|
7795
|
+
}
|
|
7796
|
+
} catch (error) {
|
|
7797
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7798
|
+
setState("listError", message);
|
|
7799
|
+
setState("selectedMeeting", null);
|
|
7800
|
+
setState("selectedMeetingBundle", null);
|
|
7801
|
+
setState("detailError", message);
|
|
7802
|
+
}
|
|
7803
|
+
};
|
|
7804
|
+
const refreshAll = async (forceRefresh = false) => {
|
|
7805
|
+
if (!client) await attachClient();
|
|
7806
|
+
setStatus(forceRefresh ? "Syncing…" : "Refreshing…", "busy");
|
|
7807
|
+
if (forceRefresh) await client?.sync({
|
|
7808
|
+
forceRefresh: true,
|
|
7809
|
+
foreground: true
|
|
7810
|
+
});
|
|
7811
|
+
await Promise.all([loadFolders(forceRefresh), mergeAuthState()]);
|
|
7812
|
+
await loadMeetings({ refresh: forceRefresh });
|
|
7813
|
+
setState("serverLocked", false);
|
|
7814
|
+
setStatus(forceRefresh ? "Sync complete" : state.meetingSource === "index" ? "Loaded from index" : "Connected", "ok");
|
|
7815
|
+
};
|
|
7816
|
+
const connectAndRefresh = async (forceRefresh = false) => {
|
|
7817
|
+
try {
|
|
7818
|
+
await refreshAll(forceRefresh);
|
|
7819
|
+
} catch (error) {
|
|
7820
|
+
setStatus("Connection failed", "error");
|
|
7821
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7822
|
+
}
|
|
7823
|
+
};
|
|
7824
|
+
const quickOpenMeeting = async () => {
|
|
7825
|
+
if (!client) return;
|
|
7826
|
+
const query = state.quickOpen.trim();
|
|
7827
|
+
if (!query) {
|
|
7828
|
+
setStatus("Enter a title or id", "error");
|
|
7829
|
+
return;
|
|
7830
|
+
}
|
|
7831
|
+
setStatus("Opening meeting…", "busy");
|
|
7832
|
+
try {
|
|
7833
|
+
const bundle = await client.findMeeting(query);
|
|
7834
|
+
setState("selectedFolderId", bundle.meeting.meeting.folders[0]?.id || null);
|
|
7835
|
+
setState("search", "");
|
|
7836
|
+
setState("updatedFrom", "");
|
|
7837
|
+
setState("updatedTo", "");
|
|
7838
|
+
await loadMeetings({ preferredMeetingId: bundle.document.id });
|
|
7839
|
+
setStatus("Connected", "ok");
|
|
7840
|
+
} catch (error) {
|
|
7841
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7842
|
+
setStatus("Quick open failed", "error");
|
|
7843
|
+
}
|
|
7844
|
+
};
|
|
7845
|
+
const saveApiKey = async () => {
|
|
7846
|
+
if (!client) return;
|
|
7847
|
+
if (!state.apiKeyDraft.trim()) {
|
|
7848
|
+
setStatus("Enter a Granola API key", "error");
|
|
7849
|
+
return;
|
|
7850
|
+
}
|
|
7851
|
+
setStatus("Saving API key…", "busy");
|
|
7852
|
+
try {
|
|
7853
|
+
const auth = await client.loginAuth({ apiKey: state.apiKeyDraft.trim() });
|
|
7854
|
+
setState("apiKeyDraft", "");
|
|
7855
|
+
await mergeAuthState(auth);
|
|
7856
|
+
await refreshAll();
|
|
7857
|
+
} catch (error) {
|
|
7858
|
+
await mergeAuthState();
|
|
7859
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7860
|
+
setStatus("API key save failed", "error");
|
|
7861
|
+
}
|
|
7862
|
+
};
|
|
7863
|
+
const importDesktopSession = async () => {
|
|
7864
|
+
if (!client) return;
|
|
7865
|
+
setStatus("Importing desktop session…", "busy");
|
|
7866
|
+
try {
|
|
7867
|
+
await mergeAuthState(await client.loginAuth());
|
|
7868
|
+
await refreshAll();
|
|
7869
|
+
} catch (error) {
|
|
7870
|
+
await mergeAuthState();
|
|
7871
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7872
|
+
setStatus("Auth import failed", "error");
|
|
7873
|
+
}
|
|
7874
|
+
};
|
|
7875
|
+
const refreshAuth = async () => {
|
|
7876
|
+
if (!client) return;
|
|
7877
|
+
setStatus("Refreshing session…", "busy");
|
|
7878
|
+
try {
|
|
7879
|
+
await mergeAuthState(await client.refreshAuth());
|
|
7880
|
+
await refreshAll();
|
|
7881
|
+
} catch (error) {
|
|
7882
|
+
await mergeAuthState();
|
|
7883
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7884
|
+
setStatus("Refresh failed", "error");
|
|
7885
|
+
}
|
|
7886
|
+
};
|
|
7887
|
+
const switchAuthMode = async (mode) => {
|
|
7888
|
+
if (!client) return;
|
|
7889
|
+
setStatus("Switching auth source…", "busy");
|
|
7890
|
+
try {
|
|
7891
|
+
await mergeAuthState(await client.switchAuthMode(mode));
|
|
7892
|
+
await refreshAll();
|
|
7893
|
+
} catch (error) {
|
|
7894
|
+
await mergeAuthState();
|
|
7895
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7896
|
+
setStatus("Switch failed", "error");
|
|
7897
|
+
}
|
|
7898
|
+
};
|
|
7899
|
+
const logout = async () => {
|
|
7900
|
+
if (!client) return;
|
|
7901
|
+
setStatus("Signing out…", "busy");
|
|
7902
|
+
try {
|
|
7903
|
+
await mergeAuthState(await client.logoutAuth());
|
|
7904
|
+
await refreshAll();
|
|
7905
|
+
} catch (error) {
|
|
7906
|
+
await mergeAuthState();
|
|
7907
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7908
|
+
setStatus("Sign out failed", "error");
|
|
7909
|
+
}
|
|
7910
|
+
};
|
|
7911
|
+
const exportNotes = async () => {
|
|
7912
|
+
if (!client) return;
|
|
7913
|
+
setStatus(state.selectedFolderId ? "Exporting folder notes…" : "Exporting notes…", "busy");
|
|
7914
|
+
try {
|
|
7915
|
+
await client.exportNotes("markdown", { folderId: state.selectedFolderId || void 0 });
|
|
7916
|
+
await refreshAll();
|
|
7917
|
+
} catch (error) {
|
|
7918
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7919
|
+
setStatus("Export failed", "error");
|
|
7920
|
+
}
|
|
7921
|
+
};
|
|
7922
|
+
const exportTranscripts = async () => {
|
|
7923
|
+
if (!client) return;
|
|
7924
|
+
setStatus(state.selectedFolderId ? "Exporting folder transcripts…" : "Exporting transcripts…", "busy");
|
|
7925
|
+
try {
|
|
7926
|
+
await client.exportTranscripts("text", { folderId: state.selectedFolderId || void 0 });
|
|
7927
|
+
await refreshAll();
|
|
7928
|
+
} catch (error) {
|
|
7929
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7930
|
+
setStatus("Export failed", "error");
|
|
7931
|
+
}
|
|
7932
|
+
};
|
|
7933
|
+
const rerunJob = async (jobId) => {
|
|
7934
|
+
if (!client) return;
|
|
7935
|
+
setStatus("Rerunning export…", "busy");
|
|
7936
|
+
try {
|
|
7937
|
+
await client.rerunExportJob(jobId);
|
|
7938
|
+
await refreshAll();
|
|
7939
|
+
} catch (error) {
|
|
7940
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7941
|
+
setStatus("Rerun failed", "error");
|
|
7942
|
+
}
|
|
7943
|
+
};
|
|
7944
|
+
const unlockServer = async () => {
|
|
7945
|
+
if (!state.serverPassword.trim()) {
|
|
7946
|
+
setStatus("Enter the server password", "error");
|
|
7947
|
+
return;
|
|
7948
|
+
}
|
|
7949
|
+
setStatus("Unlocking server…", "busy");
|
|
7950
|
+
try {
|
|
7951
|
+
await requestJson("/auth/unlock", {
|
|
7952
|
+
body: JSON.stringify({ password: state.serverPassword }),
|
|
7953
|
+
headers: { "content-type": "application/json" },
|
|
7954
|
+
method: "POST"
|
|
7955
|
+
});
|
|
7956
|
+
setState("serverPassword", "");
|
|
7957
|
+
setState("serverLocked", false);
|
|
7958
|
+
await connectAndRefresh(true);
|
|
7959
|
+
} catch (error) {
|
|
7960
|
+
setState("detailError", error instanceof Error ? error.message : String(error));
|
|
7961
|
+
setStatus("Unlock failed", "error");
|
|
7962
|
+
}
|
|
7963
|
+
};
|
|
7964
|
+
const lockServer = async () => {
|
|
7965
|
+
try {
|
|
7966
|
+
await requestJson("/auth/lock", { method: "POST" });
|
|
7967
|
+
} catch {}
|
|
7968
|
+
await detachClient();
|
|
7969
|
+
setState({
|
|
7970
|
+
appState: null,
|
|
7971
|
+
detailError: "",
|
|
7972
|
+
folderError: "",
|
|
7973
|
+
folders: [],
|
|
7974
|
+
listError: "",
|
|
7975
|
+
meetings: [],
|
|
7976
|
+
selectedFolderId: null,
|
|
7977
|
+
selectedMeeting: null,
|
|
7978
|
+
selectedMeetingBundle: null,
|
|
7979
|
+
selectedMeetingId: null,
|
|
7980
|
+
serverLocked: true,
|
|
7981
|
+
serverPassword: ""
|
|
7982
|
+
});
|
|
7983
|
+
setStatus("Server locked", "error");
|
|
7984
|
+
};
|
|
7985
|
+
createEffect(() => {
|
|
7986
|
+
const nextPath = buildBrowserUrlPath(window.location.href, {
|
|
7987
|
+
selectedFolderId: state.selectedFolderId,
|
|
7988
|
+
selectedMeetingId: state.selectedMeetingId,
|
|
7989
|
+
workspaceTab: state.workspaceTab
|
|
7990
|
+
});
|
|
7991
|
+
if (nextPath !== \`\${window.location.pathname}\${window.location.search}\${window.location.hash}\`) history.replaceState(null, "", nextPath);
|
|
7992
|
+
});
|
|
7993
|
+
onMount(() => {
|
|
7994
|
+
const onKeyDown = (event) => {
|
|
7995
|
+
const target = event.target;
|
|
7996
|
+
if (target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement) return;
|
|
7997
|
+
const nextTab = nextWorkspaceTab(state.workspaceTab, event.key);
|
|
7998
|
+
if (nextTab) setState("workspaceTab", nextTab);
|
|
7999
|
+
};
|
|
8000
|
+
document.addEventListener("keydown", onKeyDown);
|
|
8001
|
+
onCleanup(() => {
|
|
8002
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
8003
|
+
});
|
|
8004
|
+
if (!state.serverLocked) connectAndRefresh();
|
|
8005
|
+
});
|
|
8006
|
+
onCleanup(() => {
|
|
8007
|
+
detachClient();
|
|
8008
|
+
});
|
|
8009
|
+
return (() => {
|
|
8010
|
+
var _el$ = _tmpl$(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling, _el$4 = _el$3.firstChild, _el$6 = _el$4.firstChild.firstChild, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling;
|
|
8011
|
+
insert(_el$2, createComponent(ToolbarFilters, {
|
|
8012
|
+
onQuickOpen: () => {
|
|
8013
|
+
quickOpenMeeting();
|
|
8014
|
+
},
|
|
8015
|
+
onQuickOpenInput: (value) => {
|
|
8016
|
+
setState("quickOpen", value);
|
|
8017
|
+
},
|
|
8018
|
+
onSearchInput: (value) => {
|
|
8019
|
+
setState("search", value.trim());
|
|
8020
|
+
loadMeetings();
|
|
8021
|
+
},
|
|
8022
|
+
onSortChange: (value) => {
|
|
8023
|
+
setState("sort", value);
|
|
8024
|
+
loadMeetings();
|
|
8025
|
+
},
|
|
8026
|
+
onUpdatedFromChange: (value) => {
|
|
8027
|
+
setState("updatedFrom", value);
|
|
8028
|
+
loadMeetings();
|
|
8029
|
+
},
|
|
8030
|
+
onUpdatedToChange: (value) => {
|
|
8031
|
+
setState("updatedTo", value);
|
|
8032
|
+
loadMeetings();
|
|
8033
|
+
},
|
|
8034
|
+
get quickOpen() {
|
|
8035
|
+
return state.quickOpen;
|
|
8036
|
+
},
|
|
8037
|
+
get search() {
|
|
8038
|
+
return state.search;
|
|
8039
|
+
},
|
|
8040
|
+
get sort() {
|
|
8041
|
+
return state.sort;
|
|
8042
|
+
},
|
|
8043
|
+
get updatedFrom() {
|
|
8044
|
+
return state.updatedFrom;
|
|
8045
|
+
},
|
|
8046
|
+
get updatedTo() {
|
|
8047
|
+
return state.updatedTo;
|
|
8048
|
+
}
|
|
8049
|
+
}), null);
|
|
8050
|
+
insert(_el$2, createComponent(FolderList, {
|
|
8051
|
+
get error() {
|
|
8052
|
+
return state.folderError;
|
|
8053
|
+
},
|
|
8054
|
+
get folders() {
|
|
8055
|
+
return state.folders;
|
|
8056
|
+
},
|
|
8057
|
+
onSelect: (folderId) => {
|
|
8058
|
+
setState("selectedFolderId", folderId);
|
|
8059
|
+
setState("selectedMeetingId", null);
|
|
8060
|
+
setState("selectedMeeting", null);
|
|
8061
|
+
setState("selectedMeetingBundle", null);
|
|
8062
|
+
loadMeetings();
|
|
8063
|
+
},
|
|
8064
|
+
get selectedFolderId() {
|
|
8065
|
+
return state.selectedFolderId;
|
|
8066
|
+
}
|
|
8067
|
+
}), null);
|
|
8068
|
+
insert(_el$2, createComponent(MeetingList, {
|
|
8069
|
+
get error() {
|
|
8070
|
+
return state.listError;
|
|
8071
|
+
},
|
|
8072
|
+
get folders() {
|
|
8073
|
+
return state.folders;
|
|
8074
|
+
},
|
|
8075
|
+
get meetings() {
|
|
8076
|
+
return state.meetings;
|
|
8077
|
+
},
|
|
8078
|
+
onSelect: (meetingId) => {
|
|
8079
|
+
loadMeeting(meetingId);
|
|
8080
|
+
},
|
|
8081
|
+
get search() {
|
|
8082
|
+
return state.search;
|
|
8083
|
+
},
|
|
8084
|
+
get selectedFolderId() {
|
|
8085
|
+
return state.selectedFolderId;
|
|
8086
|
+
},
|
|
8087
|
+
get selectedMeetingId() {
|
|
8088
|
+
return state.selectedMeetingId;
|
|
8089
|
+
},
|
|
8090
|
+
get updatedFrom() {
|
|
8091
|
+
return state.updatedFrom;
|
|
8092
|
+
},
|
|
8093
|
+
get updatedTo() {
|
|
8094
|
+
return state.updatedTo;
|
|
8095
|
+
}
|
|
8096
|
+
}), null);
|
|
8097
|
+
insert(_el$3, createComponent(AppStatePanel, {
|
|
8098
|
+
get appState() {
|
|
8099
|
+
return state.appState;
|
|
8100
|
+
},
|
|
8101
|
+
get statusLabel() {
|
|
8102
|
+
return state.statusLabel;
|
|
8103
|
+
},
|
|
8104
|
+
get statusTone() {
|
|
8105
|
+
return state.statusTone;
|
|
8106
|
+
}
|
|
8107
|
+
}), _el$4);
|
|
8108
|
+
_el$6.$$click = () => {
|
|
8109
|
+
connectAndRefresh(true);
|
|
8110
|
+
};
|
|
8111
|
+
_el$7.$$click = () => {
|
|
8112
|
+
exportNotes();
|
|
8113
|
+
};
|
|
8114
|
+
_el$8.$$click = () => {
|
|
8115
|
+
exportTranscripts();
|
|
8116
|
+
};
|
|
8117
|
+
insert(_el$3, createComponent(SecurityPanel, {
|
|
8118
|
+
onLock: () => {
|
|
8119
|
+
lockServer();
|
|
8120
|
+
},
|
|
8121
|
+
onPasswordChange: (value) => {
|
|
8122
|
+
setState("serverPassword", value);
|
|
8123
|
+
},
|
|
8124
|
+
onUnlock: () => {
|
|
8125
|
+
unlockServer();
|
|
8126
|
+
},
|
|
8127
|
+
get password() {
|
|
8128
|
+
return state.serverPassword;
|
|
8129
|
+
},
|
|
8130
|
+
get visible() {
|
|
8131
|
+
return state.serverLocked;
|
|
8132
|
+
}
|
|
8133
|
+
}), null);
|
|
8134
|
+
insert(_el$3, createComponent(AuthPanel, {
|
|
8135
|
+
get apiKeyDraft() {
|
|
8136
|
+
return state.apiKeyDraft;
|
|
8137
|
+
},
|
|
8138
|
+
get auth() {
|
|
8139
|
+
return state.appState?.auth;
|
|
8140
|
+
},
|
|
8141
|
+
onApiKeyDraftChange: (value) => {
|
|
8142
|
+
setState("apiKeyDraft", value);
|
|
8143
|
+
},
|
|
8144
|
+
onImportDesktopSession: () => {
|
|
8145
|
+
importDesktopSession();
|
|
8146
|
+
},
|
|
8147
|
+
onLogout: () => {
|
|
8148
|
+
logout();
|
|
8149
|
+
},
|
|
8150
|
+
onRefresh: () => {
|
|
8151
|
+
refreshAuth();
|
|
8152
|
+
},
|
|
8153
|
+
onSaveApiKey: () => {
|
|
8154
|
+
saveApiKey();
|
|
8155
|
+
},
|
|
8156
|
+
onSwitchMode: (mode) => {
|
|
8157
|
+
switchAuthMode(mode);
|
|
8158
|
+
}
|
|
8159
|
+
}), null);
|
|
8160
|
+
insert(_el$3, createComponent(ExportJobsPanel, {
|
|
8161
|
+
get jobs() {
|
|
8162
|
+
return state.appState?.exports.jobs || [];
|
|
8163
|
+
},
|
|
8164
|
+
onRerun: (jobId) => {
|
|
8165
|
+
rerunJob(jobId);
|
|
8166
|
+
}
|
|
8167
|
+
}), null);
|
|
8168
|
+
insert(_el$3, createComponent(Workspace, {
|
|
8169
|
+
get bundle() {
|
|
8170
|
+
return state.selectedMeetingBundle;
|
|
8171
|
+
},
|
|
8172
|
+
get detailError() {
|
|
8173
|
+
return state.detailError;
|
|
8174
|
+
},
|
|
8175
|
+
onSelectTab: (tab) => {
|
|
8176
|
+
setState("workspaceTab", tab);
|
|
8177
|
+
},
|
|
8178
|
+
get selectedMeeting() {
|
|
8179
|
+
return state.selectedMeeting;
|
|
8180
|
+
},
|
|
8181
|
+
get tab() {
|
|
8182
|
+
return state.workspaceTab;
|
|
8183
|
+
}
|
|
8184
|
+
}), null);
|
|
8185
|
+
return _el$;
|
|
8186
|
+
})();
|
|
6866
8187
|
}
|
|
8188
|
+
delegateEvents(["click"]);
|
|
8189
|
+
//#endregion
|
|
8190
|
+
//#region src/web-app/main.tsx
|
|
8191
|
+
/** @jsxImportSource solid-js */
|
|
8192
|
+
var root = document.getElementById("granola-web-root");
|
|
8193
|
+
if (!root) throw new Error("Granola web root element not found");
|
|
8194
|
+
render(() => createComponent(App, {}), root);
|
|
8195
|
+
//#endregion
|
|
6867
8196
|
`;
|
|
6868
8197
|
//#endregion
|
|
8198
|
+
//#region src/web/assets.ts
|
|
8199
|
+
const granolaWebAssetPaths = {
|
|
8200
|
+
script: "/assets/granola-web-client.js",
|
|
8201
|
+
stylesheet: "/assets/granola-web-client.css"
|
|
8202
|
+
};
|
|
8203
|
+
function granolaWebAssetForPath(path) {
|
|
8204
|
+
switch (path) {
|
|
8205
|
+
case granolaWebAssetPaths.script: return {
|
|
8206
|
+
body: granolaWebClientJs,
|
|
8207
|
+
contentType: "text/javascript; charset=utf-8"
|
|
8208
|
+
};
|
|
8209
|
+
case granolaWebAssetPaths.stylesheet: return {
|
|
8210
|
+
body: granolaWebClientCss,
|
|
8211
|
+
contentType: "text/css; charset=utf-8"
|
|
8212
|
+
};
|
|
8213
|
+
default: return;
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8216
|
+
//#endregion
|
|
6869
8217
|
//#region src/server/web.ts
|
|
6870
8218
|
function renderGranolaWebPage(options = {}) {
|
|
6871
8219
|
return `<!doctype html>
|
|
@@ -6874,16 +8222,14 @@ function renderGranolaWebPage(options = {}) {
|
|
|
6874
8222
|
<meta charset="utf-8" />
|
|
6875
8223
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6876
8224
|
<title>Granola Toolkit</title>
|
|
6877
|
-
<
|
|
6878
|
-
${granolaWebStyles}
|
|
6879
|
-
</style>
|
|
8225
|
+
<link rel="stylesheet" href="${granolaWebAssetPaths.stylesheet}" />
|
|
6880
8226
|
</head>
|
|
6881
8227
|
<body>
|
|
6882
|
-
|
|
6883
|
-
<script
|
|
8228
|
+
<div id="granola-web-root"></div>
|
|
8229
|
+
<script>
|
|
6884
8230
|
window.__GRANOLA_SERVER__ = ${JSON.stringify({ passwordRequired: options.serverPasswordRequired ?? false })};
|
|
6885
|
-
${granolaWebClientScript}
|
|
6886
8231
|
<\/script>
|
|
8232
|
+
<script type="module" src="${granolaWebAssetPaths.script}"><\/script>
|
|
6887
8233
|
</body>
|
|
6888
8234
|
</html>`;
|
|
6889
8235
|
}
|
|
@@ -7028,7 +8374,7 @@ function isPasswordAuthenticated(request, password) {
|
|
|
7028
8374
|
return parseCookies(request)[PASSWORD_COOKIE_NAME] === password;
|
|
7029
8375
|
}
|
|
7030
8376
|
function publicRoute(path, enableWebClient) {
|
|
7031
|
-
return path === granolaTransportPaths.health || path === granolaTransportPaths.serverInfo || path === granolaTransportPaths.authUnlock || enableWebClient && path === granolaTransportPaths.root;
|
|
8377
|
+
return path === granolaTransportPaths.health || path === granolaTransportPaths.serverInfo || path === granolaTransportPaths.authUnlock || enableWebClient && (path === granolaTransportPaths.root || Boolean(granolaWebAssetForPath(path)));
|
|
7032
8378
|
}
|
|
7033
8379
|
async function startGranolaServer(app, options = {}) {
|
|
7034
8380
|
const enableWebClient = options.enableWebClient ?? false;
|
|
@@ -7091,6 +8437,18 @@ async function startGranolaServer(app, options = {}) {
|
|
|
7091
8437
|
sendHtml(response, renderGranolaWebPage({ serverPasswordRequired: Boolean(security.password) }), 200, originHeaders);
|
|
7092
8438
|
return;
|
|
7093
8439
|
}
|
|
8440
|
+
if (method === "GET" && enableWebClient) {
|
|
8441
|
+
const asset = granolaWebAssetForPath(path);
|
|
8442
|
+
if (asset) {
|
|
8443
|
+
response.writeHead(200, {
|
|
8444
|
+
"content-length": Buffer.byteLength(asset.body),
|
|
8445
|
+
"content-type": asset.contentType,
|
|
8446
|
+
...originHeaders
|
|
8447
|
+
});
|
|
8448
|
+
response.end(asset.body);
|
|
8449
|
+
return;
|
|
8450
|
+
}
|
|
8451
|
+
}
|
|
7094
8452
|
if (method === "GET" && path === granolaTransportPaths.health) {
|
|
7095
8453
|
sendJson(response, {
|
|
7096
8454
|
ok: true,
|