granola-toolkit 0.64.0 → 0.65.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/dist/cli.js +88 -23
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1959,6 +1959,79 @@ function buildGranolaTuiSummary(state, meetingSource) {
|
|
|
1959
1959
|
return `auth ${state.auth.mode === "api-key" ? "API key" : state.auth.mode === "stored-session" ? "stored session" : "supabase.json"} | ${state.documents.loaded ? `${state.documents.count} documents` : "documents pending"} | ${state.folders.loaded ? `${state.folders.count} folders` : "folders pending"} | ${state.cache.loaded ? `${state.cache.transcriptCount} transcript sets` : state.cache.configured ? "cache configured" : "cache missing"} | ${state.index.loaded ? `${state.index.meetingCount} indexed` : "index pending"} | ${state.sync.running ? "sync running" : state.sync.lastError ? "sync needs attention" : state.sync.lastCompletedAt ? `synced ${state.sync.lastCompletedAt.slice(11, 16)}` : "sync idle"} | ${state.automation.pendingArtefactCount ? `${state.automation.pendingArtefactCount} artefacts pending` : state.automation.pendingRunCount ? `${state.automation.pendingRunCount} runs pending` : state.automation.runCount ? `${state.automation.runCount} runs` : "automation idle"} | source ${meetingSource}`;
|
|
1960
1960
|
}
|
|
1961
1961
|
//#endregion
|
|
1962
|
+
//#region src/review-inbox.ts
|
|
1963
|
+
function issuePriority(issue) {
|
|
1964
|
+
if (issue.severity === "error" && issue.recoverable) return 0;
|
|
1965
|
+
if (issue.severity === "error") return 1;
|
|
1966
|
+
return 4;
|
|
1967
|
+
}
|
|
1968
|
+
function runPriority(_run) {
|
|
1969
|
+
return 3;
|
|
1970
|
+
}
|
|
1971
|
+
function artefactPriority(_artefact) {
|
|
1972
|
+
return 2;
|
|
1973
|
+
}
|
|
1974
|
+
function buildGranolaReviewInbox(options) {
|
|
1975
|
+
const items = [];
|
|
1976
|
+
for (const issue of options.issues) items.push({
|
|
1977
|
+
issue,
|
|
1978
|
+
key: `issue:${issue.id}`,
|
|
1979
|
+
kind: "issue",
|
|
1980
|
+
meetingId: issue.meetingId,
|
|
1981
|
+
priority: issuePriority(issue),
|
|
1982
|
+
status: issue.severity,
|
|
1983
|
+
subtitle: issue.kind,
|
|
1984
|
+
summary: issue.detail,
|
|
1985
|
+
timestamp: issue.detectedAt,
|
|
1986
|
+
title: issue.title
|
|
1987
|
+
});
|
|
1988
|
+
for (const artefact of options.artefacts) {
|
|
1989
|
+
if (artefact.status !== "generated") continue;
|
|
1990
|
+
items.push({
|
|
1991
|
+
artefact,
|
|
1992
|
+
key: `artefact:${artefact.id}`,
|
|
1993
|
+
kind: "artefact",
|
|
1994
|
+
meetingId: artefact.meetingId,
|
|
1995
|
+
priority: artefactPriority(artefact),
|
|
1996
|
+
status: artefact.status,
|
|
1997
|
+
subtitle: `${artefact.kind} • ${artefact.ruleName}`,
|
|
1998
|
+
summary: artefact.structured.summary || artefact.structured.markdown || artefact.structured.title,
|
|
1999
|
+
timestamp: artefact.updatedAt,
|
|
2000
|
+
title: artefact.structured.title
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
for (const run of options.runs) {
|
|
2004
|
+
if (run.status !== "pending") continue;
|
|
2005
|
+
items.push({
|
|
2006
|
+
key: `run:${run.id}`,
|
|
2007
|
+
kind: "run",
|
|
2008
|
+
meetingId: run.meetingId,
|
|
2009
|
+
priority: runPriority(run),
|
|
2010
|
+
run,
|
|
2011
|
+
status: run.status,
|
|
2012
|
+
subtitle: `${run.actionName} • ${run.ruleName}`,
|
|
2013
|
+
summary: run.prompt || run.result || run.error || run.eventKind,
|
|
2014
|
+
timestamp: run.startedAt,
|
|
2015
|
+
title: run.title
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
return items.sort((left, right) => left.priority - right.priority || right.timestamp.localeCompare(left.timestamp) || left.title.localeCompare(right.title) || left.key.localeCompare(right.key));
|
|
2019
|
+
}
|
|
2020
|
+
function summariseGranolaReviewInbox(items) {
|
|
2021
|
+
return items.reduce((summary, item) => {
|
|
2022
|
+
summary.total += 1;
|
|
2023
|
+
if (item.kind === "artefact") summary.artefacts += 1;
|
|
2024
|
+
else if (item.kind === "issue") summary.issues += 1;
|
|
2025
|
+
else summary.runs += 1;
|
|
2026
|
+
return summary;
|
|
2027
|
+
}, {
|
|
2028
|
+
artefacts: 0,
|
|
2029
|
+
issues: 0,
|
|
2030
|
+
runs: 0,
|
|
2031
|
+
total: 0
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
//#endregion
|
|
1962
2035
|
//#region src/tui/theme.ts
|
|
1963
2036
|
const RESET = "\x1B[0m";
|
|
1964
2037
|
function colour(code, text) {
|
|
@@ -2026,20 +2099,11 @@ var GranolaTuiAutomationOverlay = class {
|
|
|
2026
2099
|
}
|
|
2027
2100
|
invalidate() {}
|
|
2028
2101
|
get items() {
|
|
2029
|
-
return
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
...this.options.artefacts.map((artefact) => ({
|
|
2035
|
-
artefact,
|
|
2036
|
-
kind: "artefact"
|
|
2037
|
-
})),
|
|
2038
|
-
...this.options.runs.map((run) => ({
|
|
2039
|
-
kind: "run",
|
|
2040
|
-
run
|
|
2041
|
-
}))
|
|
2042
|
-
];
|
|
2102
|
+
return buildGranolaReviewInbox({
|
|
2103
|
+
artefacts: this.options.artefacts,
|
|
2104
|
+
issues: this.options.issues,
|
|
2105
|
+
runs: this.options.runs
|
|
2106
|
+
});
|
|
2043
2107
|
}
|
|
2044
2108
|
get selected() {
|
|
2045
2109
|
return this.items[this.#selectedIndex];
|
|
@@ -2081,21 +2145,22 @@ var GranolaTuiAutomationOverlay = class {
|
|
|
2081
2145
|
const innerWidth = Math.max(1, bodyWidth - 4);
|
|
2082
2146
|
const maxRuns = 6;
|
|
2083
2147
|
const lines = [];
|
|
2148
|
+
const summary = summariseGranolaReviewInbox(this.items);
|
|
2084
2149
|
lines.push(`+${"-".repeat(bodyWidth - 2)}+`);
|
|
2085
|
-
lines.push(frameLine$2(granolaTuiTheme.strong("
|
|
2150
|
+
lines.push(frameLine$2(granolaTuiTheme.strong("Review Inbox"), bodyWidth));
|
|
2086
2151
|
lines.push(frameLine$2("Enter/a approve r reject e rerun artefact Esc close", bodyWidth));
|
|
2152
|
+
lines.push(frameLine$2(granolaTuiTheme.dim(`${summary.total} items · ${summary.issues} issues · ${summary.artefacts} artefacts · ${summary.runs} approvals`), bodyWidth));
|
|
2087
2153
|
lines.push(frameLine$2("", bodyWidth));
|
|
2088
|
-
if (this.items.length === 0) lines.push(frameLine$2(granolaTuiTheme.dim("No
|
|
2154
|
+
if (this.items.length === 0) lines.push(frameLine$2(granolaTuiTheme.dim("No review items waiting."), bodyWidth));
|
|
2089
2155
|
else for (const [index, item] of this.items.slice(0, maxRuns).entries()) {
|
|
2090
2156
|
const selected = index === this.#selectedIndex;
|
|
2091
|
-
const title =
|
|
2157
|
+
const title = `${selected ? ">" : " "} ${item.title} · ${item.kind === "issue" ? issueSeverityLabel(item.issue) : item.kind === "artefact" ? artefactStatusLabel(item.artefact) : statusLabel(item.run)}`;
|
|
2092
2158
|
lines.push(frameLine$2(selected ? granolaTuiTheme.selected(title) : title, bodyWidth));
|
|
2093
|
-
lines.push(frameLine$2(
|
|
2094
|
-
const
|
|
2095
|
-
for (const line of wrapDetails(` ${details}`, innerWidth).slice(0, 2)) lines.push(frameLine$2(line, bodyWidth));
|
|
2159
|
+
lines.push(frameLine$2(` ${item.subtitle}${item.meetingId ? ` · ${item.meetingId}` : ""}`, bodyWidth));
|
|
2160
|
+
for (const line of wrapDetails(` ${item.summary}`, innerWidth).slice(0, 2)) lines.push(frameLine$2(line, bodyWidth));
|
|
2096
2161
|
lines.push(frameLine$2("", bodyWidth));
|
|
2097
2162
|
}
|
|
2098
|
-
lines.push(frameLine$2(granolaTuiTheme.dim("
|
|
2163
|
+
lines.push(frameLine$2(granolaTuiTheme.dim("Issues stay first, then generated artefacts, then pending approvals."), bodyWidth));
|
|
2099
2164
|
lines.push(`+${"-".repeat(bodyWidth - 2)}+`);
|
|
2100
2165
|
return lines;
|
|
2101
2166
|
}
|
|
@@ -2906,7 +2971,7 @@ var GranolaTuiWorkspace = class {
|
|
|
2906
2971
|
minWidth: 56,
|
|
2907
2972
|
width: "76%"
|
|
2908
2973
|
});
|
|
2909
|
-
this.setStatus("
|
|
2974
|
+
this.setStatus("Review inbox");
|
|
2910
2975
|
}
|
|
2911
2976
|
handleInput(data) {
|
|
2912
2977
|
if (matchesKey(data, "ctrl+c") || matchesKey(data, "q")) {
|
|
@@ -9856,7 +9921,7 @@ async function openExternalUrl(url, options = {}) {
|
|
|
9856
9921
|
//#endregion
|
|
9857
9922
|
//#region src/web/generated.ts
|
|
9858
9923
|
const granolaWebClientCss = ":root {\n --bg: #f2ede2;\n --panel: rgba(255, 252, 247, 0.86);\n --panel-strong: #fffaf2;\n --line: rgba(36, 39, 44, 0.12);\n --ink: #1d242c;\n --muted: #5d6b77;\n --accent: #0d6a6d;\n --accent-soft: rgba(13, 106, 109, 0.12);\n --warm: #a34f2f;\n --ok: #246b4f;\n --error: #9d2c2c;\n --shadow: 0 24px 80px rgba(40, 32, 16, 0.12);\n --radius: 24px;\n --mono: \"SF Mono\", \"IBM Plex Mono\", \"Cascadia Code\", monospace;\n --serif: \"Iowan Old Style\", \"Palatino Linotype\", \"Book Antiqua\", Georgia, serif;\n --sans: \"Avenir Next\", \"Segoe UI\", sans-serif;\n}\n\n* {\n box-sizing: border-box;\n}\n\nbody {\n margin: 0;\n min-height: 100vh;\n font-family: var(--sans);\n color: var(--ink);\n background:\n radial-gradient(circle at top left, rgba(163, 79, 47, 0.18), transparent 32%),\n radial-gradient(circle at right 12%, rgba(13, 106, 109, 0.16), transparent 28%),\n linear-gradient(180deg, #f8f2e8 0%, var(--bg) 100%);\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\n#granola-web-root {\n min-height: 100vh;\n}\n\n.shell {\n display: grid;\n grid-template-columns: 320px minmax(0, 1fr);\n gap: 18px;\n min-height: 100vh;\n padding: 24px;\n}\n\n.shell--onboarding {\n grid-template-columns: minmax(0, 960px);\n justify-content: center;\n}\n\n.pane {\n background: var(--panel);\n backdrop-filter: blur(18px);\n border: 1px solid var(--line);\n border-radius: var(--radius);\n box-shadow: var(--shadow);\n}\n\n.sidebar {\n display: grid;\n grid-template-rows: auto auto auto auto 1fr;\n overflow: hidden;\n}\n\n.hero,\n.toolbar,\n.detail-head,\n.folder-panel {\n padding: 22px 24px;\n border-bottom: 1px solid var(--line);\n}\n\n.hero h1 {\n margin: 0;\n font-family: var(--serif);\n font-size: clamp(2rem, 3vw, 2.8rem);\n font-weight: 600;\n letter-spacing: -0.04em;\n}\n\n.hero p,\n.toolbar p {\n margin: 8px 0 0;\n color: var(--muted);\n line-height: 1.5;\n}\n\n.search,\n.select,\n.field-input,\n.input {\n width: 100%;\n margin-top: 16px;\n padding: 12px 14px;\n border: 1px solid var(--line);\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.7);\n color: var(--ink);\n}\n\n.field-row {\n display: grid;\n gap: 10px;\n margin-top: 12px;\n}\n\n.field-row--inline {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n\n.field-label {\n display: block;\n margin-bottom: 6px;\n color: var(--muted);\n font-size: 0.78rem;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.folder-panel {\n display: grid;\n gap: 14px;\n}\n\n.folder-panel__head h2 {\n margin: 0;\n font-size: 0.92rem;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.folder-panel__head p {\n margin: 6px 0 0;\n color: var(--muted);\n font-size: 0.9rem;\n}\n\n.folder-list,\n.jobs-list,\n.saved-filter-list {\n display: grid;\n gap: 10px;\n}\n\n.saved-filter-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n\n.folder-row,\n.meeting-row,\n.saved-filter-card__main {\n width: 100%;\n display: grid;\n gap: 4px;\n text-align: left;\n padding: 12px 14px;\n border: 1px solid transparent;\n border-radius: 16px;\n background: rgba(255, 255, 255, 0.72);\n color: inherit;\n cursor: pointer;\n transition:\n transform 140ms ease,\n border-color 140ms ease,\n background 140ms ease;\n}\n\n.meeting-row {\n margin: 0 0 10px;\n padding: 14px 16px;\n border-radius: 18px;\n}\n\n.saved-filter-card {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 10px;\n align-items: start;\n}\n\n.saved-filter-card__main {\n margin: 0;\n}\n\n.saved-filter-card__remove {\n border: 1px solid var(--line);\n border-radius: 999px;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.72);\n color: var(--muted);\n cursor: pointer;\n font-weight: 700;\n}\n\n.folder-row:hover,\n.folder-row[data-selected=\"true\"],\n.saved-filter-card__main:hover {\n transform: translateY(-1px);\n border-color: rgba(163, 79, 47, 0.26);\n background: var(--panel-strong);\n}\n\n.meeting-row:hover,\n.meeting-row[data-selected=\"true\"] {\n transform: translateY(-1px);\n border-color: rgba(13, 106, 109, 0.25);\n background: var(--panel-strong);\n}\n\n.folder-row__title,\n.job-card__title,\n.saved-filter-card__title {\n font-weight: 700;\n}\n\n.meeting-row__title {\n font-weight: 600;\n}\n\n.folder-row__meta,\n.meeting-row__meta,\n.auth-card__meta,\n.job-card__meta,\n.folder-empty,\n.job-empty,\n.meeting-empty,\n.saved-filter-card__meta {\n color: var(--muted);\n font-size: 0.9rem;\n}\n\n.folder-empty--error,\n.meeting-empty--error,\n.auth-card__error {\n color: var(--error);\n}\n\n.meeting-list {\n padding: 14px;\n overflow: auto;\n}\n\n.detail {\n display: grid;\n grid-template-rows: auto auto 1fr;\n min-width: 0;\n}\n\n.detail--onboarding {\n align-content: start;\n}\n\n.detail-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 18px;\n}\n\n.detail-head h2 {\n margin: 0;\n font-family: var(--serif);\n font-size: clamp(1.8rem, 2.4vw, 2.4rem);\n font-weight: 600;\n}\n\n.state-badge {\n padding: 10px 14px;\n border-radius: 999px;\n background: var(--accent-soft);\n color: var(--accent);\n font-size: 0.92rem;\n font-weight: 700;\n}\n\n.state-badge[data-tone=\"busy\"] {\n background: rgba(163, 79, 47, 0.12);\n color: var(--warm);\n}\n\n.state-badge[data-tone=\"error\"] {\n background: rgba(157, 44, 44, 0.12);\n color: var(--error);\n}\n\n.state-badge[data-tone=\"ok\"] {\n background: rgba(36, 107, 79, 0.12);\n color: var(--ok);\n}\n\n.toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 14px;\n}\n\n.toolbar-actions,\n.auth-card__actions,\n.job-card__actions {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n\n.toolbar-form {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 10px;\n width: min(440px, 100%);\n}\n\n.security-panel,\n.auth-panel,\n.jobs-panel {\n padding: 0 24px 18px;\n}\n\n.onboarding-panel {\n display: grid;\n gap: 20px;\n padding: 0 24px 24px;\n}\n\n.onboarding-panel__hero {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 280px;\n gap: 18px;\n padding: 24px;\n border: 1px solid var(--line);\n border-radius: 24px;\n background:\n radial-gradient(circle at top right, rgba(13, 106, 109, 0.14), transparent 35%),\n linear-gradient(180deg, rgba(255, 255, 255, 0.82), rgba(255, 255, 255, 0.72));\n}\n\n.onboarding-panel__eyebrow {\n margin: 0 0 10px;\n color: var(--warm);\n font-size: 0.82rem;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n\n.onboarding-panel__hero h2 {\n margin: 0;\n font-family: var(--serif);\n font-size: clamp(2rem, 3vw, 2.8rem);\n font-weight: 600;\n letter-spacing: -0.04em;\n}\n\n.onboarding-panel__hero p {\n margin: 10px 0 0;\n color: var(--muted);\n line-height: 1.55;\n}\n\n.onboarding-panel__summary {\n display: grid;\n align-content: start;\n gap: 10px;\n padding: 18px;\n border: 1px solid var(--line);\n border-radius: 20px;\n background: rgba(255, 255, 255, 0.82);\n}\n\n.onboarding-steps {\n display: grid;\n gap: 16px;\n}\n\n.onboarding-step {\n display: grid;\n gap: 14px;\n padding: 22px 24px;\n border: 1px solid var(--line);\n border-radius: 24px;\n background: rgba(255, 255, 255, 0.76);\n}\n\n.onboarding-step[data-complete=\"true\"] {\n background: rgba(255, 255, 255, 0.9);\n}\n\n.onboarding-step__head {\n display: flex;\n flex-wrap: wrap;\n align-items: start;\n justify-content: space-between;\n gap: 12px;\n}\n\n.onboarding-step__number {\n display: block;\n margin-bottom: 8px;\n color: var(--muted);\n font-size: 0.78rem;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.onboarding-step h3 {\n margin: 0;\n font-size: 1.15rem;\n}\n\n.onboarding-step p {\n margin: 0;\n color: var(--muted);\n line-height: 1.55;\n}\n\n.onboarding-step__body {\n display: grid;\n gap: 14px;\n}\n\n.onboarding-providers {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 12px;\n}\n\n.onboarding-provider {\n width: 100%;\n display: grid;\n gap: 8px;\n text-align: left;\n padding: 16px;\n border: 1px solid var(--line);\n border-radius: 18px;\n background: rgba(255, 255, 255, 0.78);\n color: inherit;\n cursor: pointer;\n}\n\n.onboarding-provider[data-selected=\"true\"] {\n border-color: rgba(13, 106, 109, 0.26);\n background: var(--panel-strong);\n box-shadow: inset 0 0 0 1px rgba(13, 106, 109, 0.16);\n}\n\n.onboarding-provider__title {\n font-weight: 700;\n}\n\n.onboarding-provider__body {\n color: var(--muted);\n font-size: 0.9rem;\n line-height: 1.45;\n}\n\n.security-panel__head h3,\n.auth-panel__head h3,\n.jobs-panel__head h3 {\n margin: 0;\n font-size: 0.92rem;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.security-panel__head p,\n.auth-panel__head p,\n.jobs-panel__head p {\n margin: 6px 0 0;\n color: var(--muted);\n font-size: 0.9rem;\n}\n\n.security-panel__body,\n.auth-panel__body {\n display: grid;\n gap: 12px;\n margin-top: 14px;\n}\n\n.auth-card,\n.job-card {\n display: grid;\n gap: 12px;\n padding: 14px 16px;\n border: 1px solid var(--line);\n border-radius: 18px;\n background: rgba(255, 255, 255, 0.72);\n}\n\n.job-card--button {\n width: 100%;\n text-align: left;\n cursor: pointer;\n}\n\n.job-card--button[data-selected=\"true\"] {\n border-color: rgba(13, 106, 109, 0.26);\n background: var(--panel-strong);\n}\n\n.job-card__head {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n}\n\n.job-card__status {\n padding: 6px 10px;\n border-radius: 999px;\n background: var(--accent-soft);\n color: var(--accent);\n font-size: 0.82rem;\n font-weight: 700;\n}\n\n.job-card__status[data-status=\"running\"] {\n background: rgba(163, 79, 47, 0.12);\n color: var(--warm);\n}\n\n.job-card__status[data-status=\"failed\"] {\n background: rgba(157, 44, 44, 0.12);\n color: var(--error);\n}\n\n.job-card__status[data-status=\"error\"] {\n background: rgba(157, 44, 44, 0.12);\n color: var(--error);\n}\n\n.job-card__status[data-status=\"completed\"] {\n background: rgba(36, 107, 79, 0.12);\n color: var(--ok);\n}\n\n.job-card__status[data-status=\"warning\"] {\n background: rgba(163, 79, 47, 0.12);\n color: var(--warm);\n}\n\n.job-card__status[data-status=\"approved\"] {\n background: rgba(36, 107, 79, 0.12);\n color: var(--ok);\n}\n\n.job-card__status[data-status=\"generated\"] {\n background: rgba(163, 79, 47, 0.12);\n color: var(--warm);\n}\n\n.job-card__status[data-status=\"rejected\"] {\n background: rgba(157, 44, 44, 0.12);\n color: var(--error);\n}\n\n.job-card__status[data-status=\"superseded\"] {\n background: rgba(93, 107, 119, 0.12);\n color: var(--muted);\n}\n\n.workspace-tabs {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 10px;\n padding: 0 24px 18px;\n}\n\n.control-deck {\n display: grid;\n gap: 14px;\n padding: 0 24px 24px;\n}\n\n.control-deck__tabs {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n\n.control-deck__body,\n.control-deck__panel {\n display: grid;\n gap: 14px;\n}\n\n.workspace-tab,\n.button {\n border: 1px solid var(--line);\n border-radius: 999px;\n padding: 10px 14px;\n background: rgba(255, 255, 255, 0.72);\n color: var(--ink);\n cursor: pointer;\n font-weight: 700;\n}\n\n.button {\n padding: 12px 16px;\n}\n\n.workspace-tab[data-selected=\"true\"],\n.button--primary {\n background: var(--ink);\n color: #fff;\n border-color: var(--ink);\n}\n\n.button--secondary {\n background: rgba(255, 255, 255, 0.72);\n}\n\n.button:disabled {\n cursor: not-allowed;\n opacity: 0.56;\n}\n\n.workspace-hint {\n margin-left: auto;\n color: var(--muted);\n font-size: 0.86rem;\n}\n\n.status-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 14px;\n}\n\n.status-label {\n display: block;\n margin-bottom: 6px;\n color: var(--muted);\n font-size: 0.78rem;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.detail-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n padding: 0 24px 18px;\n}\n\n.detail-chip {\n padding: 10px 12px;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.72);\n border: 1px solid var(--line);\n color: var(--muted);\n font-size: 0.88rem;\n}\n\n.detail-body {\n padding: 0 24px 24px;\n overflow: auto;\n}\n\n.review-panel {\n padding: 0 24px 24px;\n}\n\n.harness-panel {\n padding: 0 24px 24px;\n}\n\n.harness-grid {\n display: grid;\n grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);\n gap: 18px;\n}\n\n.harness-toolbar,\n.harness-status-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n margin-bottom: 16px;\n}\n\n.harness-status-row h2 {\n margin: 0;\n font-size: 1.1rem;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n\n.harness-test-result {\n display: grid;\n gap: 14px;\n margin-top: 16px;\n}\n\n.review-body {\n display: grid;\n gap: 18px;\n}\n\n.review-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 18px;\n}\n\n.workspace-grid {\n display: grid;\n grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);\n gap: 18px;\n}\n\n.detail-section {\n margin-bottom: 20px;\n padding: 20px;\n background: rgba(255, 255, 255, 0.72);\n border: 1px solid var(--line);\n border-radius: 20px;\n}\n\n.detail-section h2 {\n margin: 0 0 14px;\n font-size: 1rem;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.detail-section h3 {\n margin: 18px 0 10px;\n font-size: 0.95rem;\n}\n\n.detail-pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-word;\n font-family: var(--mono);\n line-height: 1.55;\n}\n\n.detail-list {\n margin: 0;\n padding-left: 18px;\n display: grid;\n gap: 10px;\n}\n\n.field-input--plain,\n.review-textarea {\n width: 100%;\n margin-top: 0;\n padding: 12px 14px;\n border: 1px solid var(--line);\n border-radius: 18px;\n background: rgba(255, 255, 255, 0.92);\n color: var(--ink);\n}\n\n.review-textarea {\n min-height: 220px;\n resize: vertical;\n}\n\n.review-textarea--summary {\n min-height: 96px;\n}\n\n.review-history {\n margin-bottom: 0;\n}\n\n.empty {\n margin: 24px;\n padding: 24px;\n border-radius: 20px;\n background: rgba(255, 255, 255, 0.72);\n color: var(--muted);\n}\n\n@media (max-width: 1024px) {\n .shell,\n .onboarding-panel__hero,\n .onboarding-providers,\n .harness-grid,\n .review-grid,\n .workspace-grid {\n grid-template-columns: 1fr;\n }\n}\n/*$vite$:1*/";
|
|
9859
|
-
const granolaWebClientJs = "//#region node_modules/solid-js/dist/solid.js\nvar sharedConfig = {\n context: void 0,\n registry: void 0,\n effects: void 0,\n done: false,\n getContextId() {\n return getContextId(this.context.count);\n },\n getNextContextId() {\n return getContextId(this.context.count++);\n }\n};\nfunction getContextId(count) {\n const num = String(count), len = num.length - 1;\n return sharedConfig.context.id + (len ? String.fromCharCode(96 + len) : \"\") + num;\n}\nfunction setHydrateContext(context) {\n sharedConfig.context = context;\n}\nfunction nextHydrateContext() {\n return {\n ...sharedConfig.context,\n id: sharedConfig.getNextContextId(),\n count: 0\n };\n}\nvar equalFn = (a, b) => a === b;\nvar $PROXY = Symbol(\"solid-proxy\");\nvar $TRACK = Symbol(\"solid-track\");\nvar signalOptions = { equals: equalFn };\nvar ERROR = null;\nvar runEffects = runQueue;\nvar STALE = 1;\nvar PENDING = 2;\nvar UNOWNED = {\n owned: null,\n cleanups: null,\n context: null,\n owner: null\n};\nvar Owner = null;\nvar Transition = null;\nvar Scheduler = null;\nvar ExternalSourceConfig = null;\nvar Listener = null;\nvar Updates = null;\nvar Effects = null;\nvar ExecCount = 0;\nfunction createRoot(fn, detachedOwner) {\n const listener = Listener, owner = Owner, unowned = fn.length === 0, current = detachedOwner === void 0 ? owner : detachedOwner, root = unowned ? UNOWNED : {\n owned: null,\n cleanups: null,\n context: current ? current.context : null,\n owner: current\n }, updateFn = unowned ? fn : () => fn(() => untrack(() => cleanNode(root)));\n Owner = root;\n Listener = null;\n try {\n return runUpdates(updateFn, true);\n } finally {\n Listener = listener;\n Owner = owner;\n }\n}\nfunction createSignal(value, options) {\n options = options ? Object.assign({}, signalOptions, options) : signalOptions;\n const s = {\n value,\n observers: null,\n observerSlots: null,\n comparator: options.equals || void 0\n };\n const setter = (value) => {\n if (typeof value === \"function\") if (Transition && Transition.running && Transition.sources.has(s)) value = value(s.tValue);\n else value = value(s.value);\n return writeSignal(s, value);\n };\n return [readSignal.bind(s), setter];\n}\nfunction createRenderEffect(fn, value, options) {\n const c = createComputation(fn, value, false, STALE);\n if (Scheduler && Transition && Transition.running) Updates.push(c);\n else updateComputation(c);\n}\nfunction createEffect(fn, value, options) {\n runEffects = runUserEffects;\n const c = createComputation(fn, value, false, STALE), s = SuspenseContext && useContext(SuspenseContext);\n if (s) c.suspense = s;\n if (!options || !options.render) c.user = true;\n Effects ? Effects.push(c) : updateComputation(c);\n}\nfunction createMemo(fn, value, options) {\n options = options ? Object.assign({}, signalOptions, options) : signalOptions;\n const c = createComputation(fn, value, true, 0);\n c.observers = null;\n c.observerSlots = null;\n c.comparator = options.equals || void 0;\n if (Scheduler && Transition && Transition.running) {\n c.tState = STALE;\n Updates.push(c);\n } else updateComputation(c);\n return readSignal.bind(c);\n}\nfunction batch(fn) {\n return runUpdates(fn, false);\n}\nfunction untrack(fn) {\n if (!ExternalSourceConfig && Listener === null) return fn();\n const listener = Listener;\n Listener = null;\n try {\n if (ExternalSourceConfig) return ExternalSourceConfig.untrack(fn);\n return fn();\n } finally {\n Listener = listener;\n }\n}\nfunction onMount(fn) {\n createEffect(() => untrack(fn));\n}\nfunction onCleanup(fn) {\n if (Owner === null);\n else if (Owner.cleanups === null) Owner.cleanups = [fn];\n else Owner.cleanups.push(fn);\n return fn;\n}\nfunction getListener() {\n return Listener;\n}\nfunction startTransition(fn) {\n if (Transition && Transition.running) {\n fn();\n return Transition.done;\n }\n const l = Listener;\n const o = Owner;\n return Promise.resolve().then(() => {\n Listener = l;\n Owner = o;\n let t;\n if (Scheduler || SuspenseContext) {\n t = Transition || (Transition = {\n sources: /* @__PURE__ */ new Set(),\n effects: [],\n promises: /* @__PURE__ */ new Set(),\n disposed: /* @__PURE__ */ new Set(),\n queue: /* @__PURE__ */ new Set(),\n running: true\n });\n t.done || (t.done = new Promise((res) => t.resolve = res));\n t.running = true;\n }\n runUpdates(fn, false);\n Listener = Owner = null;\n return t ? t.done : void 0;\n });\n}\nvar [transPending, setTransPending] = /* @__PURE__ */ createSignal(false);\nfunction useContext(context) {\n let value;\n return Owner && Owner.context && (value = Owner.context[context.id]) !== void 0 ? value : context.defaultValue;\n}\nvar SuspenseContext;\nfunction readSignal() {\n const runningTransition = Transition && Transition.running;\n if (this.sources && (runningTransition ? this.tState : this.state)) if ((runningTransition ? this.tState : this.state) === STALE) updateComputation(this);\n else {\n const updates = Updates;\n Updates = null;\n runUpdates(() => lookUpstream(this), false);\n Updates = updates;\n }\n if (Listener) {\n const sSlot = this.observers ? this.observers.length : 0;\n if (!Listener.sources) {\n Listener.sources = [this];\n Listener.sourceSlots = [sSlot];\n } else {\n Listener.sources.push(this);\n Listener.sourceSlots.push(sSlot);\n }\n if (!this.observers) {\n this.observers = [Listener];\n this.observerSlots = [Listener.sources.length - 1];\n } else {\n this.observers.push(Listener);\n this.observerSlots.push(Listener.sources.length - 1);\n }\n }\n if (runningTransition && Transition.sources.has(this)) return this.tValue;\n return this.value;\n}\nfunction writeSignal(node, value, isComp) {\n let current = Transition && Transition.running && Transition.sources.has(node) ? node.tValue : node.value;\n if (!node.comparator || !node.comparator(current, value)) {\n if (Transition) {\n const TransitionRunning = Transition.running;\n if (TransitionRunning || !isComp && Transition.sources.has(node)) {\n Transition.sources.add(node);\n node.tValue = value;\n }\n if (!TransitionRunning) node.value = value;\n } else node.value = value;\n if (node.observers && node.observers.length) runUpdates(() => {\n for (let i = 0; i < node.observers.length; i += 1) {\n const o = node.observers[i];\n const TransitionRunning = Transition && Transition.running;\n if (TransitionRunning && Transition.disposed.has(o)) continue;\n if (TransitionRunning ? !o.tState : !o.state) {\n if (o.pure) Updates.push(o);\n else Effects.push(o);\n if (o.observers) markDownstream(o);\n }\n if (!TransitionRunning) o.state = STALE;\n else o.tState = STALE;\n }\n if (Updates.length > 1e6) {\n Updates = [];\n throw new Error();\n }\n }, false);\n }\n return value;\n}\nfunction updateComputation(node) {\n if (!node.fn) return;\n cleanNode(node);\n const time = ExecCount;\n runComputation(node, Transition && Transition.running && Transition.sources.has(node) ? node.tValue : node.value, time);\n if (Transition && !Transition.running && Transition.sources.has(node)) queueMicrotask(() => {\n runUpdates(() => {\n Transition && (Transition.running = true);\n Listener = Owner = node;\n runComputation(node, node.tValue, time);\n Listener = Owner = null;\n }, false);\n });\n}\nfunction runComputation(node, value, time) {\n let nextValue;\n const owner = Owner, listener = Listener;\n Listener = Owner = node;\n try {\n nextValue = node.fn(value);\n } catch (err) {\n if (node.pure) if (Transition && Transition.running) {\n node.tState = STALE;\n node.tOwned && node.tOwned.forEach(cleanNode);\n node.tOwned = void 0;\n } else {\n node.state = STALE;\n node.owned && node.owned.forEach(cleanNode);\n node.owned = null;\n }\n node.updatedAt = time + 1;\n return handleError(err);\n } finally {\n Listener = listener;\n Owner = owner;\n }\n if (!node.updatedAt || node.updatedAt <= time) {\n if (node.updatedAt != null && \"observers\" in node) writeSignal(node, nextValue, true);\n else if (Transition && Transition.running && node.pure) {\n if (!Transition.sources.has(node)) node.value = nextValue;\n Transition.sources.add(node);\n node.tValue = nextValue;\n } else node.value = nextValue;\n node.updatedAt = time;\n }\n}\nfunction createComputation(fn, init, pure, state = STALE, options) {\n const c = {\n fn,\n state,\n updatedAt: null,\n owned: null,\n sources: null,\n sourceSlots: null,\n cleanups: null,\n value: init,\n owner: Owner,\n context: Owner ? Owner.context : null,\n pure\n };\n if (Transition && Transition.running) {\n c.state = 0;\n c.tState = state;\n }\n if (Owner === null);\n else if (Owner !== UNOWNED) if (Transition && Transition.running && Owner.pure) if (!Owner.tOwned) Owner.tOwned = [c];\n else Owner.tOwned.push(c);\n else if (!Owner.owned) Owner.owned = [c];\n else Owner.owned.push(c);\n if (ExternalSourceConfig && c.fn) {\n const sourceFn = c.fn;\n const [track, trigger] = createSignal(void 0, { equals: false });\n const ordinary = ExternalSourceConfig.factory(sourceFn, trigger);\n onCleanup(() => ordinary.dispose());\n let inTransition;\n const triggerInTransition = () => startTransition(trigger).then(() => {\n if (inTransition) {\n inTransition.dispose();\n inTransition = void 0;\n }\n });\n c.fn = (x) => {\n track();\n if (Transition && Transition.running) {\n if (!inTransition) inTransition = ExternalSourceConfig.factory(sourceFn, triggerInTransition);\n return inTransition.track(x);\n }\n return ordinary.track(x);\n };\n }\n return c;\n}\nfunction runTop(node) {\n const runningTransition = Transition && Transition.running;\n if ((runningTransition ? node.tState : node.state) === 0) return;\n if ((runningTransition ? node.tState : node.state) === PENDING) return lookUpstream(node);\n if (node.suspense && untrack(node.suspense.inFallback)) return node.suspense.effects.push(node);\n const ancestors = [node];\n while ((node = node.owner) && (!node.updatedAt || node.updatedAt < ExecCount)) {\n if (runningTransition && Transition.disposed.has(node)) return;\n if (runningTransition ? node.tState : node.state) ancestors.push(node);\n }\n for (let i = ancestors.length - 1; i >= 0; i--) {\n node = ancestors[i];\n if (runningTransition) {\n let top = node, prev = ancestors[i + 1];\n while ((top = top.owner) && top !== prev) if (Transition.disposed.has(top)) return;\n }\n if ((runningTransition ? node.tState : node.state) === STALE) updateComputation(node);\n else if ((runningTransition ? node.tState : node.state) === PENDING) {\n const updates = Updates;\n Updates = null;\n runUpdates(() => lookUpstream(node, ancestors[0]), false);\n Updates = updates;\n }\n }\n}\nfunction runUpdates(fn, init) {\n if (Updates) return fn();\n let wait = false;\n if (!init) Updates = [];\n if (Effects) wait = true;\n else Effects = [];\n ExecCount++;\n try {\n const res = fn();\n completeUpdates(wait);\n return res;\n } catch (err) {\n if (!wait) Effects = null;\n Updates = null;\n handleError(err);\n }\n}\nfunction completeUpdates(wait) {\n if (Updates) {\n if (Scheduler && Transition && Transition.running) scheduleQueue(Updates);\n else runQueue(Updates);\n Updates = null;\n }\n if (wait) return;\n let res;\n if (Transition) {\n if (!Transition.promises.size && !Transition.queue.size) {\n const sources = Transition.sources;\n const disposed = Transition.disposed;\n Effects.push.apply(Effects, Transition.effects);\n res = Transition.resolve;\n for (const e of Effects) {\n \"tState\" in e && (e.state = e.tState);\n delete e.tState;\n }\n Transition = null;\n runUpdates(() => {\n for (const d of disposed) cleanNode(d);\n for (const v of sources) {\n v.value = v.tValue;\n if (v.owned) for (let i = 0, len = v.owned.length; i < len; i++) cleanNode(v.owned[i]);\n if (v.tOwned) v.owned = v.tOwned;\n delete v.tValue;\n delete v.tOwned;\n v.tState = 0;\n }\n setTransPending(false);\n }, false);\n } else if (Transition.running) {\n Transition.running = false;\n Transition.effects.push.apply(Transition.effects, Effects);\n Effects = null;\n setTransPending(true);\n return;\n }\n }\n const e = Effects;\n Effects = null;\n if (e.length) runUpdates(() => runEffects(e), false);\n if (res) res();\n}\nfunction runQueue(queue) {\n for (let i = 0; i < queue.length; i++) runTop(queue[i]);\n}\nfunction scheduleQueue(queue) {\n for (let i = 0; i < queue.length; i++) {\n const item = queue[i];\n const tasks = Transition.queue;\n if (!tasks.has(item)) {\n tasks.add(item);\n Scheduler(() => {\n tasks.delete(item);\n runUpdates(() => {\n Transition.running = true;\n runTop(item);\n }, false);\n Transition && (Transition.running = false);\n });\n }\n }\n}\nfunction runUserEffects(queue) {\n let i, userLength = 0;\n for (i = 0; i < queue.length; i++) {\n const e = queue[i];\n if (!e.user) runTop(e);\n else queue[userLength++] = e;\n }\n if (sharedConfig.context) {\n if (sharedConfig.count) {\n sharedConfig.effects || (sharedConfig.effects = []);\n sharedConfig.effects.push(...queue.slice(0, userLength));\n return;\n }\n setHydrateContext();\n }\n if (sharedConfig.effects && (sharedConfig.done || !sharedConfig.count)) {\n queue = [...sharedConfig.effects, ...queue];\n userLength += sharedConfig.effects.length;\n delete sharedConfig.effects;\n }\n for (i = 0; i < userLength; i++) runTop(queue[i]);\n}\nfunction lookUpstream(node, ignore) {\n const runningTransition = Transition && Transition.running;\n if (runningTransition) node.tState = 0;\n else node.state = 0;\n for (let i = 0; i < node.sources.length; i += 1) {\n const source = node.sources[i];\n if (source.sources) {\n const state = runningTransition ? source.tState : source.state;\n if (state === STALE) {\n if (source !== ignore && (!source.updatedAt || source.updatedAt < ExecCount)) runTop(source);\n } else if (state === PENDING) lookUpstream(source, ignore);\n }\n }\n}\nfunction markDownstream(node) {\n const runningTransition = Transition && Transition.running;\n for (let i = 0; i < node.observers.length; i += 1) {\n const o = node.observers[i];\n if (runningTransition ? !o.tState : !o.state) {\n if (runningTransition) o.tState = PENDING;\n else o.state = PENDING;\n if (o.pure) Updates.push(o);\n else Effects.push(o);\n o.observers && markDownstream(o);\n }\n }\n}\nfunction cleanNode(node) {\n let i;\n if (node.sources) while (node.sources.length) {\n const source = node.sources.pop(), index = node.sourceSlots.pop(), obs = source.observers;\n if (obs && obs.length) {\n const n = obs.pop(), s = source.observerSlots.pop();\n if (index < obs.length) {\n n.sourceSlots[s] = index;\n obs[index] = n;\n source.observerSlots[index] = s;\n }\n }\n }\n if (node.tOwned) {\n for (i = node.tOwned.length - 1; i >= 0; i--) cleanNode(node.tOwned[i]);\n delete node.tOwned;\n }\n if (Transition && Transition.running && node.pure) reset(node, true);\n else if (node.owned) {\n for (i = node.owned.length - 1; i >= 0; i--) cleanNode(node.owned[i]);\n node.owned = null;\n }\n if (node.cleanups) {\n for (i = node.cleanups.length - 1; i >= 0; i--) node.cleanups[i]();\n node.cleanups = null;\n }\n if (Transition && Transition.running) node.tState = 0;\n else node.state = 0;\n}\nfunction reset(node, top) {\n if (!top) {\n node.tState = 0;\n Transition.disposed.add(node);\n }\n if (node.owned) for (let i = 0; i < node.owned.length; i++) reset(node.owned[i]);\n}\nfunction castError(err) {\n if (err instanceof Error) return err;\n return new Error(typeof err === \"string\" ? err : \"Unknown error\", { cause: err });\n}\nfunction runErrors(err, fns, owner) {\n try {\n for (const f of fns) f(err);\n } catch (e) {\n handleError(e, owner && owner.owner || null);\n }\n}\nfunction handleError(err, owner = Owner) {\n const fns = ERROR && owner && owner.context && owner.context[ERROR];\n const error = castError(err);\n if (!fns) throw error;\n if (Effects) Effects.push({\n fn() {\n runErrors(error, fns, owner);\n },\n state: STALE\n });\n else runErrors(error, fns, owner);\n}\nvar FALLBACK = Symbol(\"fallback\");\nfunction dispose(d) {\n for (let i = 0; i < d.length; i++) d[i]();\n}\nfunction mapArray(list, mapFn, options = {}) {\n let items = [], mapped = [], disposers = [], len = 0, indexes = mapFn.length > 1 ? [] : null;\n onCleanup(() => dispose(disposers));\n return () => {\n let newItems = list() || [], newLen = newItems.length, i, j;\n newItems[$TRACK];\n return untrack(() => {\n let newIndices, newIndicesNext, temp, tempdisposers, tempIndexes, start, end, newEnd, item;\n if (newLen === 0) {\n if (len !== 0) {\n dispose(disposers);\n disposers = [];\n items = [];\n mapped = [];\n len = 0;\n indexes && (indexes = []);\n }\n if (options.fallback) {\n items = [FALLBACK];\n mapped[0] = createRoot((disposer) => {\n disposers[0] = disposer;\n return options.fallback();\n });\n len = 1;\n }\n } else if (len === 0) {\n mapped = new Array(newLen);\n for (j = 0; j < newLen; j++) {\n items[j] = newItems[j];\n mapped[j] = createRoot(mapper);\n }\n len = newLen;\n } else {\n temp = new Array(newLen);\n tempdisposers = new Array(newLen);\n indexes && (tempIndexes = new Array(newLen));\n for (start = 0, end = Math.min(len, newLen); start < end && items[start] === newItems[start]; start++);\n for (end = len - 1, newEnd = newLen - 1; end >= start && newEnd >= start && items[end] === newItems[newEnd]; end--, newEnd--) {\n temp[newEnd] = mapped[end];\n tempdisposers[newEnd] = disposers[end];\n indexes && (tempIndexes[newEnd] = indexes[end]);\n }\n newIndices = /* @__PURE__ */ new Map();\n newIndicesNext = new Array(newEnd + 1);\n for (j = newEnd; j >= start; j--) {\n item = newItems[j];\n i = newIndices.get(item);\n newIndicesNext[j] = i === void 0 ? -1 : i;\n newIndices.set(item, j);\n }\n for (i = start; i <= end; i++) {\n item = items[i];\n j = newIndices.get(item);\n if (j !== void 0 && j !== -1) {\n temp[j] = mapped[i];\n tempdisposers[j] = disposers[i];\n indexes && (tempIndexes[j] = indexes[i]);\n j = newIndicesNext[j];\n newIndices.set(item, j);\n } else disposers[i]();\n }\n for (j = start; j < newLen; j++) if (j in temp) {\n mapped[j] = temp[j];\n disposers[j] = tempdisposers[j];\n if (indexes) {\n indexes[j] = tempIndexes[j];\n indexes[j](j);\n }\n } else mapped[j] = createRoot(mapper);\n mapped = mapped.slice(0, len = newLen);\n items = newItems.slice(0);\n }\n return mapped;\n });\n function mapper(disposer) {\n disposers[j] = disposer;\n if (indexes) {\n const [s, set] = createSignal(j);\n indexes[j] = set;\n return mapFn(newItems[j], s);\n }\n return mapFn(newItems[j]);\n }\n };\n}\nvar hydrationEnabled = false;\nfunction createComponent(Comp, props) {\n if (hydrationEnabled) {\n if (sharedConfig.context) {\n const c = sharedConfig.context;\n setHydrateContext(nextHydrateContext());\n const r = untrack(() => Comp(props || {}));\n setHydrateContext(c);\n return r;\n }\n }\n return untrack(() => Comp(props || {}));\n}\nvar narrowedError = (name) => `Stale read from <${name}>.`;\nfunction For(props) {\n const fallback = \"fallback\" in props && { fallback: () => props.fallback };\n return createMemo(mapArray(() => props.each, props.children, fallback || void 0));\n}\nfunction Show(props) {\n const keyed = props.keyed;\n const conditionValue = createMemo(() => props.when, void 0, void 0);\n const condition = keyed ? conditionValue : createMemo(conditionValue, void 0, { equals: (a, b) => !a === !b });\n return createMemo(() => {\n const c = condition();\n if (c) {\n const child = props.children;\n return typeof child === \"function\" && child.length > 0 ? untrack(() => child(keyed ? c : () => {\n if (!untrack(condition)) throw narrowedError(\"Show\");\n return conditionValue();\n })) : child;\n }\n return props.fallback;\n }, void 0, void 0);\n}\n//#endregion\n//#region node_modules/solid-js/web/dist/web.js\nvar memo = (fn) => createMemo(() => fn());\nfunction reconcileArrays(parentNode, a, b) {\n let bLength = b.length, aEnd = a.length, bEnd = bLength, aStart = 0, bStart = 0, after = a[aEnd - 1].nextSibling, map = null;\n while (aStart < aEnd || bStart < bEnd) {\n if (a[aStart] === b[bStart]) {\n aStart++;\n bStart++;\n continue;\n }\n while (a[aEnd - 1] === b[bEnd - 1]) {\n aEnd--;\n bEnd--;\n }\n if (aEnd === aStart) {\n const node = bEnd < bLength ? bStart ? b[bStart - 1].nextSibling : b[bEnd - bStart] : after;\n while (bStart < bEnd) parentNode.insertBefore(b[bStart++], node);\n } else if (bEnd === bStart) while (aStart < aEnd) {\n if (!map || !map.has(a[aStart])) a[aStart].remove();\n aStart++;\n }\n else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {\n const node = a[--aEnd].nextSibling;\n parentNode.insertBefore(b[bStart++], a[aStart++].nextSibling);\n parentNode.insertBefore(b[--bEnd], node);\n a[aEnd] = b[bEnd];\n } else {\n if (!map) {\n map = /* @__PURE__ */ new Map();\n let i = bStart;\n while (i < bEnd) map.set(b[i], i++);\n }\n const index = map.get(a[aStart]);\n if (index != null) if (bStart < index && index < bEnd) {\n let i = aStart, sequence = 1, t;\n while (++i < aEnd && i < bEnd) {\n if ((t = map.get(a[i])) == null || t !== index + sequence) break;\n sequence++;\n }\n if (sequence > index - bStart) {\n const node = a[aStart];\n while (bStart < index) parentNode.insertBefore(b[bStart++], node);\n } else parentNode.replaceChild(b[bStart++], a[aStart++]);\n } else aStart++;\n else a[aStart++].remove();\n }\n }\n}\nvar $$EVENTS = \"_$DX_DELEGATE\";\nfunction render(code, element, init, options = {}) {\n let disposer;\n createRoot((dispose) => {\n disposer = dispose;\n element === document ? code() : insert(element, code(), element.firstChild ? null : void 0, init);\n }, options.owner);\n return () => {\n disposer();\n element.textContent = \"\";\n };\n}\nfunction template(html, isImportNode, isSVG, isMathML) {\n let node;\n const create = () => {\n const t = isMathML ? document.createElementNS(\"http://www.w3.org/1998/Math/MathML\", \"template\") : document.createElement(\"template\");\n t.innerHTML = html;\n return isSVG ? t.content.firstChild.firstChild : isMathML ? t.firstChild : t.content.firstChild;\n };\n const fn = isImportNode ? () => untrack(() => document.importNode(node || (node = create()), true)) : () => (node || (node = create())).cloneNode(true);\n fn.cloneNode = fn;\n return fn;\n}\nfunction delegateEvents(eventNames, document = window.document) {\n const e = document[$$EVENTS] || (document[$$EVENTS] = /* @__PURE__ */ new Set());\n for (let i = 0, l = eventNames.length; i < l; i++) {\n const name = eventNames[i];\n if (!e.has(name)) {\n e.add(name);\n document.addEventListener(name, eventHandler);\n }\n }\n}\nfunction setAttribute(node, name, value) {\n if (isHydrating(node)) return;\n if (value == null) node.removeAttribute(name);\n else node.setAttribute(name, value);\n}\nfunction addEventListener(node, name, handler, delegate) {\n if (delegate) if (Array.isArray(handler)) {\n node[`$$${name}`] = handler[0];\n node[`$$${name}Data`] = handler[1];\n } else node[`$$${name}`] = handler;\n else if (Array.isArray(handler)) {\n const handlerFn = handler[0];\n node.addEventListener(name, handler[0] = (e) => handlerFn.call(node, handler[1], e));\n } else node.addEventListener(name, handler, typeof handler !== \"function\" && handler);\n}\nfunction insert(parent, accessor, marker, initial) {\n if (marker !== void 0 && !initial) initial = [];\n if (typeof accessor !== \"function\") return insertExpression(parent, accessor, initial, marker);\n createRenderEffect((current) => insertExpression(parent, accessor(), current, marker), initial);\n}\nfunction isHydrating(node) {\n return !!sharedConfig.context && !sharedConfig.done && (!node || node.isConnected);\n}\nfunction eventHandler(e) {\n if (sharedConfig.registry && sharedConfig.events) {\n if (sharedConfig.events.find(([el, ev]) => ev === e)) return;\n }\n let node = e.target;\n const key = `$$${e.type}`;\n const oriTarget = e.target;\n const oriCurrentTarget = e.currentTarget;\n const retarget = (value) => Object.defineProperty(e, \"target\", {\n configurable: true,\n value\n });\n const handleNode = () => {\n const handler = node[key];\n if (handler && !node.disabled) {\n const data = node[`${key}Data`];\n data !== void 0 ? handler.call(node, data, e) : handler.call(node, e);\n if (e.cancelBubble) return;\n }\n node.host && typeof node.host !== \"string\" && !node.host._$host && node.contains(e.target) && retarget(node.host);\n return true;\n };\n const walkUpTree = () => {\n while (handleNode() && (node = node._$host || node.parentNode || node.host));\n };\n Object.defineProperty(e, \"currentTarget\", {\n configurable: true,\n get() {\n return node || document;\n }\n });\n if (sharedConfig.registry && !sharedConfig.done) sharedConfig.done = _$HY.done = true;\n if (e.composedPath) {\n const path = e.composedPath();\n retarget(path[0]);\n for (let i = 0; i < path.length - 2; i++) {\n node = path[i];\n if (!handleNode()) break;\n if (node._$host) {\n node = node._$host;\n walkUpTree();\n break;\n }\n if (node.parentNode === oriCurrentTarget) break;\n }\n } else walkUpTree();\n retarget(oriTarget);\n}\nfunction insertExpression(parent, value, current, marker, unwrapArray) {\n const hydrating = isHydrating(parent);\n if (hydrating) {\n !current && (current = [...parent.childNodes]);\n let cleaned = [];\n for (let i = 0; i < current.length; i++) {\n const node = current[i];\n if (node.nodeType === 8 && node.data.slice(0, 2) === \"!$\") node.remove();\n else cleaned.push(node);\n }\n current = cleaned;\n }\n while (typeof current === \"function\") current = current();\n if (value === current) return current;\n const t = typeof value, multi = marker !== void 0;\n parent = multi && current[0] && current[0].parentNode || parent;\n if (t === \"string\" || t === \"number\") {\n if (hydrating) return current;\n if (t === \"number\") {\n value = value.toString();\n if (value === current) return current;\n }\n if (multi) {\n let node = current[0];\n if (node && node.nodeType === 3) node.data !== value && (node.data = value);\n else node = document.createTextNode(value);\n current = cleanChildren(parent, current, marker, node);\n } else if (current !== \"\" && typeof current === \"string\") current = parent.firstChild.data = value;\n else current = parent.textContent = value;\n } else if (value == null || t === \"boolean\") {\n if (hydrating) return current;\n current = cleanChildren(parent, current, marker);\n } else if (t === \"function\") {\n createRenderEffect(() => {\n let v = value();\n while (typeof v === \"function\") v = v();\n current = insertExpression(parent, v, current, marker);\n });\n return () => current;\n } else if (Array.isArray(value)) {\n const array = [];\n const currentArray = current && Array.isArray(current);\n if (normalizeIncomingArray(array, value, current, unwrapArray)) {\n createRenderEffect(() => current = insertExpression(parent, array, current, marker, true));\n return () => current;\n }\n if (hydrating) {\n if (!array.length) return current;\n if (marker === void 0) return current = [...parent.childNodes];\n let node = array[0];\n if (node.parentNode !== parent) return current;\n const nodes = [node];\n while ((node = node.nextSibling) !== marker) nodes.push(node);\n return current = nodes;\n }\n if (array.length === 0) {\n current = cleanChildren(parent, current, marker);\n if (multi) return current;\n } else if (currentArray) if (current.length === 0) appendNodes(parent, array, marker);\n else reconcileArrays(parent, current, array);\n else {\n current && cleanChildren(parent);\n appendNodes(parent, array);\n }\n current = array;\n } else if (value.nodeType) {\n if (hydrating && value.parentNode) return current = multi ? [value] : value;\n if (Array.isArray(current)) {\n if (multi) return current = cleanChildren(parent, current, marker, value);\n cleanChildren(parent, current, null, value);\n } else if (current == null || current === \"\" || !parent.firstChild) parent.appendChild(value);\n else parent.replaceChild(value, parent.firstChild);\n current = value;\n }\n return current;\n}\nfunction normalizeIncomingArray(normalized, array, current, unwrap) {\n let dynamic = false;\n for (let i = 0, len = array.length; i < len; i++) {\n let item = array[i], prev = current && current[normalized.length], t;\n if (item == null || item === true || item === false);\n else if ((t = typeof item) === \"object\" && item.nodeType) normalized.push(item);\n else if (Array.isArray(item)) dynamic = normalizeIncomingArray(normalized, item, prev) || dynamic;\n else if (t === \"function\") if (unwrap) {\n while (typeof item === \"function\") item = item();\n dynamic = normalizeIncomingArray(normalized, Array.isArray(item) ? item : [item], Array.isArray(prev) ? prev : [prev]) || dynamic;\n } else {\n normalized.push(item);\n dynamic = true;\n }\n else {\n const value = String(item);\n if (prev && prev.nodeType === 3 && prev.data === value) normalized.push(prev);\n else normalized.push(document.createTextNode(value));\n }\n }\n return dynamic;\n}\nfunction appendNodes(parent, array, marker = null) {\n for (let i = 0, len = array.length; i < len; i++) parent.insertBefore(array[i], marker);\n}\nfunction cleanChildren(parent, current, marker, replacement) {\n if (marker === void 0) return parent.textContent = \"\";\n const node = replacement || document.createTextNode(\"\");\n if (current.length) {\n let inserted = false;\n for (let i = current.length - 1; i >= 0; i--) {\n const el = current[i];\n if (node !== el) {\n const isParent = el.parentNode === parent;\n if (!inserted && !i) isParent ? parent.replaceChild(node, el) : parent.insertBefore(node, marker);\n else isParent && el.remove();\n } else inserted = true;\n }\n } else parent.insertBefore(node, marker);\n return [node];\n}\n//#endregion\n//#region node_modules/solid-js/store/dist/store.js\nvar $RAW = Symbol(\"store-raw\"), $NODE = Symbol(\"store-node\"), $HAS = Symbol(\"store-has\"), $SELF = Symbol(\"store-self\");\nfunction wrap$1(value) {\n let p = value[$PROXY];\n if (!p) {\n Object.defineProperty(value, $PROXY, { value: p = new Proxy(value, proxyTraps$1) });\n if (!Array.isArray(value)) {\n const keys = Object.keys(value), desc = Object.getOwnPropertyDescriptors(value);\n for (let i = 0, l = keys.length; i < l; i++) {\n const prop = keys[i];\n if (desc[prop].get) Object.defineProperty(value, prop, {\n enumerable: desc[prop].enumerable,\n get: desc[prop].get.bind(p)\n });\n }\n }\n }\n return p;\n}\nfunction isWrappable(obj) {\n let proto;\n return obj != null && typeof obj === \"object\" && (obj[$PROXY] || !(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype || Array.isArray(obj));\n}\nfunction unwrap(item, set = /* @__PURE__ */ new Set()) {\n let result, unwrapped, v, prop;\n if (result = item != null && item[$RAW]) return result;\n if (!isWrappable(item) || set.has(item)) return item;\n if (Array.isArray(item)) {\n if (Object.isFrozen(item)) item = item.slice(0);\n else set.add(item);\n for (let i = 0, l = item.length; i < l; i++) {\n v = item[i];\n if ((unwrapped = unwrap(v, set)) !== v) item[i] = unwrapped;\n }\n } else {\n if (Object.isFrozen(item)) item = Object.assign({}, item);\n else set.add(item);\n const keys = Object.keys(item), desc = Object.getOwnPropertyDescriptors(item);\n for (let i = 0, l = keys.length; i < l; i++) {\n prop = keys[i];\n if (desc[prop].get) continue;\n v = item[prop];\n if ((unwrapped = unwrap(v, set)) !== v) item[prop] = unwrapped;\n }\n }\n return item;\n}\nfunction getNodes(target, symbol) {\n let nodes = target[symbol];\n if (!nodes) Object.defineProperty(target, symbol, { value: nodes = Object.create(null) });\n return nodes;\n}\nfunction getNode(nodes, property, value) {\n if (nodes[property]) return nodes[property];\n const [s, set] = createSignal(value, {\n equals: false,\n internal: true\n });\n s.$ = set;\n return nodes[property] = s;\n}\nfunction proxyDescriptor$1(target, property) {\n const desc = Reflect.getOwnPropertyDescriptor(target, property);\n if (!desc || desc.get || !desc.configurable || property === $PROXY || property === $NODE) return desc;\n delete desc.value;\n delete desc.writable;\n desc.get = () => target[$PROXY][property];\n return desc;\n}\nfunction trackSelf(target) {\n getListener() && getNode(getNodes(target, $NODE), $SELF)();\n}\nfunction ownKeys(target) {\n trackSelf(target);\n return Reflect.ownKeys(target);\n}\nvar proxyTraps$1 = {\n get(target, property, receiver) {\n if (property === $RAW) return target;\n if (property === $PROXY) return receiver;\n if (property === $TRACK) {\n trackSelf(target);\n return receiver;\n }\n const nodes = getNodes(target, $NODE);\n const tracked = nodes[property];\n let value = tracked ? tracked() : target[property];\n if (property === $NODE || property === $HAS || property === \"__proto__\") return value;\n if (!tracked) {\n const desc = Object.getOwnPropertyDescriptor(target, property);\n if (getListener() && (typeof value !== \"function\" || target.hasOwnProperty(property)) && !(desc && desc.get)) value = getNode(nodes, property, value)();\n }\n return isWrappable(value) ? wrap$1(value) : value;\n },\n has(target, property) {\n if (property === $RAW || property === $PROXY || property === $TRACK || property === $NODE || property === $HAS || property === \"__proto__\") return true;\n getListener() && getNode(getNodes(target, $HAS), property)();\n return property in target;\n },\n set() {\n return true;\n },\n deleteProperty() {\n return true;\n },\n ownKeys,\n getOwnPropertyDescriptor: proxyDescriptor$1\n};\nfunction setProperty(state, property, value, deleting = false) {\n if (!deleting && state[property] === value) return;\n const prev = state[property], len = state.length;\n if (value === void 0) {\n delete state[property];\n if (state[$HAS] && state[$HAS][property] && prev !== void 0) state[$HAS][property].$();\n } else {\n state[property] = value;\n if (state[$HAS] && state[$HAS][property] && prev === void 0) state[$HAS][property].$();\n }\n let nodes = getNodes(state, $NODE), node;\n if (node = getNode(nodes, property, prev)) node.$(() => value);\n if (Array.isArray(state) && state.length !== len) {\n for (let i = state.length; i < len; i++) (node = nodes[i]) && node.$();\n (node = getNode(nodes, \"length\", len)) && node.$(state.length);\n }\n (node = nodes[$SELF]) && node.$();\n}\nfunction mergeStoreNode(state, value) {\n const keys = Object.keys(value);\n for (let i = 0; i < keys.length; i += 1) {\n const key = keys[i];\n setProperty(state, key, value[key]);\n }\n}\nfunction updateArray(current, next) {\n if (typeof next === \"function\") next = next(current);\n next = unwrap(next);\n if (Array.isArray(next)) {\n if (current === next) return;\n let i = 0, len = next.length;\n for (; i < len; i++) {\n const value = next[i];\n if (current[i] !== value) setProperty(current, i, value);\n }\n setProperty(current, \"length\", len);\n } else mergeStoreNode(current, next);\n}\nfunction updatePath(current, path, traversed = []) {\n let part, prev = current;\n if (path.length > 1) {\n part = path.shift();\n const partType = typeof part, isArray = Array.isArray(current);\n if (Array.isArray(part)) {\n for (let i = 0; i < part.length; i++) updatePath(current, [part[i]].concat(path), traversed);\n return;\n } else if (isArray && partType === \"function\") {\n for (let i = 0; i < current.length; i++) if (part(current[i], i)) updatePath(current, [i].concat(path), traversed);\n return;\n } else if (isArray && partType === \"object\") {\n const { from = 0, to = current.length - 1, by = 1 } = part;\n for (let i = from; i <= to; i += by) updatePath(current, [i].concat(path), traversed);\n return;\n } else if (path.length > 1) {\n updatePath(current[part], path, [part].concat(traversed));\n return;\n }\n prev = current[part];\n traversed = [part].concat(traversed);\n }\n let value = path[0];\n if (typeof value === \"function\") {\n value = value(prev, traversed);\n if (value === prev) return;\n }\n if (part === void 0 && value == void 0) return;\n value = unwrap(value);\n if (part === void 0 || isWrappable(prev) && isWrappable(value) && !Array.isArray(value)) mergeStoreNode(prev, value);\n else setProperty(current, part, value);\n}\nfunction createStore(...[store, options]) {\n const unwrappedStore = unwrap(store || {});\n const isArray = Array.isArray(unwrappedStore);\n const wrappedStore = wrap$1(unwrappedStore);\n function setStore(...args) {\n batch(() => {\n isArray && args.length === 1 ? updateArray(unwrappedStore, args[0]) : updatePath(unwrappedStore, args);\n });\n }\n return [wrappedStore, setStore];\n}\n//#endregion\n//#region src/transport.ts\nvar granolaTransportPaths = {\n authLock: \"/auth/lock\",\n authLogin: \"/auth/login\",\n authLogout: \"/auth/logout\",\n authMode: \"/auth/mode\",\n authRefresh: \"/auth/refresh\",\n authStatus: \"/auth/status\",\n authUnlock: \"/auth/unlock\",\n automationEvaluate: \"/automation/evaluate\",\n automationHarnesses: \"/automation/harnesses\",\n automationHarnessExplain: \"/automation/harnesses/explain\",\n automationMatches: \"/automation/matches\",\n automationArtefacts: \"/automation/artefacts\",\n automationRules: \"/automation/rules\",\n automationRuns: \"/automation/runs\",\n events: \"/events\",\n exportJobs: \"/exports/jobs\",\n exportNotes: \"/exports/notes\",\n exportTranscripts: \"/exports/transcripts\",\n folderResolve: \"/folders/resolve\",\n folders: \"/folders\",\n health: \"/health\",\n meetingResolve: \"/meetings/resolve\",\n meetings: \"/meetings\",\n processingIssues: \"/processing/issues\",\n root: \"/\",\n serverInfo: \"/server/info\",\n syncRun: \"/sync\",\n syncEvents: \"/sync/events\",\n state: \"/state\"\n};\nfunction appendSearchParams(path, params) {\n const url = new URL(path, \"http://localhost\");\n for (const [key, value] of Object.entries(params)) {\n if (value === void 0 || value === false || value === \"\") continue;\n url.searchParams.set(key, String(value));\n }\n return `${url.pathname}${url.search}`;\n}\nfunction granolaMeetingPath(id) {\n return `${granolaTransportPaths.meetings}/${encodeURIComponent(id)}`;\n}\nfunction granolaMeetingResolvePath(query, options = {}) {\n return appendSearchParams(granolaTransportPaths.meetingResolve, {\n includeTranscript: options.includeTranscript ? \"true\" : void 0,\n q: query\n });\n}\nfunction granolaMeetingsPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.meetings, {\n folderId: options.folderId,\n limit: options.limit,\n refresh: options.forceRefresh ? \"true\" : void 0,\n search: options.search,\n sort: options.sort,\n updatedFrom: options.updatedFrom,\n updatedTo: options.updatedTo\n });\n}\nfunction granolaFolderPath(id) {\n return `${granolaTransportPaths.folders}/${encodeURIComponent(id)}`;\n}\nfunction granolaFolderResolvePath(query) {\n return appendSearchParams(granolaTransportPaths.folderResolve, { q: query });\n}\nfunction granolaFoldersPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.folders, {\n limit: options.limit,\n refresh: options.forceRefresh ? \"true\" : void 0,\n search: options.search\n });\n}\nfunction granolaExportJobsPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.exportJobs, { limit: options.limit });\n}\nfunction granolaAutomationRunsPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.automationRuns, {\n limit: options.limit,\n status: options.status\n });\n}\nfunction granolaAutomationHarnessExplainPath(meetingId) {\n return appendSearchParams(granolaTransportPaths.automationHarnessExplain, { meetingId });\n}\nfunction granolaAutomationArtefactsPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.automationArtefacts, {\n kind: options.kind,\n limit: options.limit,\n meetingId: options.meetingId,\n status: options.status\n });\n}\nfunction granolaAutomationRunDecisionPath(id, decision) {\n return `${granolaTransportPaths.automationRuns}/${encodeURIComponent(id)}/${decision}`;\n}\nfunction granolaAutomationArtefactRerunPath(id) {\n return `${granolaTransportPaths.automationArtefacts}/${encodeURIComponent(id)}/rerun`;\n}\nfunction granolaAutomationArtefactPath(id) {\n return `${granolaTransportPaths.automationArtefacts}/${encodeURIComponent(id)}`;\n}\nfunction granolaAutomationArtefactDecisionPath(id, decision) {\n return `${granolaAutomationArtefactPath(id)}/${decision}`;\n}\nfunction granolaAutomationArtefactUpdatePath(id) {\n return `${granolaAutomationArtefactPath(id)}/update`;\n}\nfunction granolaProcessingIssuesPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.processingIssues, {\n limit: options.limit,\n meetingId: options.meetingId,\n severity: options.severity\n });\n}\nfunction granolaProcessingIssueRecoverPath(id) {\n return `${granolaTransportPaths.processingIssues}/${encodeURIComponent(id)}/recover`;\n}\nfunction granolaExportJobRerunPath(id) {\n return `${granolaTransportPaths.exportJobs}/${encodeURIComponent(id)}/rerun`;\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/checkPrivateRedeclaration.js\nfunction _checkPrivateRedeclaration(e, t) {\n if (t.has(e)) throw new TypeError(\"Cannot initialize the same private elements twice on an object\");\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldInitSpec.js\nfunction _classPrivateFieldInitSpec(e, t, a) {\n _checkPrivateRedeclaration(e, t), t.set(e, a);\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/typeof.js\nfunction _typeof(o) {\n \"@babel/helpers - typeof\";\n return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function(o) {\n return typeof o;\n } : function(o) {\n return o && \"function\" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? \"symbol\" : typeof o;\n }, _typeof(o);\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/toPrimitive.js\nfunction toPrimitive(t, r) {\n if (\"object\" != _typeof(t) || !t) return t;\n var e = t[Symbol.toPrimitive];\n if (void 0 !== e) {\n var i = e.call(t, r || \"default\");\n if (\"object\" != _typeof(i)) return i;\n throw new TypeError(\"@@toPrimitive must return a primitive value.\");\n }\n return (\"string\" === r ? String : Number)(t);\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/toPropertyKey.js\nfunction toPropertyKey(t) {\n var i = toPrimitive(t, \"string\");\n return \"symbol\" == _typeof(i) ? i : i + \"\";\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/defineProperty.js\nfunction _defineProperty(e, r, t) {\n return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {\n value: t,\n enumerable: !0,\n configurable: !0,\n writable: !0\n }) : e[r] = t, e;\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/assertClassBrand.js\nfunction _assertClassBrand(e, t, n) {\n if (\"function\" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;\n throw new TypeError(\"Private element is not present on this object\");\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldSet2.js\nfunction _classPrivateFieldSet2(s, a, r) {\n return s.set(_assertClassBrand(s, a), r), r;\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldGet2.js\nfunction _classPrivateFieldGet2(s, a) {\n return s.get(_assertClassBrand(s, a));\n}\n//#endregion\n//#region src/server/client.ts\nfunction resolveFetchImpl(fetchImpl) {\n if (fetchImpl) return ((input, init) => fetchImpl(input, init));\n return ((input, init) => globalThis.fetch(input, init));\n}\nfunction cloneValue(value) {\n return structuredClone(value);\n}\nfunction normaliseServerUrl(serverUrl) {\n const raw = serverUrl instanceof URL ? serverUrl.href : serverUrl.trim();\n if (!raw) throw new Error(\"server URL is required\");\n const withProtocol = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw) ? raw : `http://${raw}`;\n const parsed = new URL(withProtocol);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") throw new Error(\"server URL must use http or https\");\n parsed.pathname = \"/\";\n parsed.search = \"\";\n parsed.hash = \"\";\n return parsed;\n}\nfunction mergeHeaders(...values) {\n const headers = new Headers();\n for (const value of values) {\n if (!value) continue;\n new Headers(value).forEach((headerValue, headerName) => {\n headers.set(headerName, headerValue);\n });\n }\n return headers;\n}\nasync function responseError(response) {\n let message = `${response.status} ${response.statusText}`.trim();\n try {\n const payload = await response.json();\n if (typeof payload.error === \"string\" && payload.error.trim()) message = payload.error;\n else if (typeof payload.message === \"string\" && payload.message.trim()) message = payload.message;\n } catch {\n const text = (await response.text()).trim();\n if (text) message = text;\n }\n return new Error(message);\n}\nfunction parseSseEvent(payload) {\n const data = payload.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\").filter((line) => line.startsWith(\"data:\")).map((line) => line.slice(5).trimStart()).join(\"\\n\");\n if (!data) return;\n return JSON.parse(data);\n}\nvar _closed = /* @__PURE__ */ new WeakMap();\nvar _eventLoop = /* @__PURE__ */ new WeakMap();\nvar _listeners = /* @__PURE__ */ new WeakMap();\nvar _fetchImpl = /* @__PURE__ */ new WeakMap();\nvar _password = /* @__PURE__ */ new WeakMap();\nvar _reconnectDelayMs = /* @__PURE__ */ new WeakMap();\nvar _streamAbortController = /* @__PURE__ */ new WeakMap();\nvar _state = /* @__PURE__ */ new WeakMap();\nvar GranolaServerClient = class GranolaServerClient {\n constructor(info, url, initialState, options = {}) {\n _classPrivateFieldInitSpec(this, _closed, false);\n _classPrivateFieldInitSpec(this, _eventLoop, void 0);\n _classPrivateFieldInitSpec(this, _listeners, /* @__PURE__ */ new Set());\n _classPrivateFieldInitSpec(this, _fetchImpl, void 0);\n _classPrivateFieldInitSpec(this, _password, void 0);\n _classPrivateFieldInitSpec(this, _reconnectDelayMs, void 0);\n _defineProperty(this, \"info\", void 0);\n _classPrivateFieldInitSpec(this, _streamAbortController, void 0);\n _classPrivateFieldInitSpec(this, _state, void 0);\n this.url = url;\n _classPrivateFieldSet2(_fetchImpl, this, resolveFetchImpl(options.fetchImpl));\n this.info = cloneValue(info);\n _classPrivateFieldSet2(_password, this, options.password?.trim() || void 0);\n _classPrivateFieldSet2(_reconnectDelayMs, this, options.reconnectDelayMs ?? 1e3);\n _classPrivateFieldSet2(_state, this, cloneValue(initialState));\n }\n static async connect(serverUrl, options = {}) {\n const url = normaliseServerUrl(serverUrl);\n const fetchImpl = resolveFetchImpl(options.fetchImpl);\n const infoResponse = await fetchImpl(new URL(granolaTransportPaths.serverInfo, url), { headers: mergeHeaders({\n ...options.password?.trim() ? { \"x-granola-password\": options.password.trim() } : {},\n accept: \"application/json\"\n }) });\n if (!infoResponse.ok) throw await responseError(infoResponse);\n const info = await infoResponse.json();\n if (info.protocolVersion !== 2) throw new Error(`unsupported Granola transport protocol: expected 2, got ${info.protocolVersion}`);\n const response = await fetchImpl(new URL(granolaTransportPaths.state, url), { headers: mergeHeaders({\n ...options.password?.trim() ? { \"x-granola-password\": options.password.trim() } : {},\n accept: \"application/json\"\n }) });\n if (!response.ok) throw await responseError(response);\n const client = new GranolaServerClient(info, url, await response.json(), options);\n client.startEvents();\n return client;\n }\n async close() {\n _classPrivateFieldSet2(_closed, this, true);\n _classPrivateFieldGet2(_streamAbortController, this)?.abort();\n try {\n await _classPrivateFieldGet2(_eventLoop, this);\n } catch {}\n }\n getState() {\n return cloneValue(_classPrivateFieldGet2(_state, this));\n }\n subscribe(listener) {\n _classPrivateFieldGet2(_listeners, this).add(listener);\n return () => {\n _classPrivateFieldGet2(_listeners, this).delete(listener);\n };\n }\n async inspectAuth() {\n return await this.requestJson(granolaTransportPaths.authStatus);\n }\n async listAgentHarnesses() {\n return await this.requestJson(granolaTransportPaths.automationHarnesses);\n }\n async saveAgentHarnesses(harnesses) {\n return await this.requestJson(granolaTransportPaths.automationHarnesses, {\n body: JSON.stringify({ harnesses }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async explainAgentHarnesses(meetingId) {\n return await this.requestJson(granolaAutomationHarnessExplainPath(meetingId));\n }\n async listAutomationArtefacts(options = {}) {\n return await this.requestJson(granolaAutomationArtefactsPath(options));\n }\n async evaluateAutomationCases(cases, options) {\n return await this.requestJson(granolaTransportPaths.automationEvaluate, {\n body: JSON.stringify({\n cases,\n options\n }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async listProcessingIssues(options = {}) {\n return await this.requestJson(granolaProcessingIssuesPath(options));\n }\n async getAutomationArtefact(id) {\n return await this.requestJson(granolaAutomationArtefactPath(id));\n }\n async listAutomationRules() {\n return await this.requestJson(granolaTransportPaths.automationRules);\n }\n async saveAutomationRules(rules) {\n return await this.requestJson(granolaTransportPaths.automationRules, {\n body: JSON.stringify({ rules }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async listAutomationMatches(options = {}) {\n const path = options.limit ? `${granolaTransportPaths.automationMatches}?limit=${encodeURIComponent(String(options.limit))}` : granolaTransportPaths.automationMatches;\n return await this.requestJson(path);\n }\n async listAutomationRuns(options = {}) {\n return await this.requestJson(granolaAutomationRunsPath(options));\n }\n async resolveAutomationRun(id, decision, options = {}) {\n return await this.requestJson(granolaAutomationRunDecisionPath(id, decision), {\n body: JSON.stringify(options),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async resolveAutomationArtefact(id, decision, options = {}) {\n return await this.requestJson(granolaAutomationArtefactDecisionPath(id, decision), {\n body: JSON.stringify(options),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async recoverProcessingIssue(id) {\n return await this.requestJson(granolaProcessingIssueRecoverPath(id), { method: \"POST\" });\n }\n async updateAutomationArtefact(id, patch) {\n return await this.requestJson(granolaAutomationArtefactUpdatePath(id), {\n body: JSON.stringify(patch),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async rerunAutomationArtefact(id) {\n return await this.requestJson(granolaAutomationArtefactRerunPath(id), { method: \"POST\" });\n }\n async inspectSync() {\n return cloneValue(_classPrivateFieldGet2(_state, this).sync);\n }\n async listSyncEvents(options = {}) {\n const path = options.limit ? `${granolaTransportPaths.syncEvents}?limit=${encodeURIComponent(String(options.limit))}` : granolaTransportPaths.syncEvents;\n return await this.requestJson(path);\n }\n async loginAuth(options = {}) {\n return await this.requestJson(granolaTransportPaths.authLogin, {\n body: JSON.stringify(options),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async logoutAuth() {\n return await this.requestJson(granolaTransportPaths.authLogout, { method: \"POST\" });\n }\n async refreshAuth() {\n return await this.requestJson(granolaTransportPaths.authRefresh, { method: \"POST\" });\n }\n async switchAuthMode(mode) {\n return await this.requestJson(granolaTransportPaths.authMode, {\n body: JSON.stringify({ mode }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async sync(options = {}) {\n return await this.requestJson(granolaTransportPaths.syncRun, {\n body: JSON.stringify(options),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async listFolders(options = {}) {\n return await this.requestJson(granolaFoldersPath(options));\n }\n async getFolder(id) {\n return await this.requestJson(granolaFolderPath(id));\n }\n async findFolder(query) {\n return await this.requestJson(granolaFolderResolvePath(query));\n }\n async listMeetings(options = {}) {\n return await this.requestJson(granolaMeetingsPath(options));\n }\n async getMeeting(id, options = {}) {\n return await this.requestJson(`${granolaMeetingPath(id)}${options.requireCache ? \"?includeTranscript=true\" : \"\"}`);\n }\n async findMeeting(query, options = {}) {\n return await this.requestJson(granolaMeetingResolvePath(query, { includeTranscript: options.requireCache }));\n }\n async listExportJobs(options = {}) {\n return await this.requestJson(granolaExportJobsPath(options));\n }\n async exportNotes(format = \"markdown\", options = {}) {\n return await this.requestJson(granolaTransportPaths.exportNotes, {\n body: JSON.stringify({\n folderId: options.folderId,\n format\n }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async exportTranscripts(format = \"text\", options = {}) {\n return await this.requestJson(granolaTransportPaths.exportTranscripts, {\n body: JSON.stringify({\n folderId: options.folderId,\n format\n }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async rerunExportJob(id) {\n return await this.requestJson(granolaExportJobRerunPath(id), { method: \"POST\" });\n }\n async request(path, init = {}) {\n const response = await _classPrivateFieldGet2(_fetchImpl, this).call(this, new URL(path, this.url), {\n ...init,\n headers: mergeHeaders({\n ..._classPrivateFieldGet2(_password, this) ? { \"x-granola-password\": _classPrivateFieldGet2(_password, this) } : {},\n accept: \"application/json\"\n }, init.headers)\n });\n if (!response.ok) throw await responseError(response);\n return response;\n }\n async requestJson(path, init = {}) {\n return cloneValue(await (await this.request(path, init)).json());\n }\n emit(event) {\n _classPrivateFieldSet2(_state, this, cloneValue(event.state));\n const nextEvent = cloneValue(event);\n for (const listener of _classPrivateFieldGet2(_listeners, this)) listener(nextEvent);\n }\n startEvents() {\n if (_classPrivateFieldGet2(_eventLoop, this)) return;\n _classPrivateFieldSet2(_eventLoop, this, this.runEventsLoop());\n }\n async runEventsLoop() {\n while (!_classPrivateFieldGet2(_closed, this)) {\n const controller = new AbortController();\n _classPrivateFieldSet2(_streamAbortController, this, controller);\n try {\n const response = await this.request(granolaTransportPaths.events, {\n headers: { accept: \"text/event-stream\" },\n signal: controller.signal\n });\n await this.consumeEventStream(response);\n } catch {\n if (_classPrivateFieldGet2(_closed, this) || controller.signal.aborted) break;\n await new Promise((resolve) => {\n setTimeout(resolve, _classPrivateFieldGet2(_reconnectDelayMs, this));\n });\n }\n }\n }\n async consumeEventStream(response) {\n const reader = response.body?.getReader();\n if (!reader) throw new Error(\"server did not provide an event stream\");\n const decoder = new TextDecoder();\n let buffer = \"\";\n while (!_classPrivateFieldGet2(_closed, this)) {\n const { done, value } = await reader.read();\n if (done) return;\n buffer += decoder.decode(value, { stream: true });\n buffer = buffer.replaceAll(\"\\r\\n\", \"\\n\");\n while (true) {\n const boundary = buffer.indexOf(\"\\n\\n\");\n if (boundary < 0) break;\n const chunk = buffer.slice(0, boundary);\n buffer = buffer.slice(boundary + 2);\n const event = parseSseEvent(chunk);\n if (event) this.emit(event);\n }\n }\n }\n};\nasync function createGranolaServerClient(serverUrl, options = {}) {\n return await GranolaServerClient.connect(serverUrl, options);\n}\n//#endregion\n//#region src/web/client-state.ts\nvar granolaWebWorkspaceStorageKey = \"granola-toolkit.web-workspace\";\nvar maxRecentMeetings = 6;\nvar maxSavedFilters = 6;\nfunction normaliseFilterValue(value) {\n const trimmed = value?.trim();\n return trimmed ? trimmed : void 0;\n}\nfunction normaliseFilters(filters) {\n const selectedFolderId = normaliseFilterValue(filters.selectedFolderId);\n return {\n search: normaliseFilterValue(filters.search),\n selectedFolderId,\n sort: normaliseFilterValue(filters.sort) ?? \"updated-desc\",\n updatedFrom: normaliseFilterValue(filters.updatedFrom),\n updatedTo: normaliseFilterValue(filters.updatedTo)\n };\n}\nfunction filtersKey(filters) {\n return JSON.stringify(normaliseFilters(filters));\n}\nfunction defaultWorkspacePreferences() {\n return {\n recentMeetings: [],\n savedFilters: []\n };\n}\nfunction parseWorkspacePreferences(raw) {\n if (!raw) return defaultWorkspacePreferences();\n try {\n const parsed = JSON.parse(raw);\n return {\n recentMeetings: Array.isArray(parsed?.recentMeetings) ? parsed.recentMeetings.map((entry) => ({\n folderId: normaliseFilterValue(entry?.folderId),\n id: normaliseFilterValue(entry?.id) || \"\",\n title: normaliseFilterValue(entry?.title) || \"\",\n updatedAt: normaliseFilterValue(entry?.updatedAt) || \"\"\n })).filter((entry) => entry.id && entry.title).slice(0, maxRecentMeetings) : [],\n savedFilters: Array.isArray(parsed?.savedFilters) ? parsed.savedFilters.map((preset) => ({\n filters: normaliseFilters(preset?.filters ?? {}),\n id: normaliseFilterValue(preset?.id) || \"\",\n label: normaliseFilterValue(preset?.label) || \"\"\n })).filter((preset) => preset.id && preset.label).slice(0, maxSavedFilters) : []\n };\n } catch {\n return defaultWorkspacePreferences();\n }\n}\nfunction serialiseWorkspacePreferences(preferences) {\n return JSON.stringify({\n recentMeetings: preferences.recentMeetings.slice(0, maxRecentMeetings),\n savedFilters: preferences.savedFilters.slice(0, maxSavedFilters)\n });\n}\nfunction hasActiveFilters(filters) {\n const normalised = normaliseFilters(filters);\n return Boolean(normalised.search || normalised.selectedFolderId || normalised.updatedFrom || normalised.updatedTo || normalised.sort !== \"updated-desc\");\n}\nfunction filterLabel(filters) {\n const summary = currentFilterSummary(filters);\n if (!summary) return \"Current workspace\";\n return summary;\n}\nfunction rememberRecentMeeting(preferences, meeting) {\n const nextEntry = {\n folderId: meeting.folders?.[0]?.id,\n id: meeting.id,\n title: meeting.title?.trim() || meeting.id,\n updatedAt: meeting.updatedAt\n };\n return {\n ...preferences,\n recentMeetings: [nextEntry, ...preferences.recentMeetings.filter((entry) => entry.id !== nextEntry.id)].slice(0, maxRecentMeetings)\n };\n}\nfunction saveWorkspaceFilter(preferences, filters, options = {}) {\n const nextFilters = normaliseFilters(filters);\n if (!hasActiveFilters(nextFilters)) return preferences;\n const key = filtersKey(nextFilters);\n const nextPreset = {\n filters: nextFilters,\n id: preferences.savedFilters.find((preset) => filtersKey(preset.filters) === key)?.id ?? options.idFactory?.() ?? `filter-${preferences.savedFilters.length + 1}`,\n label: filterLabel(filters)\n };\n return {\n ...preferences,\n savedFilters: [nextPreset, ...preferences.savedFilters.filter((preset) => preset.id !== nextPreset.id)].slice(0, maxSavedFilters)\n };\n}\nfunction removeWorkspaceFilter(preferences, id) {\n return {\n ...preferences,\n savedFilters: preferences.savedFilters.filter((preset) => preset.id !== id)\n };\n}\nfunction applyWorkspaceFilter(preset) {\n return {\n search: preset.filters.search ?? \"\",\n selectedFolderId: preset.filters.selectedFolderId ?? null,\n sort: preset.filters.sort ?? \"updated-desc\",\n updatedFrom: preset.filters.updatedFrom ?? \"\",\n updatedTo: preset.filters.updatedTo ?? \"\"\n };\n}\nfunction parseWorkspaceTab(value) {\n switch (value) {\n case \"metadata\":\n case \"raw\":\n case \"transcript\": return value;\n default: return \"notes\";\n }\n}\nfunction startupSelectionFromSearch(search) {\n const params = new URLSearchParams(search);\n return {\n folderId: params.get(\"folder\")?.trim() || \"\",\n meetingId: params.get(\"meeting\")?.trim() || \"\",\n workspaceTab: parseWorkspaceTab(params.get(\"tab\"))\n };\n}\nfunction buildBrowserUrlPath(currentHref, selection) {\n const url = new URL(currentHref);\n if (selection.selectedFolderId) url.searchParams.set(\"folder\", selection.selectedFolderId);\n else url.searchParams.delete(\"folder\");\n if (selection.selectedMeetingId) url.searchParams.set(\"meeting\", selection.selectedMeetingId);\n else url.searchParams.delete(\"meeting\");\n if (parseWorkspaceTab(selection.workspaceTab) !== \"notes\") url.searchParams.set(\"tab\", parseWorkspaceTab(selection.workspaceTab));\n else url.searchParams.delete(\"tab\");\n return `${url.pathname}${url.search}${url.hash}`;\n}\nfunction exportScopeLabel(scope) {\n return scope && scope.mode === \"folder\" ? `Folder: ${scope.folderName || scope.folderId}` : \"Scope: All meetings\";\n}\nfunction currentFilterSummary(filters) {\n const parts = [];\n if (filters.selectedFolderId) {\n const folder = filters.folders.find((candidate) => candidate.id === filters.selectedFolderId);\n parts.push(`folder \"${folder ? folder.name : filters.selectedFolderId}\"`);\n }\n if (filters.search) parts.push(`search \"${filters.search}\"`);\n if (filters.updatedFrom) parts.push(`from ${filters.updatedFrom}`);\n if (filters.updatedTo) parts.push(`to ${filters.updatedTo}`);\n if (filters.sort && filters.sort !== \"updated-desc\") parts.push(filters.sort === \"updated-asc\" ? \"oldest first\" : filters.sort === \"title-asc\" ? \"title A-Z\" : \"title Z-A\");\n return parts.join(\", \");\n}\nfunction selectMeetingId(meetings, selectedMeetingId) {\n if (selectedMeetingId && meetings.some((meeting) => meeting.id === selectedMeetingId)) return selectedMeetingId;\n return meetings[0]?.id ?? null;\n}\nfunction nextWorkspaceTab(currentTab, key) {\n const current = parseWorkspaceTab(currentTab);\n switch (key) {\n case \"1\": return \"notes\";\n case \"2\": return \"transcript\";\n case \"3\": return \"metadata\";\n case \"4\": return \"raw\";\n case \"]\":\n switch (current) {\n case \"notes\": return \"transcript\";\n case \"transcript\": return \"metadata\";\n case \"metadata\": return \"raw\";\n case \"raw\": return \"notes\";\n }\n break;\n case \"[\":\n switch (current) {\n case \"notes\": return \"raw\";\n case \"transcript\": return \"notes\";\n case \"metadata\": return \"transcript\";\n case \"raw\": return \"metadata\";\n }\n break;\n default: return;\n }\n}\nfunction describeSyncStatus(sync) {\n if (sync.running) return \"Sync running\";\n if (sync.lastError) return \"Sync needs attention\";\n if (sync.lastCompletedAt) {\n const suffix = sync.summary?.changedCount ? ` · ${sync.summary.changedCount} changes` : \"\";\n return `Synced ${sync.lastCompletedAt.slice(11, 19)}${suffix}`;\n }\n return \"Sync idle\";\n}\nfunction describeAuthStatus(auth) {\n if (!auth) return \"Waiting for auth\";\n if (auth.lastError) return \"Auth needs attention\";\n switch (auth.mode) {\n case \"api-key\": return \"Personal API key active\";\n case \"stored-session\": return \"Stored session active\";\n default: return \"supabase.json fallback active\";\n }\n}\n//#endregion\n//#region src/auth-summary.ts\nfunction granolaAuthModeLabel(mode) {\n switch (mode) {\n case \"api-key\": return \"Personal API key\";\n case \"stored-session\": return \"Stored session\";\n default: return \"supabase.json fallback\";\n }\n}\nfunction granolaAuthRecommendation(auth) {\n if (auth.mode === \"api-key\") return {\n detail: \"This is the recommended default for long-running sync, web, and automation workflows.\",\n status: \"Recommended auth active\"\n };\n if (auth.apiKeyAvailable) return {\n detail: \"A stored Personal API key is available. Switch to it for the cleanest background-service auth path.\",\n nextAction: \"granola auth use api-key\",\n status: \"Stored API key available\"\n };\n return {\n detail: \"Recommended: create a Personal API key in Granola Settings -> API. Desktop-session import remains available as a fallback.\",\n nextAction: \"granola auth login --api-key grn_...\",\n status: \"No stored API key yet\"\n };\n}\n//#endregion\n//#region src/web-app/components.tsx\n/** @jsxImportSource solid-js */\nvar _tmpl$$3 = /* @__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, notes, transcripts, or artefacts\"><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$3 = /* @__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$3 = /* @__PURE__ */ template(`<div class=\"folder-empty folder-empty--error\">`), _tmpl$4$3 = /* @__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$3 = /* @__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$2 = /* @__PURE__ */ template(`<div class=folder-empty>No folders found.`), _tmpl$7$2 = /* @__PURE__ */ template(`<button class=folder-row type=button><span class=folder-row__title></span><span class=folder-row__meta>`), _tmpl$8$2 = /* @__PURE__ */ template(`<section class=folder-panel><div class=folder-panel__head><h2>Saved Filters</h2><p>Keep the slices you revisit often close at hand.</p></div><div class=saved-filter-actions><button class=\"button button--secondary\"type=button>Save current filter</button></div><div class=saved-filter-list>`), _tmpl$9$1 = /* @__PURE__ */ template(`<div class=folder-empty>No saved filters yet.`), _tmpl$0$1 = /* @__PURE__ */ template(`<div class=saved-filter-card><button class=saved-filter-card__main type=button><span class=folder-row__title></span><span class=folder-row__meta></span></button><button class=saved-filter-card__remove type=button>Remove`), _tmpl$1$1 = /* @__PURE__ */ template(`<section class=folder-panel><div class=folder-panel__head><h2>Recent Meetings</h2><p>Jump back into the conversations you opened most recently.</p></div><div class=folder-list>`), _tmpl$10$1 = /* @__PURE__ */ template(`<div class=folder-empty>No recent meetings yet.`), _tmpl$11 = /* @__PURE__ */ template(`<div class=\"meeting-empty meeting-empty--error\">`), _tmpl$12 = /* @__PURE__ */ template(`<section class=meeting-list>`), _tmpl$13 = /* @__PURE__ */ template(`<div class=meeting-empty>`), _tmpl$14 = /* @__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$15 = /* @__PURE__ */ template(`<p>`), _tmpl$16 = /* @__PURE__ */ template(`<section class=detail-head><div><h2>Meeting Workspace</h2></div><div class=state-badge>`), _tmpl$17 = /* @__PURE__ */ template(`<p>Waiting for server state…`), _tmpl$18 = /* @__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></strong></div><div><span class=status-label>Automation</span><strong>`), _tmpl$19 = /* @__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$20 = /* @__PURE__ */ template(`<section class=auth-panel><div class=auth-panel__head><h3>Auth Session</h3><p>Prefer a Granola Personal API key, then keep stored session and <code>supabase.json</code> as fallbacks.</p></div><div class=auth-panel__body>`), _tmpl$21 = /* @__PURE__ */ template(`<div class=auth-card><div class=auth-card__meta>Auth state unavailable.`), _tmpl$22 = /* @__PURE__ */ template(`<div class=auth-card__meta>Client ID: `), _tmpl$23 = /* @__PURE__ */ template(`<div class=auth-card__meta>Sign-in method: `), _tmpl$24 = /* @__PURE__ */ template(`<div class=auth-card__meta>supabase path: `), _tmpl$25 = /* @__PURE__ */ template(`<div class=\"auth-card__meta auth-card__error\">`), _tmpl$26 = /* @__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><strong>.</strong> </div><div class=auth-card__meta>Save a Personal API key here or use <code>granola auth login --api-key <token></code>. Desktop-session import remains the fallback path.</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>Use API key</button><button class=\"button button--secondary\"type=button>Import desktop session fallback</button><button class=\"button button--secondary\"type=button>Refresh stored session</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$27 = /* @__PURE__ */ template(`<div class=auth-card__meta>Next step: `), _tmpl$28 = /* @__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$29 = /* @__PURE__ */ template(`<div class=job-empty>No export jobs yet.`), _tmpl$30 = /* @__PURE__ */ template(`<div class=job-card__meta>`), _tmpl$31 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Rerun`), _tmpl$32 = /* @__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$33 = /* @__PURE__ */ template(`<section class=jobs-panel><div class=jobs-panel__head><h3>Automation Runs</h3><p>Recent action runs triggered by durable sync events.</p></div><div class=jobs-list>`), _tmpl$34 = /* @__PURE__ */ template(`<div class=job-empty>No automation runs yet.`), _tmpl$35 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Approve`), _tmpl$36 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Reject`), _tmpl$37 = /* @__PURE__ */ template(`<article class=job-card><div class=job-card__head><div><div class=job-card__title></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></div><div class=job-card__actions>`), _tmpl$38 = /* @__PURE__ */ template(`<section class=jobs-panel><div class=jobs-panel__head><h3>Processing Health</h3><p>Catch stale syncs, missing transcripts, and failed or outdated note pipelines.</p></div><div class=jobs-list>`), _tmpl$39 = /* @__PURE__ */ template(`<div class=job-empty>No processing issues detected.`), _tmpl$40 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Open Meeting`), _tmpl$41 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Recover`), _tmpl$42 = /* @__PURE__ */ template(`<section class=jobs-panel><div class=jobs-panel__head><h3>Review Queue</h3><p>Generated note and enrichment candidates waiting for review or follow-up.</p></div><div class=jobs-list>`), _tmpl$43 = /* @__PURE__ */ template(`<div class=job-empty>No automation artefacts yet.`), _tmpl$44 = /* @__PURE__ */ template(`<button class=\"job-card job-card--button\"type=button><div class=job-card__head><div><div class=job-card__title></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>`), _tmpl$45 = /* @__PURE__ */ template(`<section class=review-panel><div class=jobs-panel__head><h3>Artefact Review</h3><p>Review generated candidate notes, compare them to the current meeting, then approve, reject, edit, or rerun.`), _tmpl$46 = /* @__PURE__ */ template(`<div class=job-empty>`), _tmpl$47 = /* @__PURE__ */ template(`<div class=detail-section><h3>Action Items</h3><ul class=detail-list>`), _tmpl$48 = /* @__PURE__ */ template(`<div class=detail-section><h3>Participant Summaries</h3><ul class=detail-list>`), _tmpl$49 = /* @__PURE__ */ template(`<div class=review-grid><section class=detail-section><h2>Current Meeting Notes</h2><pre class=detail-pre></pre></section><section class=detail-section><h2>Candidate</h2><label class=field-row><span class=field-label>Title</span><input class=\"field-input field-input--plain\"></label><label class=field-row><span class=field-label>Summary</span><textarea class=\"review-textarea review-textarea--summary\"></textarea></label><label class=field-row><span class=field-label>Markdown</span><textarea class=review-textarea></textarea></label><label class=field-row><span class=field-label>Review Note</span><textarea class=\"review-textarea review-textarea--summary\"></textarea></label><div class=job-card__actions><button class=\"button button--secondary\"type=button>Save edits</button><button class=\"button button--secondary\"type=button>Approve</button><button class=\"button button--secondary\"type=button>Reject</button><button class=\"button button--secondary\"type=button>Rerun`), _tmpl$50 = /* @__PURE__ */ template(`<div class=review-body><div class=detail-meta><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip></div></div><section class=\"detail-section review-history\"><h2>History</h2><div class=jobs-list>`), _tmpl$51 = /* @__PURE__ */ template(`<div class=empty>`), _tmpl$52 = /* @__PURE__ */ template(`<span>`), _tmpl$53 = /* @__PURE__ */ template(`<li><strong>`), _tmpl$54 = /* @__PURE__ */ template(`<li><strong></strong><div>`), _tmpl$55 = /* @__PURE__ */ template(`<div class=job-card><div class=job-card__head><div class=job-card__title></div><div class=job-card__meta>`), _tmpl$56 = /* @__PURE__ */ template(`<nav class=workspace-tabs><span class=workspace-hint>1-4 switch tabs, [ and ] cycle`), _tmpl$57 = /* @__PURE__ */ template(`<button class=workspace-tab type=button>`), _tmpl$58 = /* @__PURE__ */ template(`<div class=detail-meta><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip>`), _tmpl$59 = /* @__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>`);\nfunction metadataLines(record) {\n return [\n `Title: ${record.meeting.title || record.meeting.id}`,\n `Created: ${record.meeting.createdAt}`,\n `Updated: ${record.meeting.updatedAt}`,\n `Folders: ${record.meeting.folders.length ? record.meeting.folders.map((folder) => folder.name).join(\", \") : \"none\"}`,\n `Tags: ${record.meeting.tags.length ? record.meeting.tags.join(\", \") : \"none\"}`,\n `Transcript loaded: ${record.meeting.transcriptLoaded ? \"yes\" : \"no\"}`,\n `Owner candidates: ${record.roleHelpers.ownerCandidates.length ? record.roleHelpers.ownerCandidates.map((candidate) => candidate.label).join(\", \") : \"none\"}`,\n `Speakers: ${record.roleHelpers.speakers.length ? record.roleHelpers.speakers.map((speaker) => `${speaker.label} (${speaker.segmentCount})`).join(\", \") : \"none\"}`\n ].join(\"\\n\");\n}\nfunction workspaceBody(bundle, record, tab) {\n switch (tab) {\n case \"transcript\": return {\n body: record.transcriptText || \"(Transcript unavailable)\",\n title: \"Transcript\"\n };\n case \"metadata\": return {\n body: metadataLines(record),\n title: \"Metadata\"\n };\n case \"raw\": return {\n body: JSON.stringify(bundle || record, null, 2),\n title: \"Raw Bundle\"\n };\n default: return {\n body: record.noteMarkdown || \"(No notes available)\",\n title: \"Notes\"\n };\n }\n}\nfunction scopeLabel(scope) {\n return exportScopeLabel(scope);\n}\nfunction ToolbarFilters(props) {\n return [(() => {\n var _el$ = _tmpl$$3(), _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;\n _el$4.$$input = (event) => {\n props.onSearchInput(event.currentTarget.value);\n };\n _el$8.addEventListener(\"change\", (event) => {\n props.onSortChange(event.currentTarget.value);\n });\n _el$1.addEventListener(\"change\", (event) => {\n props.onUpdatedFromChange(event.currentTarget.value);\n });\n _el$12.addEventListener(\"change\", (event) => {\n props.onUpdatedToChange(event.currentTarget.value);\n });\n createRenderEffect(() => _el$4.value = props.search);\n createRenderEffect(() => _el$8.value = props.sort);\n createRenderEffect(() => _el$1.value = props.updatedFrom);\n createRenderEffect(() => _el$12.value = props.updatedTo);\n return _el$;\n })(), (() => {\n var _el$13 = _tmpl$2$3(), _el$16 = _el$13.firstChild.nextSibling.firstChild, _el$17 = _el$16.nextSibling;\n _el$16.$$keydown = (event) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n props.onQuickOpen();\n }\n };\n _el$16.$$input = (event) => {\n props.onQuickOpenInput(event.currentTarget.value);\n };\n addEventListener(_el$17, \"click\", props.onQuickOpen, true);\n createRenderEffect(() => _el$16.value = props.quickOpen);\n return _el$13;\n })()];\n}\nfunction FolderList(props) {\n return (() => {\n var _el$18 = _tmpl$4$3(), _el$20 = _el$18.firstChild.nextSibling;\n insert(_el$20, createComponent(Show, {\n get fallback() {\n return [\n (() => {\n var _el$22 = _tmpl$5$3();\n _el$22.$$click = () => {\n props.onSelect(null);\n };\n createRenderEffect(() => setAttribute(_el$22, \"data-selected\", !props.selectedFolderId ? \"true\" : void 0));\n return _el$22;\n })(),\n createComponent(For, {\n get each() {\n return props.folders;\n },\n children: (folder) => (() => {\n var _el$24 = _tmpl$7$2(), _el$25 = _el$24.firstChild, _el$26 = _el$25.nextSibling;\n _el$24.$$click = () => {\n props.onSelect(folder.id);\n };\n insert(_el$25, () => (folder.isFavourite ? \"★ \" : \"\") + (folder.name || folder.id));\n insert(_el$26, () => `${folder.documentCount} meetings`);\n createRenderEffect(() => setAttribute(_el$24, \"data-selected\", folder.id === props.selectedFolderId ? \"true\" : void 0));\n return _el$24;\n })()\n }),\n createComponent(Show, {\n get when() {\n return props.folders.length === 0;\n },\n get children() {\n return _tmpl$6$2();\n }\n })\n ];\n },\n get when() {\n return !props.error;\n },\n get children() {\n var _el$21 = _tmpl$3$3();\n insert(_el$21, () => props.error);\n return _el$21;\n }\n }));\n return _el$18;\n })();\n}\nfunction SavedFiltersPanel(props) {\n const canSaveCurrent = () => hasActiveFilters({\n search: props.search,\n selectedFolderId: props.selectedFolderId,\n sort: props.sort,\n updatedFrom: props.updatedFrom,\n updatedTo: props.updatedTo\n });\n return (() => {\n var _el$27 = _tmpl$8$2(), _el$29 = _el$27.firstChild.nextSibling, _el$30 = _el$29.firstChild, _el$31 = _el$29.nextSibling;\n _el$30.$$click = () => {\n props.onSaveCurrent();\n };\n insert(_el$31, createComponent(Show, {\n get when() {\n return props.savedFilters.length > 0;\n },\n get fallback() {\n return _tmpl$9$1();\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.savedFilters;\n },\n children: (preset) => (() => {\n var _el$33 = _tmpl$0$1(), _el$34 = _el$33.firstChild, _el$35 = _el$34.firstChild, _el$36 = _el$35.nextSibling, _el$37 = _el$34.nextSibling;\n _el$34.$$click = () => {\n props.onApply(preset);\n };\n insert(_el$35, () => preset.label);\n insert(_el$36, () => currentFilterSummary({\n folders: props.folders,\n ...preset.filters\n }) || \"Saved workspace scope\");\n _el$37.$$click = () => {\n props.onRemove(preset.id);\n };\n return _el$33;\n })()\n });\n }\n }));\n createRenderEffect(() => _el$30.disabled = !canSaveCurrent());\n return _el$27;\n })();\n}\nfunction RecentMeetingsPanel(props) {\n return (() => {\n var _el$38 = _tmpl$1$1(), _el$40 = _el$38.firstChild.nextSibling;\n insert(_el$40, createComponent(Show, {\n get when() {\n return props.recentMeetings.length > 0;\n },\n get fallback() {\n return _tmpl$10$1();\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.recentMeetings;\n },\n children: (meeting) => (() => {\n var _el$42 = _tmpl$7$2(), _el$43 = _el$42.firstChild, _el$44 = _el$43.nextSibling;\n _el$42.$$click = () => {\n props.onOpen(meeting);\n };\n insert(_el$43, () => meeting.title);\n insert(_el$44, () => meeting.updatedAt.slice(0, 10));\n return _el$42;\n })()\n });\n }\n }));\n return _el$38;\n })();\n}\nfunction MeetingList(props) {\n const summary = () => currentFilterSummary({\n folders: props.folders,\n search: props.search,\n selectedFolderId: props.selectedFolderId,\n updatedFrom: props.updatedFrom,\n updatedTo: props.updatedTo\n });\n return (() => {\n var _el$45 = _tmpl$12();\n insert(_el$45, createComponent(Show, {\n get fallback() {\n return createComponent(Show, {\n get fallback() {\n return (() => {\n var _el$47 = _tmpl$13();\n insert(_el$47, (() => {\n var _c$ = memo(() => !!summary());\n return () => _c$() ? `No meetings match ${summary()}.` : props.emptyHint || \"No meetings yet. Try Sync now.\";\n })());\n return _el$47;\n })();\n },\n get when() {\n return props.meetings.length > 0;\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.meetings;\n },\n children: (meeting) => (() => {\n var _el$48 = _tmpl$14(), _el$49 = _el$48.firstChild, _el$50 = _el$49.nextSibling, _el$51 = _el$50.nextSibling;\n _el$48.$$click = () => {\n props.onSelect(meeting.id);\n };\n insert(_el$49, () => meeting.title || meeting.id);\n insert(_el$50, (() => {\n var _c$2 = memo(() => !!meeting.tags.length);\n return () => _c$2() ? meeting.tags.map((tag) => `#${tag}`).join(\" \") : \"untagged\";\n })());\n insert(_el$51, (() => {\n var _c$3 = memo(() => !!meeting.updatedAt);\n return () => _c$3() ? meeting.updatedAt.slice(0, 10) : \"unknown\";\n })());\n createRenderEffect(() => setAttribute(_el$48, \"data-selected\", meeting.id === props.selectedMeetingId ? \"true\" : void 0));\n return _el$48;\n })()\n });\n }\n });\n },\n get when() {\n return props.error;\n },\n get children() {\n var _el$46 = _tmpl$11();\n insert(_el$46, () => props.error);\n return _el$46;\n }\n }));\n return _el$45;\n })();\n}\nfunction AppStatePanel(props) {\n const syncStatus = () => describeSyncStatus(props.appState?.sync ?? {});\n const authStatus = () => describeAuthStatus(props.appState?.auth);\n return (() => {\n var _el$52 = _tmpl$16(), _el$53 = _el$52.firstChild;\n _el$53.firstChild;\n var _el$56 = _el$53.nextSibling;\n insert(_el$53, createComponent(Show, {\n get fallback() {\n return _tmpl$17();\n },\n get when() {\n return props.appState;\n },\n children: (appState) => (() => {\n var _el$58 = _tmpl$18(), _el$59 = _el$58.firstChild, _el$61 = _el$59.firstChild.nextSibling, _el$62 = _el$59.nextSibling, _el$64 = _el$62.firstChild.nextSibling, _el$65 = _el$62.nextSibling, _el$67 = _el$65.firstChild.nextSibling, _el$68 = _el$65.nextSibling, _el$70 = _el$68.firstChild.nextSibling, _el$71 = _el$68.nextSibling, _el$73 = _el$71.firstChild.nextSibling, _el$74 = _el$71.nextSibling, _el$76 = _el$74.firstChild.nextSibling, _el$77 = _el$74.nextSibling, _el$79 = _el$77.firstChild.nextSibling, _el$80 = _el$77.nextSibling, _el$82 = _el$80.firstChild.nextSibling, _el$85 = _el$80.nextSibling.firstChild.nextSibling;\n insert(_el$61, () => appState().ui.surface);\n insert(_el$64, () => appState().ui.view);\n insert(_el$67, authStatus);\n insert(_el$70, syncStatus);\n insert(_el$73, (() => {\n var _c$4 = memo(() => !!appState().documents.loaded);\n return () => _c$4() ? String(appState().documents.count) : \"not loaded\";\n })());\n insert(_el$76, (() => {\n var _c$5 = memo(() => !!appState().folders.loaded);\n return () => _c$5() ? String(appState().folders.count) : \"not loaded\";\n })());\n insert(_el$79, (() => {\n var _c$6 = memo(() => !!appState().cache.loaded);\n return () => _c$6() ? `${appState().cache.transcriptCount} transcript sets` : appState().cache.configured ? \"configured\" : \"not configured\";\n })());\n insert(_el$82, (() => {\n var _c$7 = memo(() => !!appState().index.loaded);\n return () => _c$7() ? `${appState().index.meetingCount} meetings` : appState().index.available ? \"available\" : \"not built\";\n })());\n insert(_el$85, () => `${appState().automation.runCount} runs / ${appState().automation.pendingRunCount} pending runs / ${appState().automation.pendingArtefactCount} pending artefacts`);\n return _el$58;\n })()\n }), null);\n insert(_el$53, createComponent(Show, {\n get when() {\n return props.appState?.auth.lastError;\n },\n get children() {\n var _el$55 = _tmpl$15();\n insert(_el$55, () => props.appState?.auth.lastError);\n return _el$55;\n }\n }), null);\n insert(_el$56, () => props.statusLabel);\n createRenderEffect(() => setAttribute(_el$56, \"data-tone\", props.statusTone));\n return _el$52;\n })();\n}\nfunction SecurityPanel(props) {\n return createComponent(Show, {\n get when() {\n return props.visible;\n },\n get children() {\n var _el$86 = _tmpl$19(), _el$89 = _el$86.firstChild.nextSibling.firstChild, _el$91 = _el$89.nextSibling.firstChild, _el$92 = _el$91.nextSibling;\n _el$89.$$keydown = (event) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n props.onUnlock();\n }\n };\n _el$89.$$input = (event) => {\n props.onPasswordChange(event.currentTarget.value);\n };\n addEventListener(_el$91, \"click\", props.onUnlock, true);\n addEventListener(_el$92, \"click\", props.onLock, true);\n createRenderEffect(() => _el$89.value = props.password);\n return _el$86;\n }\n });\n}\nfunction AuthPanel(props) {\n return (() => {\n var _el$93 = _tmpl$20(), _el$95 = _el$93.firstChild.nextSibling;\n insert(_el$95, createComponent(Show, {\n get fallback() {\n return _tmpl$21();\n },\n get when() {\n return props.auth;\n },\n children: (auth) => (() => {\n var _el$97 = _tmpl$26(), _el$98 = _el$97.firstChild, _el$99 = _el$98.firstChild, _el$101 = _el$99.firstChild.nextSibling, _el$102 = _el$99.nextSibling, _el$104 = _el$102.firstChild.nextSibling, _el$105 = _el$102.nextSibling, _el$107 = _el$105.firstChild.nextSibling, _el$108 = _el$105.nextSibling, _el$110 = _el$108.firstChild.nextSibling, _el$113 = _el$108.nextSibling.firstChild.nextSibling, _el$114 = _el$98.nextSibling, _el$115 = _el$114.firstChild, _el$116 = _el$115.firstChild;\n _el$115.nextSibling;\n var _el$125 = _el$114.nextSibling, _el$127 = _el$125.nextSibling.firstChild, _el$128 = _el$127.nextSibling, _el$129 = _el$128.nextSibling, _el$130 = _el$129.nextSibling, _el$131 = _el$130.nextSibling, _el$132 = _el$131.nextSibling, _el$133 = _el$132.nextSibling, _el$134 = _el$133.nextSibling;\n insert(_el$101, () => granolaAuthModeLabel(auth().mode));\n insert(_el$104, () => auth().apiKeyAvailable ? \"available\" : \"missing\");\n insert(_el$107, () => auth().storedSessionAvailable ? \"available\" : \"missing\");\n insert(_el$110, () => auth().supabaseAvailable ? \"available\" : \"missing\");\n insert(_el$113, () => auth().refreshAvailable ? \"available\" : \"missing\");\n insert(_el$115, () => granolaAuthRecommendation(auth()).status, _el$116);\n insert(_el$114, () => granolaAuthRecommendation(auth()).detail, null);\n insert(_el$97, createComponent(Show, {\n get when() {\n return granolaAuthRecommendation(auth()).nextAction;\n },\n children: (nextAction) => (() => {\n var _el$135 = _tmpl$27();\n _el$135.firstChild;\n insert(_el$135, nextAction, null);\n return _el$135;\n })()\n }), _el$125);\n insert(_el$97, createComponent(Show, {\n get when() {\n return auth().clientId;\n },\n get children() {\n var _el$118 = _tmpl$22();\n _el$118.firstChild;\n insert(_el$118, () => auth().clientId, null);\n return _el$118;\n }\n }), _el$125);\n insert(_el$97, createComponent(Show, {\n get when() {\n return auth().signInMethod;\n },\n get children() {\n var _el$120 = _tmpl$23();\n _el$120.firstChild;\n insert(_el$120, () => auth().signInMethod, null);\n return _el$120;\n }\n }), _el$125);\n insert(_el$97, createComponent(Show, {\n get when() {\n return auth().supabasePath;\n },\n get children() {\n var _el$122 = _tmpl$24();\n _el$122.firstChild;\n insert(_el$122, () => auth().supabasePath, null);\n return _el$122;\n }\n }), _el$125);\n insert(_el$97, createComponent(Show, {\n get when() {\n return auth().lastError;\n },\n get children() {\n var _el$124 = _tmpl$25();\n insert(_el$124, () => auth().lastError);\n return _el$124;\n }\n }), _el$125);\n _el$127.$$input = (event) => {\n props.onApiKeyDraftChange(event.currentTarget.value);\n };\n addEventListener(_el$128, \"click\", props.onSaveApiKey, true);\n _el$129.$$click = () => {\n props.onSwitchMode(\"api-key\");\n };\n addEventListener(_el$130, \"click\", props.onImportDesktopSession, true);\n addEventListener(_el$131, \"click\", props.onRefresh, true);\n _el$132.$$click = () => {\n props.onSwitchMode(\"stored-session\");\n };\n _el$133.$$click = () => {\n props.onSwitchMode(\"supabase-file\");\n };\n addEventListener(_el$134, \"click\", props.onLogout, true);\n createRenderEffect((_p$) => {\n var _v$ = !auth().apiKeyAvailable || auth().mode === \"api-key\", _v$2 = !auth().supabaseAvailable, _v$3 = !auth().storedSessionAvailable || !auth().refreshAvailable, _v$4 = !auth().storedSessionAvailable || auth().mode === \"stored-session\", _v$5 = !auth().supabaseAvailable || auth().mode === \"supabase-file\", _v$6 = !auth().apiKeyAvailable && !auth().storedSessionAvailable;\n _v$ !== _p$.e && (_el$129.disabled = _p$.e = _v$);\n _v$2 !== _p$.t && (_el$130.disabled = _p$.t = _v$2);\n _v$3 !== _p$.a && (_el$131.disabled = _p$.a = _v$3);\n _v$4 !== _p$.o && (_el$132.disabled = _p$.o = _v$4);\n _v$5 !== _p$.i && (_el$133.disabled = _p$.i = _v$5);\n _v$6 !== _p$.n && (_el$134.disabled = _p$.n = _v$6);\n return _p$;\n }, {\n e: void 0,\n t: void 0,\n a: void 0,\n o: void 0,\n i: void 0,\n n: void 0\n });\n createRenderEffect(() => _el$127.value = props.apiKeyDraft);\n return _el$97;\n })()\n }));\n return _el$93;\n })();\n}\nfunction ExportJobsPanel(props) {\n return (() => {\n var _el$137 = _tmpl$28(), _el$139 = _el$137.firstChild.nextSibling;\n insert(_el$139, createComponent(Show, {\n get when() {\n return props.jobs.length > 0;\n },\n get fallback() {\n return _tmpl$29();\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.jobs.slice(0, 6);\n },\n children: (job) => (() => {\n var _el$141 = _tmpl$32(), _el$142 = _el$141.firstChild, _el$143 = _el$142.firstChild, _el$144 = _el$143.firstChild, _el$145 = _el$144.firstChild, _el$146 = _el$144.nextSibling, _el$147 = _el$143.nextSibling, _el$148 = _el$142.nextSibling, _el$149 = _el$148.nextSibling;\n _el$149.firstChild;\n var _el$151 = _el$149.nextSibling;\n _el$151.firstChild;\n var _el$154 = _el$151.nextSibling;\n insert(_el$144, () => job.kind, _el$145);\n insert(_el$146, () => job.id);\n insert(_el$147, () => job.status);\n insert(_el$148, () => `Format: ${job.format} • ${scopeLabel(job.scope)} • ${job.itemCount > 0 ? `${job.completedCount}/${job.itemCount} items` : \"0 items\"} • Written: ${job.written}`);\n insert(_el$149, () => job.startedAt.slice(0, 19), null);\n insert(_el$151, () => job.outputDir, null);\n insert(_el$141, createComponent(Show, {\n get when() {\n return job.error;\n },\n get children() {\n var _el$153 = _tmpl$30();\n insert(_el$153, () => job.error);\n return _el$153;\n }\n }), _el$154);\n insert(_el$154, createComponent(Show, {\n get when() {\n return job.status !== \"running\";\n },\n get children() {\n var _el$155 = _tmpl$31();\n _el$155.$$click = () => {\n props.onRerun(job.id);\n };\n return _el$155;\n }\n }));\n createRenderEffect(() => setAttribute(_el$147, \"data-status\", job.status));\n return _el$141;\n })()\n });\n }\n }));\n return _el$137;\n })();\n}\nfunction AutomationRunsPanel(props) {\n return (() => {\n var _el$156 = _tmpl$33(), _el$158 = _el$156.firstChild.nextSibling;\n insert(_el$158, createComponent(Show, {\n get when() {\n return props.runs.length > 0;\n },\n get fallback() {\n return _tmpl$34();\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.runs.slice(0, 6);\n },\n children: (run) => (() => {\n var _el$160 = _tmpl$37(), _el$161 = _el$160.firstChild, _el$162 = _el$161.firstChild, _el$163 = _el$162.firstChild, _el$164 = _el$163.nextSibling, _el$165 = _el$162.nextSibling, _el$166 = _el$161.nextSibling, _el$167 = _el$166.nextSibling, _el$171 = _el$167.nextSibling;\n insert(_el$163, () => run.actionName);\n insert(_el$164, () => `${run.ruleName} • ${run.id}`);\n insert(_el$165, () => run.status);\n insert(_el$166, () => `${run.title} • ${run.eventKind}`);\n insert(_el$167, () => `Started: ${run.startedAt.slice(0, 19)}`);\n insert(_el$160, createComponent(Show, {\n get when() {\n return run.prompt;\n },\n get children() {\n var _el$168 = _tmpl$30();\n insert(_el$168, () => run.prompt);\n return _el$168;\n }\n }), _el$171);\n insert(_el$160, createComponent(Show, {\n get when() {\n return run.result;\n },\n get children() {\n var _el$169 = _tmpl$30();\n insert(_el$169, () => run.result);\n return _el$169;\n }\n }), _el$171);\n insert(_el$160, createComponent(Show, {\n get when() {\n return run.error;\n },\n get children() {\n var _el$170 = _tmpl$30();\n insert(_el$170, () => run.error);\n return _el$170;\n }\n }), _el$171);\n insert(_el$171, createComponent(Show, {\n get when() {\n return run.status === \"pending\";\n },\n get children() {\n return [(() => {\n var _el$172 = _tmpl$35();\n _el$172.$$click = () => {\n props.onApprove(run.id);\n };\n return _el$172;\n })(), (() => {\n var _el$173 = _tmpl$36();\n _el$173.$$click = () => {\n props.onReject(run.id);\n };\n return _el$173;\n })()];\n }\n }));\n createRenderEffect(() => setAttribute(_el$165, \"data-status\", run.status));\n return _el$160;\n })()\n });\n }\n }));\n return _el$156;\n })();\n}\nfunction ProcessingIssuesPanel(props) {\n return (() => {\n var _el$174 = _tmpl$38(), _el$176 = _el$174.firstChild.nextSibling;\n insert(_el$176, createComponent(Show, {\n get when() {\n return props.issues.length > 0;\n },\n get fallback() {\n return _tmpl$39();\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.issues.slice(0, 8);\n },\n children: (issue) => (() => {\n var _el$178 = _tmpl$37(), _el$179 = _el$178.firstChild, _el$180 = _el$179.firstChild, _el$181 = _el$180.firstChild, _el$182 = _el$181.nextSibling, _el$183 = _el$180.nextSibling, _el$184 = _el$179.nextSibling, _el$185 = _el$184.nextSibling, _el$186 = _el$185.nextSibling;\n insert(_el$181, () => issue.title);\n insert(_el$182, () => issue.id);\n insert(_el$183, () => issue.severity);\n insert(_el$184, () => issue.kind);\n insert(_el$185, () => issue.detail);\n insert(_el$186, createComponent(Show, {\n get when() {\n return issue.meetingId;\n },\n get children() {\n var _el$187 = _tmpl$40();\n _el$187.$$click = () => {\n props.onOpenMeeting(issue.meetingId);\n };\n return _el$187;\n }\n }), null);\n insert(_el$186, createComponent(Show, {\n get when() {\n return issue.recoverable;\n },\n get children() {\n var _el$188 = _tmpl$41();\n _el$188.$$click = () => {\n props.onRecover(issue.id);\n };\n return _el$188;\n }\n }), null);\n createRenderEffect(() => setAttribute(_el$183, \"data-status\", issue.severity));\n return _el$178;\n })()\n });\n }\n }));\n return _el$174;\n })();\n}\nfunction AutomationArtefactsPanel(props) {\n return (() => {\n var _el$189 = _tmpl$42(), _el$191 = _el$189.firstChild.nextSibling;\n insert(_el$191, createComponent(Show, {\n get when() {\n return props.artefacts.length > 0;\n },\n get fallback() {\n return _tmpl$43();\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.artefacts.slice(0, 10);\n },\n children: (artefact) => (() => {\n var _el$193 = _tmpl$44(), _el$194 = _el$193.firstChild, _el$195 = _el$194.firstChild, _el$196 = _el$195.firstChild, _el$197 = _el$196.nextSibling, _el$198 = _el$195.nextSibling, _el$199 = _el$194.nextSibling, _el$201 = _el$199.nextSibling;\n _el$193.$$click = () => {\n props.onSelect(artefact.id);\n };\n insert(_el$196, () => artefact.structured.title);\n insert(_el$197, () => `${artefact.kind} • ${artefact.ruleName}`);\n insert(_el$198, () => artefact.status);\n insert(_el$199, () => artefact.meetingId);\n insert(_el$193, createComponent(Show, {\n get when() {\n return artefact.structured.summary;\n },\n get children() {\n var _el$200 = _tmpl$30();\n insert(_el$200, () => artefact.structured.summary);\n return _el$200;\n }\n }), _el$201);\n insert(_el$201, () => `Updated: ${artefact.updatedAt.slice(0, 19)}`);\n createRenderEffect((_p$) => {\n var _v$7 = artefact.id === props.selectedArtefactId ? \"true\" : void 0, _v$8 = artefact.status;\n _v$7 !== _p$.e && setAttribute(_el$193, \"data-selected\", _p$.e = _v$7);\n _v$8 !== _p$.t && setAttribute(_el$198, \"data-status\", _p$.t = _v$8);\n return _p$;\n }, {\n e: void 0,\n t: void 0\n });\n return _el$193;\n })()\n });\n }\n }));\n return _el$189;\n })();\n}\nfunction ArtefactReviewPanel(props) {\n return (() => {\n var _el$202 = _tmpl$45();\n _el$202.firstChild;\n insert(_el$202, createComponent(Show, {\n get when() {\n return props.artefact;\n },\n get fallback() {\n return (() => {\n var _el$204 = _tmpl$46();\n insert(_el$204, () => props.error || \"Select an automation artefact to review it.\");\n return _el$204;\n })();\n },\n children: (artefact) => (() => {\n var _el$205 = _tmpl$50(), _el$206 = _el$205.firstChild, _el$207 = _el$206.firstChild, _el$208 = _el$207.nextSibling, _el$209 = _el$208.nextSibling, _el$210 = _el$209.nextSibling, _el$240 = _el$206.nextSibling, _el$242 = _el$240.firstChild.nextSibling;\n insert(_el$207, () => `Status: ${artefact().status}`);\n insert(_el$208, () => `Kind: ${artefact().kind}`);\n insert(_el$209, () => `Meeting: ${artefact().meetingId}`);\n insert(_el$210, () => `Provider: ${artefact().provider}/${artefact().model}`);\n insert(_el$205, createComponent(Show, {\n get when() {\n return !props.error;\n },\n get fallback() {\n return (() => {\n var _el$243 = _tmpl$51();\n insert(_el$243, () => props.error);\n return _el$243;\n })();\n },\n get children() {\n var _el$211 = _tmpl$49(), _el$212 = _el$211.firstChild, _el$214 = _el$212.firstChild.nextSibling, _el$215 = _el$212.nextSibling, _el$217 = _el$215.firstChild.nextSibling, _el$219 = _el$217.firstChild.nextSibling, _el$220 = _el$217.nextSibling, _el$222 = _el$220.firstChild.nextSibling, _el$223 = _el$220.nextSibling, _el$225 = _el$223.firstChild.nextSibling, _el$226 = _el$223.nextSibling, _el$228 = _el$226.firstChild.nextSibling, _el$230 = _el$226.nextSibling.firstChild, _el$231 = _el$230.nextSibling, _el$232 = _el$231.nextSibling, _el$233 = _el$232.nextSibling;\n insert(_el$214, () => props.bundle?.meeting.noteMarkdown || \"(No existing meeting notes)\");\n _el$219.$$input = (event) => {\n props.onDraftTitleChange(event.currentTarget.value);\n };\n _el$222.$$input = (event) => {\n props.onDraftSummaryChange(event.currentTarget.value);\n };\n insert(_el$222, () => props.draftSummary);\n _el$225.$$input = (event) => {\n props.onDraftMarkdownChange(event.currentTarget.value);\n };\n insert(_el$225, () => props.draftMarkdown);\n _el$228.$$input = (event) => {\n props.onReviewNoteChange(event.currentTarget.value);\n };\n insert(_el$228, () => props.reviewNote);\n addEventListener(_el$230, \"click\", props.onSave, true);\n addEventListener(_el$231, \"click\", props.onApprove, true);\n addEventListener(_el$232, \"click\", props.onReject, true);\n addEventListener(_el$233, \"click\", props.onRerun, true);\n insert(_el$215, createComponent(Show, {\n get when() {\n return artefact().structured.actionItems.length > 0;\n },\n get children() {\n var _el$234 = _tmpl$47(), _el$236 = _el$234.firstChild.nextSibling;\n insert(_el$236, createComponent(For, {\n get each() {\n return artefact().structured.actionItems;\n },\n children: (item) => (() => {\n var _el$244 = _tmpl$53(), _el$245 = _el$244.firstChild;\n insert(_el$245, () => item.title);\n insert(_el$244, createComponent(Show, {\n get when() {\n return item.owner;\n },\n get children() {\n var _el$246 = _tmpl$52();\n insert(_el$246, () => ` • ${item.owner}`);\n return _el$246;\n }\n }), null);\n insert(_el$244, createComponent(Show, {\n get when() {\n return item.dueDate;\n },\n get children() {\n var _el$247 = _tmpl$52();\n insert(_el$247, () => ` • due ${item.dueDate}`);\n return _el$247;\n }\n }), null);\n return _el$244;\n })()\n }));\n return _el$234;\n }\n }), null);\n insert(_el$215, createComponent(Show, {\n get when() {\n return (artefact().structured.participantSummaries?.length ?? 0) > 0;\n },\n get children() {\n var _el$237 = _tmpl$48(), _el$239 = _el$237.firstChild.nextSibling;\n insert(_el$239, createComponent(For, {\n get each() {\n return artefact().structured.participantSummaries;\n },\n children: (summary) => (() => {\n var _el$248 = _tmpl$54(), _el$249 = _el$248.firstChild, _el$251 = _el$249.nextSibling;\n insert(_el$249, () => summary.speaker);\n insert(_el$248, createComponent(Show, {\n get when() {\n return summary.role;\n },\n get children() {\n var _el$250 = _tmpl$52();\n insert(_el$250, () => ` • ${summary.role}`);\n return _el$250;\n }\n }), _el$251);\n insert(_el$251, () => summary.summary);\n return _el$248;\n })()\n }));\n return _el$237;\n }\n }), null);\n createRenderEffect((_p$) => {\n var _v$9 = artefact().status === \"superseded\", _v$0 = artefact().status === \"superseded\";\n _v$9 !== _p$.e && (_el$231.disabled = _p$.e = _v$9);\n _v$0 !== _p$.t && (_el$232.disabled = _p$.t = _v$0);\n return _p$;\n }, {\n e: void 0,\n t: void 0\n });\n createRenderEffect(() => _el$219.value = props.draftTitle);\n return _el$211;\n }\n }), _el$240);\n insert(_el$242, createComponent(For, {\n get each() {\n return artefact().history.slice().reverse();\n },\n children: (entry) => (() => {\n var _el$252 = _tmpl$55(), _el$254 = _el$252.firstChild.firstChild, _el$255 = _el$254.nextSibling;\n insert(_el$254, () => entry.action);\n insert(_el$255, () => entry.at.slice(0, 19));\n insert(_el$252, createComponent(Show, {\n get when() {\n return entry.note;\n },\n get children() {\n var _el$256 = _tmpl$30();\n insert(_el$256, () => entry.note);\n return _el$256;\n }\n }), null);\n return _el$252;\n })()\n }));\n return _el$205;\n })()\n }), null);\n return _el$202;\n })();\n}\nfunction Workspace(props) {\n const parsedTab = () => parseWorkspaceTab(props.tab);\n const details = () => {\n if (!props.selectedMeeting) return null;\n return workspaceBody(props.bundle, props.selectedMeeting, parsedTab());\n };\n return [(() => {\n var _el$257 = _tmpl$56(), _el$258 = _el$257.firstChild;\n insert(_el$257, createComponent(For, {\n each: [\n \"notes\",\n \"transcript\",\n \"metadata\",\n \"raw\"\n ],\n children: (tab) => (() => {\n var _el$259 = _tmpl$57();\n _el$259.$$click = () => {\n props.onSelectTab(tab);\n };\n insert(_el$259, tab === \"notes\" ? \"Notes\" : tab === \"transcript\" ? \"Transcript\" : tab === \"metadata\" ? \"Metadata\" : \"Raw\");\n createRenderEffect(() => setAttribute(_el$259, \"data-selected\", parsedTab() === tab ? \"true\" : void 0));\n return _el$259;\n })()\n }), _el$258);\n return _el$257;\n })(), createComponent(Show, {\n get when() {\n return props.selectedMeeting;\n },\n get fallback() {\n return (() => {\n var _el$260 = _tmpl$51();\n insert(_el$260, () => props.detailError || \"Select a meeting to inspect its notes and transcript.\");\n return _el$260;\n })();\n },\n children: (meeting) => [(() => {\n var _el$261 = _tmpl$58(), _el$262 = _el$261.firstChild, _el$263 = _el$262.nextSibling, _el$264 = _el$263.nextSibling;\n insert(_el$262, () => `ID: ${meeting().meeting.id}`);\n insert(_el$263, () => `Source: ${meeting().meeting.noteContentSource}`);\n insert(_el$264, () => `Transcript: ${meeting().meeting.transcriptSegmentCount} segments`);\n return _el$261;\n })(), createComponent(Show, {\n get when() {\n return !props.detailError;\n },\n get fallback() {\n return (() => {\n var _el$273 = _tmpl$51();\n insert(_el$273, () => props.detailError);\n return _el$273;\n })();\n },\n get children() {\n var _el$265 = _tmpl$59(), _el$267 = _el$265.firstChild.firstChild, _el$269 = _el$267.firstChild.nextSibling, _el$271 = _el$267.nextSibling.firstChild, _el$272 = _el$271.nextSibling;\n insert(_el$269, () => metadataLines(meeting()));\n insert(_el$271, () => details()?.title);\n insert(_el$272, () => details()?.body);\n return _el$265;\n }\n })]\n })];\n}\ndelegateEvents([\n \"input\",\n \"keydown\",\n \"click\"\n]);\n//#endregion\n//#region src/web-app/harness-editor.tsx\n/** @jsxImportSource solid-js */\nvar _tmpl$$2 = /* @__PURE__ */ template(`<div class=empty>No harnesses configured yet. Create one here and save it to the shared harness file.`), _tmpl$2$2 = /* @__PURE__ */ template(`<section class=harness-panel><div class=jobs-panel__head><h3>Harness Editor</h3><p>Manage meeting playbooks, inspect why they match the selected meeting, and run the selected harness through the current pipeline before changing live automation.</p></div><div class=harness-grid><div class=detail-section><div class=harness-toolbar><button class=\"button button--primary\"type=button>New Harness</button><button class=\"button button--secondary\"type=button>Duplicate</button><button class=\"button button--secondary\"type=button>Remove</button><button class=\"button button--secondary\"type=button>Reload</button><button class=\"button button--primary\"type=button>Save Harnesses</button></div><div class=jobs-list>`), _tmpl$3$2 = /* @__PURE__ */ template(`<button class=\"job-card job-card--button\"type=button><div class=job-card__head><span class=job-card__title></span><span class=job-card__status></span></div><div class=job-card__meta>`), _tmpl$4$2 = /* @__PURE__ */ template(`<div class=detail-section><div class=empty>Select a harness to edit its prompts, rules, and test output.`), _tmpl$5$2 = /* @__PURE__ */ template(`<div class=auth-card__error>`), _tmpl$6$1 = /* @__PURE__ */ template(`<div class=detail-section><div class=harness-status-row><h2></h2><span class=state-badge></span></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Id</span><input class=\"field-input field-input--plain\"></label><label><span class=field-label>Name</span><input class=\"field-input field-input--plain\"></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Provider</span><select class=select><option value>Use default</option><option value=codex>Codex</option><option value=openai>OpenAI</option><option value=openrouter>OpenRouter</option></select></label><label><span class=field-label>Model</span><input class=\"field-input field-input--plain\"placeholder=\"gpt-5-codex or openai/gpt-5-mini\"></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Priority</span><input class=\"field-input field-input--plain\"placeholder=0></label><label><span class=field-label>Working Directory</span><input class=\"field-input field-input--plain\"placeholder=/path/to/project></label></div><div class=field-row><label><span class=field-label>Prompt</span><textarea class=\"review-textarea review-textarea--summary\"></textarea></label><label><span class=field-label>Prompt File</span><input class=\"field-input field-input--plain\"placeholder=./agents/customer-call/AGENT.md></label></div><div class=field-row><label><span class=field-label>System Prompt</span><textarea class=\"review-textarea review-textarea--summary\"></textarea></label><label><span class=field-label>System Prompt File</span><input class=\"field-input field-input--plain\"placeholder=./agents/customer-call/SYSTEM.md></label></div><div class=field-row><label><span class=field-label>Fallback Harnesses</span><input class=\"field-input field-input--plain\"placeholder=\"fallback-a, fallback-b\"></label></div><div class=field-row><label><span class=field-label>Event Kinds</span><input class=\"field-input field-input--plain\"placeholder=\"transcript.ready, meeting.created\"></label><label><span class=field-label>Meeting Ids</span><input class=\"field-input field-input--plain\"placeholder=doc-alpha-1111></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Folder Ids</span><input class=\"field-input field-input--plain\"placeholder=folder-team-1111></label><label><span class=field-label>Folder Names</span><input class=\"field-input field-input--plain\"placeholder=\"Team, Customers\"></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Tags</span><input class=\"field-input field-input--plain\"placeholder=\"customer, weekly\"></label><label><span class=field-label>Title Includes</span><input class=\"field-input field-input--plain\"placeholder=\"sync, review\"></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Title Regex</span><input class=\"field-input field-input--plain\"placeholder=customer.*sync></label><label><span class=field-label>Transcript Loaded</span><select class=select><option value>Either</option><option value=true>Must be loaded</option><option value=false>Must be missing</option></select></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Calendar Event Ids</span><input class=\"field-input field-input--plain\"placeholder=event-123></label><label><span class=field-label>Recurring Event Ids</span><input class=\"field-input field-input--plain\"placeholder=recurring-456></label></div><div class=detail-section><h3>Selected Meeting Test</h3><div class=job-card__meta></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Pipeline Kind</span><select class=select><option value=notes>Notes</option><option value=enrichment>Enrichment</option></select></label><label><span class=field-label>Rule Explainer Event</span><input class=\"field-input field-input--plain\"disabled></label></div><div class=harness-toolbar><button class=\"button button--primary\"type=button>Test Harness`), _tmpl$7$1 = /* @__PURE__ */ template(`<div class=job-card__status>`), _tmpl$8$1 = /* @__PURE__ */ template(`<ul class=detail-list>`), _tmpl$9 = /* @__PURE__ */ template(`<li>`), _tmpl$0 = /* @__PURE__ */ template(`<div class=field-row><label><span class=field-label>Resolved Prompt Preview</span><textarea class=review-textarea readonly></textarea></label><label><span class=field-label>Raw Output</span><textarea class=\"review-textarea review-textarea--summary\"readonly>`), _tmpl$1 = /* @__PURE__ */ template(`<div class=harness-test-result><h3>Latest Test Run</h3><div class=job-card__meta>`), _tmpl$10 = /* @__PURE__ */ template(`<div class=field-row><label><span class=field-label>Structured Title</span><input class=\"field-input field-input--plain\"disabled></label><label><span class=field-label>Structured Summary</span><textarea class=\"review-textarea review-textarea--summary\"readonly>`);\nfunction csvValues(values) {\n return values?.join(\", \") ?? \"\";\n}\nfunction parseCsvValues(value) {\n const items = value.split(\",\").map((item) => item.trim()).filter(Boolean);\n return items.length > 0 ? [...new Set(items)] : void 0;\n}\nfunction compactMatch(match) {\n if (!match) return;\n const next = {\n calendarEventIds: match.calendarEventIds,\n eventKinds: match.eventKinds,\n folderIds: match.folderIds,\n folderNames: match.folderNames,\n meetingIds: match.meetingIds,\n recurringEventIds: match.recurringEventIds,\n tags: match.tags,\n titleIncludes: match.titleIncludes,\n titleMatches: match.titleMatches?.trim() || void 0,\n transcriptLoaded: match.transcriptLoaded\n };\n return Object.values(next).some((value) => Array.isArray(value) ? value.length > 0 : value !== void 0 && value !== \"\") ? next : void 0;\n}\nfunction sanitiseHarnessSeed(value) {\n return value.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"new-harness\";\n}\nfunction nextUniqueHarnessId(existing, seed) {\n const base = sanitiseHarnessSeed(seed);\n const ids = new Set(existing.map((harness) => harness.id));\n if (!ids.has(base)) return base;\n let index = 2;\n while (ids.has(`${base}-${index}`)) index += 1;\n return `${base}-${index}`;\n}\nfunction createHarnessTemplate(existing) {\n return {\n id: nextUniqueHarnessId(existing, \"new harness\"),\n name: \"New Harness\",\n prompt: \"Describe the structure and output you want for this meeting type.\",\n provider: \"codex\"\n };\n}\nfunction duplicateHarnessTemplate(existing, harness) {\n return {\n ...structuredClone(harness),\n id: nextUniqueHarnessId(existing, `${harness.id} copy`),\n name: `${harness.name} Copy`\n };\n}\nfunction patchHarness(harness, patch) {\n return {\n ...harness,\n ...patch,\n match: compactMatch(patch.match ?? harness.match)\n };\n}\nfunction patchHarnessMatch(harness, patch) {\n return patchHarness(harness, { match: {\n ...harness.match,\n ...patch\n } });\n}\nfunction HarnessEditorPanel(props) {\n const selectedExplanation = () => props.explanations.find((explanation) => explanation.harness.id === props.selectedHarnessId) ?? null;\n const selectedResult = () => props.testResult;\n const selectedHarness = () => props.selectedHarness;\n const updateHarness = (patch) => {\n const harness = selectedHarness();\n if (!harness) return;\n props.onChange(patchHarness(harness, patch));\n };\n const updateMatch = (patch) => {\n const harness = selectedHarness();\n if (!harness) return;\n props.onChange(patchHarnessMatch(harness, patch));\n };\n return (() => {\n var _el$ = _tmpl$2$2(), _el$3 = _el$.firstChild.nextSibling, _el$5 = _el$3.firstChild.firstChild, _el$6 = _el$5.firstChild, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling, _el$9 = _el$8.nextSibling, _el$0 = _el$9.nextSibling, _el$1 = _el$5.nextSibling;\n addEventListener(_el$6, \"click\", props.onNew, true);\n addEventListener(_el$7, \"click\", props.onDuplicate, true);\n addEventListener(_el$8, \"click\", props.onRemove, true);\n addEventListener(_el$9, \"click\", props.onReload, true);\n addEventListener(_el$0, \"click\", props.onSave, true);\n insert(_el$1, createComponent(For, {\n get each() {\n return props.harnesses;\n },\n children: (harness) => {\n const explanation = () => props.explanations.find((candidate) => candidate.harness.id === harness.id) ?? null;\n return (() => {\n var _el$11 = _tmpl$3$2(), _el$12 = _el$11.firstChild, _el$13 = _el$12.firstChild, _el$14 = _el$13.nextSibling, _el$15 = _el$12.nextSibling;\n _el$11.$$click = () => {\n props.onSelect(harness.id);\n };\n insert(_el$13, () => harness.name);\n insert(_el$14, () => explanation()?.matched ? \"Matched\" : \"Check\");\n insert(_el$15, () => harness.id, null);\n insert(_el$15, (() => {\n var _c$ = memo(() => !!harness.provider);\n return () => _c$() ? ` • ${harness.provider}` : \"\";\n })(), null);\n insert(_el$15, (() => {\n var _c$2 = memo(() => !!harness.model);\n return () => _c$2() ? `/${harness.model}` : \"\";\n })(), null);\n createRenderEffect((_p$) => {\n var _v$4 = props.selectedHarnessId === harness.id, _v$5 = explanation()?.matched ? \"completed\" : \"warning\";\n _v$4 !== _p$.e && setAttribute(_el$11, \"data-selected\", _p$.e = _v$4);\n _v$5 !== _p$.t && setAttribute(_el$14, \"data-status\", _p$.t = _v$5);\n return _p$;\n }, {\n e: void 0,\n t: void 0\n });\n return _el$11;\n })();\n }\n }), null);\n insert(_el$1, createComponent(Show, {\n get when() {\n return props.harnesses.length === 0;\n },\n get children() {\n return _tmpl$$2();\n }\n }), null);\n insert(_el$3, createComponent(Show, {\n get when() {\n return selectedHarness();\n },\n get fallback() {\n return _tmpl$4$2();\n },\n children: (harness) => (() => {\n var _el$17 = _tmpl$6$1(), _el$18 = _el$17.firstChild, _el$19 = _el$18.firstChild, _el$20 = _el$19.nextSibling, _el$21 = _el$18.nextSibling, _el$22 = _el$21.firstChild, _el$24 = _el$22.firstChild.nextSibling, _el$27 = _el$22.nextSibling.firstChild.nextSibling, _el$28 = _el$21.nextSibling, _el$29 = _el$28.firstChild, _el$31 = _el$29.firstChild.nextSibling, _el$34 = _el$29.nextSibling.firstChild.nextSibling, _el$35 = _el$28.nextSibling, _el$36 = _el$35.firstChild, _el$38 = _el$36.firstChild.nextSibling, _el$41 = _el$36.nextSibling.firstChild.nextSibling, _el$42 = _el$35.nextSibling, _el$43 = _el$42.firstChild, _el$45 = _el$43.firstChild.nextSibling, _el$48 = _el$43.nextSibling.firstChild.nextSibling, _el$49 = _el$42.nextSibling, _el$50 = _el$49.firstChild, _el$52 = _el$50.firstChild.nextSibling, _el$55 = _el$50.nextSibling.firstChild.nextSibling, _el$56 = _el$49.nextSibling, _el$59 = _el$56.firstChild.firstChild.nextSibling, _el$60 = _el$56.nextSibling, _el$61 = _el$60.firstChild, _el$63 = _el$61.firstChild.nextSibling, _el$66 = _el$61.nextSibling.firstChild.nextSibling, _el$67 = _el$60.nextSibling, _el$68 = _el$67.firstChild, _el$70 = _el$68.firstChild.nextSibling, _el$73 = _el$68.nextSibling.firstChild.nextSibling, _el$74 = _el$67.nextSibling, _el$75 = _el$74.firstChild, _el$77 = _el$75.firstChild.nextSibling, _el$80 = _el$75.nextSibling.firstChild.nextSibling, _el$81 = _el$74.nextSibling, _el$82 = _el$81.firstChild, _el$84 = _el$82.firstChild.nextSibling, _el$87 = _el$82.nextSibling.firstChild.nextSibling, _el$88 = _el$81.nextSibling, _el$89 = _el$88.firstChild, _el$91 = _el$89.firstChild.nextSibling, _el$94 = _el$89.nextSibling.firstChild.nextSibling, _el$95 = _el$88.nextSibling, _el$97 = _el$95.firstChild.nextSibling, _el$98 = _el$97.nextSibling, _el$99 = _el$98.firstChild, _el$101 = _el$99.firstChild.nextSibling, _el$104 = _el$99.nextSibling.firstChild.nextSibling, _el$106 = _el$98.nextSibling.firstChild;\n insert(_el$19, () => harness().name);\n insert(_el$20, () => props.dirty ? \"Unsaved changes\" : \"Saved\");\n _el$24.$$input = (event) => {\n updateHarness({ id: event.currentTarget.value.trim() });\n };\n _el$27.$$input = (event) => {\n updateHarness({ name: event.currentTarget.value });\n };\n _el$31.addEventListener(\"change\", (event) => {\n updateHarness({ provider: event.currentTarget.value === \"\" ? void 0 : event.currentTarget.value });\n });\n _el$34.$$input = (event) => {\n updateHarness({ model: event.currentTarget.value.trim() || void 0 });\n };\n _el$38.$$input = (event) => {\n const value = event.currentTarget.value.trim();\n updateHarness({ priority: value ? Number(value) : void 0 });\n };\n _el$41.$$input = (event) => {\n updateHarness({ cwd: event.currentTarget.value.trim() || void 0 });\n };\n _el$45.$$input = (event) => {\n updateHarness({ prompt: event.currentTarget.value.trim() || void 0 });\n };\n _el$48.$$input = (event) => {\n updateHarness({ promptFile: event.currentTarget.value.trim() || void 0 });\n };\n _el$52.$$input = (event) => {\n updateHarness({ systemPrompt: event.currentTarget.value.trim() || void 0 });\n };\n _el$55.$$input = (event) => {\n updateHarness({ systemPromptFile: event.currentTarget.value.trim() || void 0 });\n };\n _el$59.$$input = (event) => {\n updateHarness({ fallbackHarnessIds: parseCsvValues(event.currentTarget.value) });\n };\n _el$63.$$input = (event) => {\n updateMatch({ eventKinds: parseCsvValues(event.currentTarget.value) });\n };\n _el$66.$$input = (event) => {\n updateMatch({ meetingIds: parseCsvValues(event.currentTarget.value) });\n };\n _el$70.$$input = (event) => {\n updateMatch({ folderIds: parseCsvValues(event.currentTarget.value) });\n };\n _el$73.$$input = (event) => {\n updateMatch({ folderNames: parseCsvValues(event.currentTarget.value) });\n };\n _el$77.$$input = (event) => {\n updateMatch({ tags: parseCsvValues(event.currentTarget.value) });\n };\n _el$80.$$input = (event) => {\n updateMatch({ titleIncludes: parseCsvValues(event.currentTarget.value) });\n };\n _el$84.$$input = (event) => {\n updateMatch({ titleMatches: event.currentTarget.value.trim() || void 0 });\n };\n _el$87.addEventListener(\"change\", (event) => {\n updateMatch({ transcriptLoaded: event.currentTarget.value === \"\" ? void 0 : event.currentTarget.value === \"true\" });\n });\n _el$91.$$input = (event) => {\n updateMatch({ calendarEventIds: parseCsvValues(event.currentTarget.value) });\n };\n _el$94.$$input = (event) => {\n updateMatch({ recurringEventIds: parseCsvValues(event.currentTarget.value) });\n };\n insert(_el$97, (() => {\n var _c$3 = memo(() => !!props.selectedMeeting);\n return () => _c$3() ? `Run ${harness().name} against ${props.selectedMeeting.meeting.title || props.selectedMeeting.meeting.id}.` : \"Select a meeting to explain and test this harness.\";\n })());\n _el$101.addEventListener(\"change\", (event) => {\n props.onTestKindChange(event.currentTarget.value);\n });\n addEventListener(_el$106, \"click\", props.onTest, true);\n insert(_el$95, createComponent(Show, {\n get when() {\n return selectedExplanation();\n },\n children: (explanation) => [(() => {\n var _el$108 = _tmpl$7$1();\n insert(_el$108, () => explanation().matched ? \"Matched selected meeting\" : \"Did not match selected meeting\");\n createRenderEffect(() => setAttribute(_el$108, \"data-status\", explanation().matched ? \"completed\" : \"warning\"));\n return _el$108;\n })(), (() => {\n var _el$109 = _tmpl$8$1();\n insert(_el$109, createComponent(For, {\n get each() {\n return explanation().reasons;\n },\n children: (reason) => (() => {\n var _el$110 = _tmpl$9();\n insert(_el$110, reason);\n return _el$110;\n })()\n }));\n return _el$109;\n })()]\n }), null);\n insert(_el$95, createComponent(Show, {\n get when() {\n return selectedResult();\n },\n children: (result) => (() => {\n var _el$111 = _tmpl$1(), _el$113 = _el$111.firstChild.nextSibling;\n insert(_el$113, () => result().status, null);\n insert(_el$113, (() => {\n var _c$4 = memo(() => !!result().provider);\n return () => _c$4() ? ` • ${result().provider}` : \"\";\n })(), null);\n insert(_el$113, (() => {\n var _c$5 = memo(() => !!result().model);\n return () => _c$5() ? `/${result().model}` : \"\";\n })(), null);\n insert(_el$113, (() => {\n var _c$6 = memo(() => !!result().parseMode);\n return () => _c$6() ? ` • ${result().parseMode}` : \"\";\n })(), null);\n insert(_el$111, createComponent(Show, {\n get when() {\n return result().status === \"failed\";\n },\n get children() {\n var _el$114 = _tmpl$5$2();\n insert(_el$114, () => result().error);\n return _el$114;\n }\n }), null);\n insert(_el$111, createComponent(Show, {\n get when() {\n return result().status === \"completed\";\n },\n get children() {\n return [(() => {\n var _el$115 = _tmpl$0(), _el$116 = _el$115.firstChild, _el$118 = _el$116.firstChild.nextSibling, _el$121 = _el$116.nextSibling.firstChild.nextSibling;\n createRenderEffect(() => _el$118.value = result().prompt);\n createRenderEffect(() => _el$121.value = result().rawOutput ?? \"\");\n return _el$115;\n })(), createComponent(Show, {\n get when() {\n return result().structured;\n },\n children: (structured) => (() => {\n var _el$122 = _tmpl$10(), _el$123 = _el$122.firstChild, _el$125 = _el$123.firstChild.nextSibling, _el$128 = _el$123.nextSibling.firstChild.nextSibling;\n createRenderEffect(() => _el$125.value = structured().title);\n createRenderEffect(() => _el$128.value = structured().summary ?? \"\");\n return _el$122;\n })()\n })];\n }\n }), null);\n return _el$111;\n })()\n }), null);\n insert(_el$17, createComponent(Show, {\n get when() {\n return props.error;\n },\n get children() {\n var _el$107 = _tmpl$5$2();\n insert(_el$107, () => props.error);\n return _el$107;\n }\n }), null);\n createRenderEffect((_p$) => {\n var _v$6 = props.dirty ? \"busy\" : \"ok\", _v$7 = !props.selectedMeeting;\n _v$6 !== _p$.e && setAttribute(_el$20, \"data-tone\", _p$.e = _v$6);\n _v$7 !== _p$.t && (_el$106.disabled = _p$.t = _v$7);\n return _p$;\n }, {\n e: void 0,\n t: void 0\n });\n createRenderEffect(() => _el$24.value = harness().id);\n createRenderEffect(() => _el$27.value = harness().name);\n createRenderEffect(() => _el$31.value = harness().provider ?? \"\");\n createRenderEffect(() => _el$34.value = harness().model ?? \"\");\n createRenderEffect(() => _el$38.value = harness().priority?.toString() ?? \"\");\n createRenderEffect(() => _el$41.value = harness().cwd ?? \"\");\n createRenderEffect(() => _el$45.value = harness().prompt ?? \"\");\n createRenderEffect(() => _el$48.value = harness().promptFile ?? \"\");\n createRenderEffect(() => _el$52.value = harness().systemPrompt ?? \"\");\n createRenderEffect(() => _el$55.value = harness().systemPromptFile ?? \"\");\n createRenderEffect(() => _el$59.value = csvValues(harness().fallbackHarnessIds));\n createRenderEffect(() => _el$63.value = csvValues(harness().match?.eventKinds));\n createRenderEffect(() => _el$66.value = csvValues(harness().match?.meetingIds));\n createRenderEffect(() => _el$70.value = csvValues(harness().match?.folderIds));\n createRenderEffect(() => _el$73.value = csvValues(harness().match?.folderNames));\n createRenderEffect(() => _el$77.value = csvValues(harness().match?.tags));\n createRenderEffect(() => _el$80.value = csvValues(harness().match?.titleIncludes));\n createRenderEffect(() => _el$84.value = harness().match?.titleMatches ?? \"\");\n createRenderEffect(() => _el$87.value = harness().match?.transcriptLoaded === void 0 ? \"\" : String(harness().match?.transcriptLoaded));\n createRenderEffect(() => _el$91.value = csvValues(harness().match?.calendarEventIds));\n createRenderEffect(() => _el$94.value = csvValues(harness().match?.recurringEventIds));\n createRenderEffect(() => _el$101.value = props.testKind);\n createRenderEffect(() => _el$104.value = props.explanationEventKind ?? \"Select a meeting\");\n return _el$17;\n })()\n }), null);\n createRenderEffect((_p$) => {\n var _v$ = !selectedHarness(), _v$2 = !selectedHarness(), _v$3 = !props.dirty;\n _v$ !== _p$.e && (_el$7.disabled = _p$.e = _v$);\n _v$2 !== _p$.t && (_el$8.disabled = _p$.t = _v$2);\n _v$3 !== _p$.a && (_el$0.disabled = _p$.a = _v$3);\n return _p$;\n }, {\n e: void 0,\n t: void 0,\n a: void 0\n });\n return _el$;\n })();\n}\ndelegateEvents([\"click\", \"input\"]);\n//#endregion\n//#region src/agent-defaults.ts\nvar defaultGranolaAgentModels = {\n codex: \"gpt-5-codex\",\n openai: \"gpt-5-mini\",\n openrouter: \"openai/gpt-5-mini\"\n};\nfunction defaultGranolaAgentModel(provider, explicitModel) {\n return explicitModel?.trim() || defaultGranolaAgentModels[provider];\n}\nfunction granolaAgentProviderLabel(provider) {\n switch (provider) {\n case \"codex\": return \"Codex\";\n case \"openai\": return \"OpenAI\";\n case \"openrouter\": return \"OpenRouter\";\n }\n}\n//#endregion\n//#region src/web-app/onboarding.tsx\n/** @jsxImportSource solid-js */\nvar _tmpl$$1 = /* @__PURE__ */ template(`<section class=onboarding-panel><div class=onboarding-panel__hero><div><p class=onboarding-panel__eyebrow>First-Run Setup</p><h2>Connect Granola, import your meetings, and pick an agent.</h2><p>The toolkit already has the core automation engine. This flow just gets you to the first useful setup without dumping every advanced panel on screen at once.</p></div><div class=onboarding-panel__summary><div class=job-card__status></div><div class=auth-card__meta></div></div></div><div class=onboarding-steps>`), _tmpl$2$1 = /* @__PURE__ */ template(`<div class=auth-card__meta>Single-folder workspace detected: `), _tmpl$3$1 = /* @__PURE__ */ template(`<div class=onboarding-step__body><input class=input placeholder=grn_... type=password><div class=toolbar-actions><button class=\"button button--primary\"type=button>Save API key</button><button class=\"button button--secondary\"type=button>Import desktop session`), _tmpl$4$1 = /* @__PURE__ */ template(`<div class=onboarding-step__body><button class=\"button button--primary\"type=button>Import meetings now`), _tmpl$5$1 = /* @__PURE__ */ template(`<div class=onboarding-step__body><div class=onboarding-providers></div><div class=auth-card__meta>Starter pipeline: transcript-ready meetings will generate reviewable notes with <!>.</div><button class=\"button button--primary\"type=button>Create starter pipeline`), _tmpl$6 = /* @__PURE__ */ template(`<article class=onboarding-step><div class=onboarding-step__head><div><span class=onboarding-step__number>Step </span><h3></h3></div><div class=job-card__status></div></div><p>`), _tmpl$7 = /* @__PURE__ */ template(`<div class=auth-card__meta>`), _tmpl$8 = /* @__PURE__ */ template(`<button class=onboarding-provider type=button><span class=onboarding-provider__title></span><span class=onboarding-provider__body>`);\nvar starterHarnessId = \"starter-meeting-notes\";\nvar starterRuleId = \"starter-meeting-notes-review\";\nvar starterRuleActionId = \"starter-meeting-notes-pipeline\";\nvar providerOptions = [\n {\n description: \"Use the local Codex CLI and your ChatGPT subscription.\",\n provider: \"codex\"\n },\n {\n description: \"Use an OPENROUTER_API_KEY and route to your preferred model.\",\n provider: \"openrouter\"\n },\n {\n description: \"Use an OPENAI_API_KEY with direct OpenAI access.\",\n provider: \"openai\"\n }\n];\nfunction suggestedFolderLabel(folders) {\n if (folders.length === 1) return folders[0]?.name;\n}\nfunction deriveOnboardingState(input) {\n const auth = input.appState?.auth;\n const connected = Boolean(auth?.apiKeyAvailable || auth?.storedSessionAvailable);\n const synced = Boolean(input.appState?.sync.lastCompletedAt);\n const pipelineReady = input.harnesses.length > 0 && input.automationRuleCount > 0;\n const syncedMeetingCount = input.appState?.documents.count ?? input.meetingsLoadedCount;\n const stepCards = [\n {\n body: \"Store a Granola API key for the toolkit. Desktop session import stays available as a fallback.\",\n complete: connected,\n cta: connected ? void 0 : \"Save API key\",\n detail: connected ? `Connected via ${auth?.mode === \"api-key\" ? \"API key\" : auth?.mode === \"stored-session\" ? \"stored session\" : \"fallback auth\"}.` : \"Recommended: use a Granola Personal API key from Settings → API.\",\n title: \"Connect Granola\"\n },\n {\n body: \"Run the first sync so the local index has meetings, folders, and transcript-ready events.\",\n complete: synced,\n cta: synced ? void 0 : \"Import meetings\",\n detail: synced ? `${syncedMeetingCount} meetings indexed locally.` : \"This builds the local meeting index the workspace and automation layer rely on.\",\n title: \"Import Meetings\"\n },\n {\n body: \"Choose the AI agent you want to use by default, then seed a starter reviewable notes pipeline.\",\n complete: pipelineReady,\n cta: pipelineReady ? void 0 : \"Create starter pipeline\",\n detail: pipelineReady ? \"Starter harnesses and automation rules are ready.\" : \"The starter pipeline generates reviewable meeting notes on transcript-ready events.\",\n title: \"Choose An Agent\"\n }\n ];\n return {\n complete: connected && synced && pipelineReady,\n connected,\n pipelineReady,\n stepCards,\n synced,\n syncedMeetingCount\n };\n}\nfunction upsertById(items, nextItem) {\n return [...items.filter((item) => item.id !== nextItem.id), nextItem];\n}\nfunction createStarterHarness(provider) {\n return {\n id: starterHarnessId,\n match: { transcriptLoaded: true },\n model: defaultGranolaAgentModel(provider),\n name: \"Starter Meeting Notes\",\n priority: 10,\n prompt: [\n \"Turn this meeting into concise, high-signal notes for internal follow-up.\",\n \"\",\n \"Requirements:\",\n \"- lead with the most important outcome, not a play-by-play recap\",\n \"- capture decisions, action items, open questions, and risks\",\n \"- assign owners when the transcript makes them clear\",\n \"- call out uncertainty explicitly instead of inventing missing detail\",\n \"- optimise for a team reading this five minutes after the meeting ends\"\n ].join(\"\\n\"),\n provider\n };\n}\nfunction createStarterRule() {\n return {\n actions: [{\n approvalMode: \"manual\",\n harnessId: starterHarnessId,\n id: starterRuleActionId,\n kind: \"agent\",\n name: \"Generate starter meeting notes\",\n pipeline: { kind: \"notes\" }\n }],\n id: starterRuleId,\n name: \"Review starter notes when a transcript is ready\",\n when: {\n eventKinds: [\"transcript.ready\"],\n transcriptLoaded: true\n }\n };\n}\nfunction buildStarterPipeline(existing) {\n const harness = createStarterHarness(existing.provider);\n const rule = createStarterRule();\n return {\n harnesses: upsertById(existing.harnesses, harness),\n rule,\n rules: upsertById(existing.rules, rule)\n };\n}\nfunction OnboardingPanel(props) {\n const dominantFolder = () => suggestedFolderLabel(props.folders);\n return (() => {\n var _el$ = _tmpl$$1(), _el$2 = _el$.firstChild, _el$4 = _el$2.firstChild.nextSibling, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$2.nextSibling;\n insert(_el$5, () => props.state.complete ? \"Ready\" : \"Setup in progress\");\n insert(_el$6, (() => {\n var _c$ = memo(() => !!props.state.synced);\n return () => _c$() ? `${props.state.syncedMeetingCount} meetings indexed` : \"No local sync yet\";\n })());\n insert(_el$4, createComponent(Show, {\n get when() {\n return dominantFolder();\n },\n children: (folderName) => (() => {\n var _el$8 = _tmpl$2$1();\n _el$8.firstChild;\n insert(_el$8, folderName, null);\n return _el$8;\n })()\n }), null);\n insert(_el$7, createComponent(For, {\n get each() {\n return props.state.stepCards;\n },\n children: (step, index) => (() => {\n var _el$0 = _tmpl$6(), _el$1 = _el$0.firstChild, _el$10 = _el$1.firstChild, _el$11 = _el$10.firstChild;\n _el$11.firstChild;\n var _el$13 = _el$11.nextSibling, _el$14 = _el$10.nextSibling, _el$15 = _el$1.nextSibling;\n insert(_el$11, () => index() + 1, null);\n insert(_el$13, () => step.title);\n insert(_el$14, () => step.complete ? \"Done\" : \"Next\");\n insert(_el$15, () => step.body);\n insert(_el$0, createComponent(Show, {\n get when() {\n return step.detail;\n },\n children: (detail) => (() => {\n var _el$31 = _tmpl$7();\n insert(_el$31, detail);\n return _el$31;\n })()\n }), null);\n insert(_el$0, createComponent(Show, {\n get when() {\n return memo(() => index() === 0)() && !step.complete;\n },\n get children() {\n var _el$16 = _tmpl$3$1(), _el$17 = _el$16.firstChild, _el$19 = _el$17.nextSibling.firstChild, _el$20 = _el$19.nextSibling;\n _el$17.$$input = (event) => {\n props.onApiKeyDraftChange(event.currentTarget.value);\n };\n addEventListener(_el$19, \"click\", props.onSaveApiKey, true);\n addEventListener(_el$20, \"click\", props.onImportDesktopSession, true);\n createRenderEffect(() => _el$20.disabled = !props.auth?.supabaseAvailable);\n createRenderEffect(() => _el$17.value = props.apiKeyDraft);\n return _el$16;\n }\n }), null);\n insert(_el$0, createComponent(Show, {\n get when() {\n return memo(() => index() === 1)() && !step.complete;\n },\n get children() {\n var _el$21 = _tmpl$4$1(), _el$22 = _el$21.firstChild;\n addEventListener(_el$22, \"click\", props.onRunSync, true);\n createRenderEffect(() => _el$22.disabled = !props.state.connected);\n return _el$21;\n }\n }), null);\n insert(_el$0, createComponent(Show, {\n get when() {\n return memo(() => index() === 2)() && !step.complete;\n },\n get children() {\n var _el$23 = _tmpl$5$1(), _el$24 = _el$23.firstChild, _el$25 = _el$24.nextSibling, _el$29 = _el$25.firstChild.nextSibling;\n _el$29.nextSibling;\n var _el$30 = _el$25.nextSibling;\n insert(_el$24, createComponent(For, {\n each: providerOptions,\n children: (option) => (() => {\n var _el$32 = _tmpl$8(), _el$33 = _el$32.firstChild, _el$34 = _el$33.nextSibling;\n _el$32.$$click = () => {\n props.onSelectProvider(option.provider);\n };\n insert(_el$33, () => granolaAgentProviderLabel(option.provider));\n insert(_el$34, () => option.description);\n createRenderEffect(() => setAttribute(_el$32, \"data-selected\", props.preferredProvider === option.provider));\n return _el$32;\n })()\n }));\n insert(_el$25, () => granolaAgentProviderLabel(props.preferredProvider), _el$29);\n addEventListener(_el$30, \"click\", props.onCreateStarterPipeline, true);\n createRenderEffect(() => _el$30.disabled = !props.state.synced);\n return _el$23;\n }\n }), null);\n createRenderEffect((_p$) => {\n var _v$ = step.complete, _v$2 = step.complete ? \"completed\" : \"warning\";\n _v$ !== _p$.e && setAttribute(_el$0, \"data-complete\", _p$.e = _v$);\n _v$2 !== _p$.t && setAttribute(_el$14, \"data-status\", _p$.t = _v$2);\n return _p$;\n }, {\n e: void 0,\n t: void 0\n });\n return _el$0;\n })()\n }));\n createRenderEffect(() => setAttribute(_el$5, \"data-status\", props.state.complete ? \"completed\" : \"warning\"));\n return _el$;\n })();\n}\ndelegateEvents([\"input\", \"click\"]);\n//#endregion\n//#region src/web-app/App.tsx\n/** @jsxImportSource solid-js */\nvar _tmpl$ = /* @__PURE__ */ template(`<section class=control-deck__panel>`), _tmpl$2 = /* @__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>Clear Filters</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.</p></section><section class=control-deck><div class=control-deck__tabs></div><div class=control-deck__body>`), _tmpl$3 = /* @__PURE__ */ template(`<div class=\"shell shell--onboarding\"><main class=\"pane detail detail--onboarding\">`), _tmpl$4 = /* @__PURE__ */ template(`<section class=jobs-panel><div class=auth-card><div class=\"auth-card__meta auth-card__error\">`), _tmpl$5 = /* @__PURE__ */ template(`<button class=workspace-tab type=button>`);\nfunction browserConfig() {\n return { passwordRequired: Boolean(window.__GRANOLA_SERVER__?.passwordRequired) };\n}\nasync function requestJson(path, init) {\n const response = await fetch(path, init);\n const payload = await response.json().catch(() => ({}));\n if (!response.ok) {\n const error = typeof payload.error === \"string\" && payload.error.trim() ? payload.error : response.statusText || \"Request failed\";\n throw new Error(error);\n }\n return payload;\n}\nfunction App() {\n const startup = startupSelectionFromSearch(window.location.search);\n const initialPreferences = parseWorkspacePreferences(window.localStorage.getItem(granolaWebWorkspaceStorageKey));\n const [state, setState] = createStore({\n apiKeyDraft: \"\",\n automationArtefactDraftMarkdown: \"\",\n automationArtefactDraftSummary: \"\",\n automationArtefactDraftTitle: \"\",\n automationArtefactError: \"\",\n automationArtefacts: [],\n automationRules: [],\n appState: null,\n automationRuns: [],\n detailError: \"\",\n folderError: \"\",\n folders: [],\n harnessDirty: false,\n harnessError: \"\",\n harnessExplainEventKind: null,\n harnessExplanations: [],\n harnessTestKind: \"notes\",\n harnessTestResult: null,\n harnesses: [],\n listError: \"\",\n meetingSource: \"live\",\n meetings: [],\n processingIssueError: \"\",\n processingIssues: [],\n preferredProvider: \"openrouter\",\n quickOpen: \"\",\n recentMeetings: initialPreferences.recentMeetings,\n savedFilters: initialPreferences.savedFilters,\n search: \"\",\n selectedAutomationArtefactId: null,\n selectedFolderId: startup.folderId || null,\n selectedHarnessId: null,\n selectedMeetingBundle: null,\n selectedMeetingId: startup.meetingId || null,\n selectedMeeting: null,\n reviewNote: \"\",\n controlPanelTab: \"overview\",\n serverLocked: browserConfig().passwordRequired,\n serverPassword: \"\",\n sort: \"updated-desc\",\n statusLabel: browserConfig().passwordRequired ? \"Server locked\" : \"Connecting…\",\n statusTone: browserConfig().passwordRequired ? \"error\" : \"idle\",\n updatedFrom: \"\",\n updatedTo: \"\",\n workspaceTab: parseWorkspaceTab(startup.workspaceTab)\n });\n let client = null;\n let automationPanelsHydrated = false;\n let unsubscribe;\n const setStatus = (label, tone = \"idle\") => {\n setState({\n statusLabel: label,\n statusTone: tone\n });\n };\n const updatePreferences = (updater) => {\n const next = updater({\n recentMeetings: state.recentMeetings,\n savedFilters: state.savedFilters\n });\n window.localStorage.setItem(granolaWebWorkspaceStorageKey, serialiseWorkspacePreferences(next));\n setState(\"recentMeetings\", next.recentMeetings);\n setState(\"savedFilters\", next.savedFilters);\n };\n const mergeAuthState = async (authState) => {\n if (!client) return;\n const nextState = client.getState();\n if (authState) {\n setState(\"appState\", {\n ...nextState,\n auth: authState\n });\n return;\n }\n try {\n setState(\"appState\", {\n ...nextState,\n auth: await client.inspectAuth()\n });\n } catch {\n setState(\"appState\", nextState);\n }\n };\n const detachClient = async () => {\n unsubscribe?.();\n unsubscribe = void 0;\n if (client) {\n await client.close().catch(() => void 0);\n client = null;\n }\n };\n const attachClient = async () => {\n await detachClient();\n client = await createGranolaServerClient(window.location.origin);\n setState(\"appState\", client.getState());\n unsubscribe = client.subscribe((event) => {\n setState(\"appState\", event.state);\n });\n await mergeAuthState();\n };\n const loadFolders = async (refresh = false) => {\n if (!client) return;\n try {\n setState(\"folderError\", \"\");\n const result = await client.listFolders({\n forceRefresh: refresh,\n limit: 100\n });\n setState(\"folders\", result.folders);\n if (state.selectedFolderId && !result.folders.some((folder) => folder.id === state.selectedFolderId)) setState(\"selectedFolderId\", null);\n } catch (error) {\n setState(\"folderError\", error instanceof Error ? error.message : String(error));\n setState(\"folders\", []);\n setState(\"selectedFolderId\", null);\n }\n };\n const loadAutomationRuns = async () => {\n if (!client) return;\n try {\n setState(\"automationRuns\", (await client.listAutomationRuns({ limit: 20 })).runs);\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n }\n };\n const loadAutomationRules = async () => {\n if (!client) return;\n try {\n setState(\"automationRules\", (await client.listAutomationRules()).rules);\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setState(\"automationRules\", []);\n }\n };\n const syncSelectedArtefact = (artefacts, options = {}) => {\n const preferred = (options.preferredId ? artefacts.find((candidate) => candidate.id === options.preferredId) : void 0) ?? (options.preferredMeetingId ? artefacts.find((candidate) => candidate.meetingId === options.preferredMeetingId && candidate.status === \"generated\") : void 0) ?? artefacts.find((candidate) => candidate.status === \"generated\") ?? artefacts[0];\n setState(\"selectedAutomationArtefactId\", preferred?.id ?? null);\n setState(\"automationArtefactDraftTitle\", preferred?.structured.title ?? \"\");\n setState(\"automationArtefactDraftSummary\", preferred?.structured.summary ?? \"\");\n setState(\"automationArtefactDraftMarkdown\", preferred?.structured.markdown ?? \"\");\n setState(\"reviewNote\", \"\");\n };\n const loadAutomationArtefacts = async (options = {}) => {\n if (!client) return;\n try {\n setState(\"automationArtefactError\", \"\");\n const result = await client.listAutomationArtefacts({ limit: 30 });\n setState(\"automationArtefacts\", result.artefacts);\n syncSelectedArtefact(result.artefacts, {\n preferredId: options.preferredId ?? state.selectedAutomationArtefactId,\n preferredMeetingId: options.preferredMeetingId ?? state.selectedMeetingId\n });\n } catch (error) {\n setState(\"automationArtefactError\", error instanceof Error ? error.message : String(error));\n setState(\"automationArtefacts\", []);\n syncSelectedArtefact([]);\n }\n };\n const loadProcessingIssues = async () => {\n if (!client) return;\n try {\n setState(\"processingIssueError\", \"\");\n setState(\"processingIssues\", (await client.listProcessingIssues({ limit: 20 })).issues);\n } catch (error) {\n setState(\"processingIssueError\", error instanceof Error ? error.message : String(error));\n setState(\"processingIssues\", []);\n }\n };\n const selectHarnessId = (harnesses, preferredId) => {\n if (preferredId && harnesses.some((harness) => harness.id === preferredId)) return preferredId;\n return harnesses[0]?.id ?? null;\n };\n const selectedHarness = () => state.harnesses.find((harness) => harness.id === state.selectedHarnessId) ?? null;\n const loadHarnessExplanations = async (meetingId) => {\n if (!client || !meetingId) {\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n return;\n }\n try {\n const result = await client.explainAgentHarnesses(meetingId);\n setState(\"harnessExplainEventKind\", result.eventKind);\n setState(\"harnessExplanations\", result.harnesses);\n } catch (error) {\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n setState(\"harnessError\", error instanceof Error ? error.message : String(error));\n }\n };\n const loadHarnesses = async (preferredId) => {\n if (!client) return;\n try {\n setState(\"harnessError\", \"\");\n const result = await client.listAgentHarnesses();\n const nextSelectedHarnessId = selectHarnessId(result.harnesses, preferredId ?? state.selectedHarnessId);\n setState(\"harnesses\", result.harnesses);\n setState(\"selectedHarnessId\", nextSelectedHarnessId);\n const nextPreferredProvider = result.harnesses.find((harness) => harness.provider)?.provider;\n if (nextPreferredProvider) setState(\"preferredProvider\", nextPreferredProvider);\n setState(\"harnessDirty\", false);\n setState(\"harnessTestResult\", null);\n await loadHarnessExplanations(state.selectedMeetingId);\n } catch (error) {\n setState(\"harnessError\", error instanceof Error ? error.message : String(error));\n setState(\"harnesses\", []);\n setState(\"selectedHarnessId\", null);\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n setState(\"harnessTestResult\", null);\n }\n };\n const updateHarness = (nextHarness) => {\n setState(\"harnesses\", state.harnesses.map((harness) => harness.id === nextHarness.id ? nextHarness : harness));\n setState(\"selectedHarnessId\", nextHarness.id);\n setState(\"harnessDirty\", true);\n setState(\"harnessTestResult\", null);\n };\n const createHarness = () => {\n const nextHarness = createHarnessTemplate(state.harnesses);\n setState(\"harnesses\", [...state.harnesses, nextHarness]);\n setState(\"selectedHarnessId\", nextHarness.id);\n setState(\"harnessDirty\", true);\n setState(\"harnessTestResult\", null);\n };\n const duplicateHarness = () => {\n const harness = selectedHarness();\n if (!harness) return;\n const nextHarness = duplicateHarnessTemplate(state.harnesses, harness);\n setState(\"harnesses\", [...state.harnesses, nextHarness]);\n setState(\"selectedHarnessId\", nextHarness.id);\n setState(\"harnessDirty\", true);\n setState(\"harnessTestResult\", null);\n };\n const removeHarness = () => {\n if (!state.selectedHarnessId) return;\n const nextHarnesses = state.harnesses.filter((harness) => harness.id !== state.selectedHarnessId);\n setState(\"harnesses\", nextHarnesses);\n setState(\"selectedHarnessId\", selectHarnessId(nextHarnesses, null));\n setState(\"harnessDirty\", true);\n setState(\"harnessTestResult\", null);\n };\n const saveHarnesses = async () => {\n if (!client) return;\n setStatus(\"Saving harnesses…\", \"busy\");\n try {\n const result = await client.saveAgentHarnesses(state.harnesses);\n const nextSelectedHarnessId = selectHarnessId(result.harnesses, state.selectedHarnessId);\n setState(\"harnesses\", result.harnesses);\n setState(\"selectedHarnessId\", nextSelectedHarnessId);\n const nextPreferredProvider = result.harnesses.find((harness) => harness.provider)?.provider;\n if (nextPreferredProvider) setState(\"preferredProvider\", nextPreferredProvider);\n setState(\"harnessDirty\", false);\n setState(\"harnessError\", \"\");\n await loadHarnessExplanations(state.selectedMeetingId);\n setStatus(\"Harnesses saved\", \"ok\");\n } catch (error) {\n setState(\"harnessError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Harness save failed\", \"error\");\n }\n };\n const reloadHarnesses = async () => {\n setStatus(\"Reloading harnesses…\", \"busy\");\n await loadHarnesses(state.selectedHarnessId);\n setStatus(\"Harnesses reloaded\", \"ok\");\n };\n const createStarterPipeline = async () => {\n if (!client) return;\n setStatus(\"Creating starter pipeline…\", \"busy\");\n try {\n const [currentHarnesses, currentRules] = await Promise.all([client.listAgentHarnesses(), client.listAutomationRules()]);\n const starter = buildStarterPipeline({\n harnesses: currentHarnesses.harnesses,\n provider: state.preferredProvider,\n rules: currentRules.rules\n });\n await client.saveAgentHarnesses(starter.harnesses);\n await client.saveAutomationRules(starter.rules);\n await refreshAll();\n setStatus(\"Starter pipeline ready\", \"ok\");\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Starter pipeline setup failed\", \"error\");\n }\n };\n const testHarness = async () => {\n if (!client) return;\n const harness = selectedHarness();\n if (!harness) {\n setStatus(\"Select a harness first\", \"error\");\n return;\n }\n const meetingId = state.selectedMeetingId;\n if (!meetingId) {\n setStatus(\"Select a meeting first\", \"error\");\n return;\n }\n setStatus(\"Testing harness…\", \"busy\");\n try {\n const bundle = state.selectedMeetingBundle?.document.id === meetingId ? state.selectedMeetingBundle : await client.getMeeting(meetingId, { requireCache: true });\n setState(\"harnessTestResult\", (await client.evaluateAutomationCases([{\n bundle,\n id: `web:${meetingId}`,\n title: bundle.meeting.meeting.title || bundle.document.title || bundle.document.id\n }], {\n harnessIds: [harness.id],\n kind: state.harnessTestKind,\n model: harness.model,\n provider: harness.provider\n })).results[0] ?? null);\n setStatus(\"Harness test complete\", \"ok\");\n } catch (error) {\n setState(\"harnessTestResult\", {\n caseId: `web:${meetingId}`,\n caseTitle: state.selectedMeeting?.meeting.title || meetingId,\n error: error instanceof Error ? error.message : String(error),\n harnessId: harness.id,\n harnessName: harness.name,\n prompt: \"\",\n status: \"failed\"\n });\n setStatus(\"Harness test failed\", \"error\");\n }\n };\n const loadMeeting = async (meetingId) => {\n if (!client) return;\n setState(\"selectedMeetingId\", meetingId);\n try {\n setState(\"detailError\", \"\");\n const bundle = await client.getMeeting(meetingId);\n setState(\"selectedMeetingBundle\", bundle);\n setState(\"selectedMeeting\", bundle.meeting);\n updatePreferences((preferences) => rememberRecentMeeting(preferences, bundle.meeting.meeting));\n await loadHarnessExplanations(bundle.document.id);\n await loadAutomationArtefacts({\n preferredId: state.selectedAutomationArtefactId,\n preferredMeetingId: bundle.document.id\n });\n } catch (error) {\n setState(\"selectedMeetingBundle\", null);\n setState(\"selectedMeeting\", null);\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n }\n };\n const loadMeetings = async (options = {}) => {\n if (!client) return;\n try {\n setState(\"listError\", \"\");\n const result = await client.listMeetings({\n folderId: state.selectedFolderId || void 0,\n forceRefresh: options.refresh,\n limit: 100,\n search: state.search || void 0,\n sort: state.sort,\n updatedFrom: state.updatedFrom || void 0,\n updatedTo: state.updatedTo || void 0\n });\n const preferredMeetingId = options.preferredMeetingId ?? state.selectedMeetingId;\n const nextMeetingId = selectMeetingId(result.meetings, preferredMeetingId);\n setState(\"meetings\", result.meetings);\n setState(\"meetingSource\", result.source);\n setState(\"selectedMeetingId\", nextMeetingId);\n if (nextMeetingId) await loadMeeting(nextMeetingId);\n else {\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n setState(\"detailError\", \"\");\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n await loadAutomationArtefacts({\n preferredId: state.selectedAutomationArtefactId,\n preferredMeetingId: null\n });\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n setState(\"listError\", message);\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n setState(\"detailError\", message);\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n }\n };\n const refreshAll = async (forceRefresh = false) => {\n if (!client) await attachClient();\n setStatus(forceRefresh ? \"Syncing…\" : \"Refreshing…\", \"busy\");\n if (forceRefresh) await client?.sync({\n forceRefresh: true,\n foreground: true\n });\n await Promise.all([\n loadFolders(forceRefresh),\n loadHarnesses(),\n loadAutomationRules(),\n loadAutomationRuns(),\n loadAutomationArtefacts(),\n loadProcessingIssues(),\n mergeAuthState()\n ]);\n await loadMeetings({ refresh: forceRefresh });\n setState(\"serverLocked\", false);\n setStatus(forceRefresh ? \"Sync complete\" : state.meetingSource === \"index\" ? \"Loaded from index\" : \"Connected\", \"ok\");\n };\n const connectAndRefresh = async (forceRefresh = false) => {\n try {\n await refreshAll(forceRefresh);\n } catch (error) {\n setStatus(\"Connection failed\", \"error\");\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n }\n };\n const quickOpenMeeting = async () => {\n if (!client) return;\n const query = state.quickOpen.trim();\n if (!query) {\n setStatus(\"Enter a title or id\", \"error\");\n return;\n }\n setStatus(\"Opening meeting…\", \"busy\");\n try {\n const bundle = await client.findMeeting(query);\n setState(\"selectedFolderId\", bundle.meeting.meeting.folders[0]?.id || null);\n setState(\"search\", \"\");\n setState(\"updatedFrom\", \"\");\n setState(\"updatedTo\", \"\");\n await loadMeetings({ preferredMeetingId: bundle.document.id });\n setStatus(\"Connected\", \"ok\");\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Quick open failed\", \"error\");\n }\n };\n const saveApiKey = async () => {\n if (!state.apiKeyDraft.trim()) {\n setStatus(\"Enter a Granola API key\", \"error\");\n return;\n }\n setStatus(\"Saving API key…\", \"busy\");\n try {\n const auth = await requestJson(granolaTransportPaths.authLogin, {\n body: JSON.stringify({ apiKey: state.apiKeyDraft.trim() }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n setState(\"apiKeyDraft\", \"\");\n setState(\"detailError\", \"\");\n if (state.appState) setState(\"appState\", \"auth\", auth);\n setStatus(\"API key saved\", \"ok\");\n } catch (error) {\n await mergeAuthState();\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"API key save failed\", \"error\");\n }\n };\n const importDesktopSession = async () => {\n setStatus(\"Importing desktop session…\", \"busy\");\n try {\n const auth = await requestJson(granolaTransportPaths.authLogin, {\n body: JSON.stringify({}),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n setState(\"detailError\", \"\");\n if (state.appState) setState(\"appState\", \"auth\", auth);\n setStatus(\"Desktop session imported\", \"ok\");\n } catch (error) {\n await mergeAuthState();\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Auth import failed\", \"error\");\n }\n };\n const refreshAuth = async () => {\n setStatus(\"Refreshing session…\", \"busy\");\n try {\n await mergeAuthState(await requestJson(granolaTransportPaths.authRefresh, { method: \"POST\" }));\n await refreshAll();\n } catch (error) {\n await mergeAuthState();\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Refresh failed\", \"error\");\n }\n };\n const switchAuthMode = async (mode) => {\n setStatus(\"Switching auth source…\", \"busy\");\n try {\n await mergeAuthState(await requestJson(granolaTransportPaths.authMode, {\n body: JSON.stringify({ mode }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n }));\n await refreshAll();\n } catch (error) {\n await mergeAuthState();\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Switch failed\", \"error\");\n }\n };\n const logout = async () => {\n setStatus(\"Signing out…\", \"busy\");\n try {\n await mergeAuthState(await requestJson(granolaTransportPaths.authLogout, { method: \"POST\" }));\n await refreshAll();\n } catch (error) {\n await mergeAuthState();\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Sign out failed\", \"error\");\n }\n };\n const exportNotes = async () => {\n if (!client) return;\n setStatus(state.selectedFolderId ? \"Exporting folder notes…\" : \"Exporting notes…\", \"busy\");\n try {\n await client.exportNotes(\"markdown\", { folderId: state.selectedFolderId || void 0 });\n await refreshAll();\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Export failed\", \"error\");\n }\n };\n const clearFilters = async () => {\n setState(\"search\", \"\");\n setState(\"sort\", \"updated-desc\");\n setState(\"updatedFrom\", \"\");\n setState(\"updatedTo\", \"\");\n setState(\"selectedFolderId\", null);\n setState(\"selectedMeetingId\", null);\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n await loadMeetings();\n setStatus(\"Filters cleared\", \"ok\");\n };\n const saveCurrentFilter = () => {\n updatePreferences((preferences) => saveWorkspaceFilter(preferences, {\n folders: state.folders,\n search: state.search,\n selectedFolderId: state.selectedFolderId,\n sort: state.sort,\n updatedFrom: state.updatedFrom,\n updatedTo: state.updatedTo\n }, { idFactory: () => `filter-${Date.now()}` }));\n setStatus(\"Saved filter\", \"ok\");\n };\n const applySavedFilterPreset = async (id) => {\n const preset = state.savedFilters.find((candidate) => candidate.id === id);\n if (!preset) return;\n const nextFilters = applyWorkspaceFilter(preset);\n setState(\"search\", nextFilters.search);\n setState(\"selectedFolderId\", nextFilters.selectedFolderId);\n setState(\"sort\", nextFilters.sort);\n setState(\"updatedFrom\", nextFilters.updatedFrom);\n setState(\"updatedTo\", nextFilters.updatedTo);\n setState(\"selectedMeetingId\", null);\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n await loadMeetings();\n setStatus(`Applied ${preset.label}`, \"ok\");\n };\n const removeSavedFilterPreset = (id) => {\n updatePreferences((preferences) => removeWorkspaceFilter(preferences, id));\n setStatus(\"Removed saved filter\", \"ok\");\n };\n const openRecentMeeting = async (meetingId, folderId) => {\n if (folderId !== void 0) {\n setState(\"selectedFolderId\", folderId || null);\n setState(\"selectedMeetingId\", null);\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n await loadMeetings({ preferredMeetingId: meetingId });\n return;\n }\n await loadMeeting(meetingId);\n };\n const meetingEmptyHint = () => {\n if (!state.appState) return \"Connect to the local server to load meetings.\";\n if (state.appState.auth.lastError) return \"Resolve auth first, then sync again.\";\n if (!state.appState.documents.loaded && !state.appState.sync.lastCompletedAt) return \"Run Sync now to populate your local meeting index.\";\n return \"Try a different folder or filter, or sync again.\";\n };\n const exportTranscripts = async () => {\n if (!client) return;\n setStatus(state.selectedFolderId ? \"Exporting folder transcripts…\" : \"Exporting transcripts…\", \"busy\");\n try {\n await client.exportTranscripts(\"text\", { folderId: state.selectedFolderId || void 0 });\n await refreshAll();\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Export failed\", \"error\");\n }\n };\n const rerunJob = async (jobId) => {\n if (!client) return;\n setStatus(\"Rerunning export…\", \"busy\");\n try {\n await client.rerunExportJob(jobId);\n await refreshAll();\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Rerun failed\", \"error\");\n }\n };\n const selectedAutomationArtefact = () => state.automationArtefacts.find((artefact) => artefact.id === state.selectedAutomationArtefactId) || null;\n const resolveAutomationRun = async (id, decision) => {\n if (!client) return;\n setStatus(decision === \"approve\" ? \"Approving automation…\" : \"Rejecting automation…\", \"busy\");\n try {\n await client.resolveAutomationRun(id, decision);\n await refreshAll();\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Automation decision failed\", \"error\");\n }\n };\n const recoverProcessingIssue = async (id) => {\n if (!client) return;\n setStatus(\"Recovering processing issue…\", \"busy\");\n try {\n const result = await client.recoverProcessingIssue(id);\n await refreshAll();\n setStatus(result.runCount > 0 ? `Recovered ${result.issue.kind} and re-ran ${result.runCount} pipeline${result.runCount === 1 ? \"\" : \"s\"}` : `Recovered ${result.issue.kind}`, \"ok\");\n } catch (error) {\n setState(\"processingIssueError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Recovery failed\", \"error\");\n }\n };\n const selectAutomationArtefact = async (id) => {\n if (!client) return;\n try {\n const artefact = state.automationArtefacts.find((candidate) => candidate.id === id) ?? await client.getAutomationArtefact(id);\n setState(\"selectedAutomationArtefactId\", artefact.id);\n setState(\"automationArtefactDraftTitle\", artefact.structured.title);\n setState(\"automationArtefactDraftSummary\", artefact.structured.summary ?? \"\");\n setState(\"automationArtefactDraftMarkdown\", artefact.structured.markdown);\n setState(\"reviewNote\", \"\");\n if (artefact.meetingId !== state.selectedMeetingId) await loadMeeting(artefact.meetingId);\n } catch (error) {\n setState(\"automationArtefactError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Unable to open artefact\", \"error\");\n }\n };\n const saveAutomationArtefact = async () => {\n if (!client || !state.selectedAutomationArtefactId) return;\n setStatus(\"Saving artefact edits…\", \"busy\");\n try {\n const artefact = await client.updateAutomationArtefact(state.selectedAutomationArtefactId, {\n markdown: state.automationArtefactDraftMarkdown,\n note: state.reviewNote || void 0,\n summary: state.automationArtefactDraftSummary,\n title: state.automationArtefactDraftTitle\n });\n await loadAutomationArtefacts({\n preferredId: artefact.id,\n preferredMeetingId: artefact.meetingId\n });\n await loadAutomationRuns();\n setStatus(\"Artefact updated\", \"ok\");\n } catch (error) {\n setState(\"automationArtefactError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Artefact save failed\", \"error\");\n }\n };\n const resolveAutomationArtefact = async (decision) => {\n if (!client || !state.selectedAutomationArtefactId) return;\n setStatus(decision === \"approve\" ? \"Approving artefact…\" : \"Rejecting artefact…\", \"busy\");\n try {\n const artefact = await client.resolveAutomationArtefact(state.selectedAutomationArtefactId, decision, { note: state.reviewNote || void 0 });\n await loadAutomationArtefacts({\n preferredId: artefact.id,\n preferredMeetingId: artefact.meetingId\n });\n await loadAutomationRuns();\n setStatus(decision === \"approve\" ? \"Artefact approved\" : \"Artefact rejected\", \"ok\");\n } catch (error) {\n setState(\"automationArtefactError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Artefact decision failed\", \"error\");\n }\n };\n const rerunAutomationArtefact = async () => {\n if (!client || !state.selectedAutomationArtefactId) return;\n setStatus(\"Rerunning artefact pipeline…\", \"busy\");\n try {\n const artefact = await client.rerunAutomationArtefact(state.selectedAutomationArtefactId);\n await loadAutomationArtefacts({\n preferredId: artefact.id,\n preferredMeetingId: artefact.meetingId\n });\n await loadAutomationRuns();\n setStatus(\"Artefact rerun complete\", \"ok\");\n } catch (error) {\n setState(\"automationArtefactError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Artefact rerun failed\", \"error\");\n }\n };\n const unlockServer = async () => {\n if (!state.serverPassword.trim()) {\n setStatus(\"Enter the server password\", \"error\");\n return;\n }\n setStatus(\"Unlocking server…\", \"busy\");\n try {\n await requestJson(\"/auth/unlock\", {\n body: JSON.stringify({ password: state.serverPassword }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n setState(\"serverPassword\", \"\");\n setState(\"serverLocked\", false);\n await connectAndRefresh(true);\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Unlock failed\", \"error\");\n }\n };\n const lockServer = async () => {\n try {\n await requestJson(\"/auth/lock\", { method: \"POST\" });\n } catch {}\n await detachClient();\n setState({\n appState: null,\n automationArtefactDraftMarkdown: \"\",\n automationArtefactDraftSummary: \"\",\n automationArtefactDraftTitle: \"\",\n automationArtefactError: \"\",\n automationArtefacts: [],\n automationRules: [],\n automationRuns: [],\n detailError: \"\",\n folderError: \"\",\n folders: [],\n harnessDirty: false,\n harnessError: \"\",\n harnessExplainEventKind: null,\n harnessExplanations: [],\n harnessTestResult: null,\n harnesses: [],\n listError: \"\",\n meetings: [],\n processingIssueError: \"\",\n processingIssues: [],\n reviewNote: \"\",\n selectedAutomationArtefactId: null,\n selectedFolderId: null,\n selectedHarnessId: null,\n selectedMeeting: null,\n selectedMeetingBundle: null,\n selectedMeetingId: null,\n controlPanelTab: \"overview\",\n serverLocked: true,\n serverPassword: \"\"\n });\n setStatus(\"Server locked\", \"error\");\n };\n createEffect(() => {\n const nextPath = buildBrowserUrlPath(window.location.href, {\n selectedFolderId: state.selectedFolderId,\n selectedMeetingId: state.selectedMeetingId,\n workspaceTab: state.workspaceTab\n });\n if (nextPath !== `${window.location.pathname}${window.location.search}${window.location.hash}`) history.replaceState(null, \"\", nextPath);\n });\n createEffect(() => {\n if (!state.appState?.automation.loaded || !client) {\n automationPanelsHydrated = false;\n return;\n }\n if (automationPanelsHydrated) return;\n automationPanelsHydrated = true;\n loadAutomationRuns();\n loadAutomationArtefacts();\n loadProcessingIssues();\n });\n onMount(() => {\n const onKeyDown = (event) => {\n const target = event.target;\n if (target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement) return;\n const nextTab = nextWorkspaceTab(state.workspaceTab, event.key);\n if (nextTab) setState(\"workspaceTab\", nextTab);\n };\n document.addEventListener(\"keydown\", onKeyDown);\n onCleanup(() => {\n document.removeEventListener(\"keydown\", onKeyDown);\n });\n if (!state.serverLocked) connectAndRefresh();\n });\n onCleanup(() => {\n detachClient();\n });\n const onboardingState = () => deriveOnboardingState({\n appState: state.appState,\n automationRuleCount: state.automationRules.length,\n harnesses: state.harnesses,\n meetingsLoadedCount: state.meetings.length\n });\n const showOnboarding = () => !state.serverLocked && !onboardingState().complete;\n const controlTabs = [\n {\n id: \"overview\",\n label: \"Overview\"\n },\n {\n id: \"auth\",\n label: \"Auth\"\n },\n {\n id: \"pipelines\",\n label: \"Pipelines\"\n },\n {\n id: \"review\",\n label: \"Review\"\n }\n ];\n return createComponent(Show, {\n get when() {\n return memo(() => !!!state.serverLocked)() && !showOnboarding();\n },\n get fallback() {\n return (() => {\n var _el$15 = _tmpl$3(), _el$16 = _el$15.firstChild;\n insert(_el$16, createComponent(AppStatePanel, {\n get appState() {\n return state.appState;\n },\n get statusLabel() {\n return state.statusLabel;\n },\n get statusTone() {\n return state.statusTone;\n }\n }), null);\n insert(_el$16, createComponent(SecurityPanel, {\n onLock: () => {\n lockServer();\n },\n onPasswordChange: (value) => {\n setState(\"serverPassword\", value);\n },\n onUnlock: () => {\n unlockServer();\n },\n get password() {\n return state.serverPassword;\n },\n get visible() {\n return state.serverLocked;\n }\n }), null);\n insert(_el$16, (() => {\n var _c$ = memo(() => !!!state.serverLocked);\n return () => _c$() ? createComponent(OnboardingPanel, {\n get apiKeyDraft() {\n return state.apiKeyDraft;\n },\n get auth() {\n return state.appState?.auth;\n },\n get folders() {\n return state.folders;\n },\n get meetingsLoadedCount() {\n return state.meetings.length;\n },\n onApiKeyDraftChange: (value) => {\n setState(\"apiKeyDraft\", value);\n },\n onCreateStarterPipeline: () => {\n createStarterPipeline();\n },\n onImportDesktopSession: () => {\n importDesktopSession();\n },\n onRunSync: () => {\n connectAndRefresh(true);\n },\n onSaveApiKey: () => {\n saveApiKey();\n },\n onSelectProvider: (provider) => {\n setState(\"preferredProvider\", provider);\n },\n get preferredProvider() {\n return state.preferredProvider;\n },\n get state() {\n return onboardingState();\n }\n }) : null;\n })(), null);\n insert(_el$16, (() => {\n var _c$2 = memo(() => !!(!state.serverLocked && state.detailError));\n return () => _c$2() ? (() => {\n var _el$17 = _tmpl$4(), _el$19 = _el$17.firstChild.firstChild;\n insert(_el$19, () => state.detailError);\n return _el$17;\n })() : null;\n })(), null);\n return _el$15;\n })();\n },\n get children() {\n var _el$ = _tmpl$2(), _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, _el$9 = _el$8.nextSibling, _el$0 = _el$4.nextSibling, _el$1 = _el$0.firstChild, _el$10 = _el$1.nextSibling;\n insert(_el$2, createComponent(ToolbarFilters, {\n onQuickOpen: () => {\n quickOpenMeeting();\n },\n onQuickOpenInput: (value) => {\n setState(\"quickOpen\", value);\n },\n onSearchInput: (value) => {\n setState(\"search\", value.trim());\n loadMeetings();\n },\n onSortChange: (value) => {\n setState(\"sort\", value);\n loadMeetings();\n },\n onUpdatedFromChange: (value) => {\n setState(\"updatedFrom\", value);\n loadMeetings();\n },\n onUpdatedToChange: (value) => {\n setState(\"updatedTo\", value);\n loadMeetings();\n },\n get quickOpen() {\n return state.quickOpen;\n },\n get search() {\n return state.search;\n },\n get sort() {\n return state.sort;\n },\n get updatedFrom() {\n return state.updatedFrom;\n },\n get updatedTo() {\n return state.updatedTo;\n }\n }), null);\n insert(_el$2, createComponent(SavedFiltersPanel, {\n get folders() {\n return state.folders;\n },\n onApply: (preset) => {\n applySavedFilterPreset(preset.id);\n },\n onRemove: removeSavedFilterPreset,\n onSaveCurrent: saveCurrentFilter,\n get savedFilters() {\n return state.savedFilters;\n },\n get search() {\n return state.search;\n },\n get selectedFolderId() {\n return state.selectedFolderId;\n },\n get sort() {\n return state.sort;\n },\n get updatedFrom() {\n return state.updatedFrom;\n },\n get updatedTo() {\n return state.updatedTo;\n }\n }), null);\n insert(_el$2, createComponent(RecentMeetingsPanel, {\n onOpen: (meeting) => {\n openRecentMeeting(meeting.id, meeting.folderId);\n },\n get recentMeetings() {\n return state.recentMeetings;\n }\n }), null);\n insert(_el$2, createComponent(FolderList, {\n get error() {\n return state.folderError;\n },\n get folders() {\n return state.folders;\n },\n onSelect: (folderId) => {\n setState(\"selectedFolderId\", folderId);\n setState(\"selectedMeetingId\", null);\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n loadMeetings();\n },\n get selectedFolderId() {\n return state.selectedFolderId;\n }\n }), null);\n insert(_el$2, createComponent(MeetingList, {\n get error() {\n return state.listError;\n },\n get emptyHint() {\n return meetingEmptyHint();\n },\n get folders() {\n return state.folders;\n },\n get meetings() {\n return state.meetings;\n },\n onSelect: (meetingId) => {\n loadMeeting(meetingId);\n },\n get search() {\n return state.search;\n },\n get selectedFolderId() {\n return state.selectedFolderId;\n },\n get selectedMeetingId() {\n return state.selectedMeetingId;\n },\n get updatedFrom() {\n return state.updatedFrom;\n },\n get updatedTo() {\n return state.updatedTo;\n }\n }), null);\n insert(_el$3, createComponent(AppStatePanel, {\n get appState() {\n return state.appState;\n },\n get statusLabel() {\n return state.statusLabel;\n },\n get statusTone() {\n return state.statusTone;\n }\n }), _el$4);\n _el$6.$$click = () => {\n connectAndRefresh(true);\n };\n _el$7.$$click = () => {\n clearFilters();\n };\n _el$8.$$click = () => {\n exportNotes();\n };\n _el$9.$$click = () => {\n exportTranscripts();\n };\n insert(_el$3, createComponent(Workspace, {\n get bundle() {\n return state.selectedMeetingBundle;\n },\n get detailError() {\n return state.detailError;\n },\n onSelectTab: (tab) => {\n setState(\"workspaceTab\", tab);\n },\n get selectedMeeting() {\n return state.selectedMeeting;\n },\n get tab() {\n return state.workspaceTab;\n }\n }), _el$0);\n insert(_el$1, () => controlTabs.map((tab) => (() => {\n var _el$20 = _tmpl$5();\n _el$20.$$click = () => {\n setState(\"controlPanelTab\", tab.id);\n };\n insert(_el$20, () => tab.label);\n createRenderEffect(() => setAttribute(_el$20, \"data-selected\", state.controlPanelTab === tab.id));\n return _el$20;\n })()));\n insert(_el$10, createComponent(Show, {\n get when() {\n return state.controlPanelTab === \"overview\";\n },\n get children() {\n var _el$11 = _tmpl$();\n insert(_el$11, createComponent(ExportJobsPanel, {\n get jobs() {\n return state.appState?.exports.jobs || [];\n },\n onRerun: (jobId) => {\n rerunJob(jobId);\n }\n }), null);\n insert(_el$11, createComponent(ProcessingIssuesPanel, {\n get issues() {\n return state.processingIssues;\n },\n onOpenMeeting: (meetingId) => {\n loadMeeting(meetingId);\n },\n onRecover: (issueId) => {\n recoverProcessingIssue(issueId);\n }\n }), null);\n return _el$11;\n }\n }), null);\n insert(_el$10, createComponent(Show, {\n get when() {\n return state.controlPanelTab === \"auth\";\n },\n get children() {\n var _el$12 = _tmpl$();\n insert(_el$12, createComponent(SecurityPanel, {\n onLock: () => {\n lockServer();\n },\n onPasswordChange: (value) => {\n setState(\"serverPassword\", value);\n },\n onUnlock: () => {\n unlockServer();\n },\n get password() {\n return state.serverPassword;\n },\n get visible() {\n return state.serverLocked;\n }\n }), null);\n insert(_el$12, createComponent(AuthPanel, {\n get apiKeyDraft() {\n return state.apiKeyDraft;\n },\n get auth() {\n return state.appState?.auth;\n },\n onApiKeyDraftChange: (value) => {\n setState(\"apiKeyDraft\", value);\n },\n onImportDesktopSession: () => {\n importDesktopSession();\n },\n onLogout: () => {\n logout();\n },\n onRefresh: () => {\n refreshAuth();\n },\n onSaveApiKey: () => {\n saveApiKey();\n },\n onSwitchMode: (mode) => {\n switchAuthMode(mode);\n }\n }), null);\n return _el$12;\n }\n }), null);\n insert(_el$10, createComponent(Show, {\n get when() {\n return state.controlPanelTab === \"pipelines\";\n },\n get children() {\n var _el$13 = _tmpl$();\n insert(_el$13, createComponent(HarnessEditorPanel, {\n get dirty() {\n return state.harnessDirty;\n },\n get error() {\n return state.harnessError;\n },\n get explanations() {\n return state.harnessExplanations;\n },\n get explanationEventKind() {\n return state.harnessExplainEventKind;\n },\n get harnesses() {\n return state.harnesses;\n },\n onChange: updateHarness,\n onDuplicate: duplicateHarness,\n onNew: createHarness,\n onReload: () => {\n reloadHarnesses();\n },\n onRemove: removeHarness,\n onSave: () => {\n saveHarnesses();\n },\n onSelect: (id) => {\n setState(\"selectedHarnessId\", id);\n setState(\"harnessTestResult\", null);\n },\n onTest: () => {\n testHarness();\n },\n onTestKindChange: (kind) => {\n setState(\"harnessTestKind\", kind);\n },\n get selectedHarness() {\n return selectedHarness();\n },\n get selectedHarnessId() {\n return state.selectedHarnessId;\n },\n get selectedMeeting() {\n return state.selectedMeeting;\n },\n get testKind() {\n return state.harnessTestKind;\n },\n get testResult() {\n return state.harnessTestResult;\n }\n }), null);\n insert(_el$13, createComponent(AutomationRunsPanel, {\n onApprove: (runId) => {\n resolveAutomationRun(runId, \"approve\");\n },\n onReject: (runId) => {\n resolveAutomationRun(runId, \"reject\");\n },\n get runs() {\n return state.automationRuns;\n }\n }), null);\n return _el$13;\n }\n }), null);\n insert(_el$10, createComponent(Show, {\n get when() {\n return state.controlPanelTab === \"review\";\n },\n get children() {\n var _el$14 = _tmpl$();\n insert(_el$14, createComponent(AutomationArtefactsPanel, {\n get artefacts() {\n return state.automationArtefacts;\n },\n onSelect: (artefactId) => {\n setState(\"controlPanelTab\", \"review\");\n selectAutomationArtefact(artefactId);\n },\n get selectedArtefactId() {\n return state.selectedAutomationArtefactId;\n }\n }), null);\n insert(_el$14, createComponent(ArtefactReviewPanel, {\n get artefact() {\n return selectedAutomationArtefact();\n },\n get bundle() {\n return state.selectedMeetingBundle;\n },\n get draftMarkdown() {\n return state.automationArtefactDraftMarkdown;\n },\n get draftSummary() {\n return state.automationArtefactDraftSummary;\n },\n get draftTitle() {\n return state.automationArtefactDraftTitle;\n },\n get error() {\n return state.automationArtefactError;\n },\n onApprove: () => {\n resolveAutomationArtefact(\"approve\");\n },\n onDraftMarkdownChange: (value) => {\n setState(\"automationArtefactDraftMarkdown\", value);\n },\n onDraftSummaryChange: (value) => {\n setState(\"automationArtefactDraftSummary\", value);\n },\n onDraftTitleChange: (value) => {\n setState(\"automationArtefactDraftTitle\", value);\n },\n onReject: () => {\n resolveAutomationArtefact(\"reject\");\n },\n onRerun: () => {\n rerunAutomationArtefact();\n },\n onReviewNoteChange: (value) => {\n setState(\"reviewNote\", value);\n },\n onSave: () => {\n saveAutomationArtefact();\n },\n get reviewNote() {\n return state.reviewNote;\n }\n }), null);\n return _el$14;\n }\n }), null);\n return _el$;\n }\n });\n}\ndelegateEvents([\"click\"]);\n//#endregion\n//#region src/web-app/main.tsx\n/** @jsxImportSource solid-js */\nvar root = document.getElementById(\"granola-web-root\");\nif (!root) throw new Error(\"Granola web root element not found\");\nrender(() => createComponent(App, {}), root);\n//#endregion\n";
|
|
9924
|
+
const granolaWebClientJs = "//#region node_modules/solid-js/dist/solid.js\nvar sharedConfig = {\n context: void 0,\n registry: void 0,\n effects: void 0,\n done: false,\n getContextId() {\n return getContextId(this.context.count);\n },\n getNextContextId() {\n return getContextId(this.context.count++);\n }\n};\nfunction getContextId(count) {\n const num = String(count), len = num.length - 1;\n return sharedConfig.context.id + (len ? String.fromCharCode(96 + len) : \"\") + num;\n}\nfunction setHydrateContext(context) {\n sharedConfig.context = context;\n}\nfunction nextHydrateContext() {\n return {\n ...sharedConfig.context,\n id: sharedConfig.getNextContextId(),\n count: 0\n };\n}\nvar equalFn = (a, b) => a === b;\nvar $PROXY = Symbol(\"solid-proxy\");\nvar $TRACK = Symbol(\"solid-track\");\nvar signalOptions = { equals: equalFn };\nvar ERROR = null;\nvar runEffects = runQueue;\nvar STALE = 1;\nvar PENDING = 2;\nvar UNOWNED = {\n owned: null,\n cleanups: null,\n context: null,\n owner: null\n};\nvar Owner = null;\nvar Transition = null;\nvar Scheduler = null;\nvar ExternalSourceConfig = null;\nvar Listener = null;\nvar Updates = null;\nvar Effects = null;\nvar ExecCount = 0;\nfunction createRoot(fn, detachedOwner) {\n const listener = Listener, owner = Owner, unowned = fn.length === 0, current = detachedOwner === void 0 ? owner : detachedOwner, root = unowned ? UNOWNED : {\n owned: null,\n cleanups: null,\n context: current ? current.context : null,\n owner: current\n }, updateFn = unowned ? fn : () => fn(() => untrack(() => cleanNode(root)));\n Owner = root;\n Listener = null;\n try {\n return runUpdates(updateFn, true);\n } finally {\n Listener = listener;\n Owner = owner;\n }\n}\nfunction createSignal(value, options) {\n options = options ? Object.assign({}, signalOptions, options) : signalOptions;\n const s = {\n value,\n observers: null,\n observerSlots: null,\n comparator: options.equals || void 0\n };\n const setter = (value) => {\n if (typeof value === \"function\") if (Transition && Transition.running && Transition.sources.has(s)) value = value(s.tValue);\n else value = value(s.value);\n return writeSignal(s, value);\n };\n return [readSignal.bind(s), setter];\n}\nfunction createRenderEffect(fn, value, options) {\n const c = createComputation(fn, value, false, STALE);\n if (Scheduler && Transition && Transition.running) Updates.push(c);\n else updateComputation(c);\n}\nfunction createEffect(fn, value, options) {\n runEffects = runUserEffects;\n const c = createComputation(fn, value, false, STALE), s = SuspenseContext && useContext(SuspenseContext);\n if (s) c.suspense = s;\n if (!options || !options.render) c.user = true;\n Effects ? Effects.push(c) : updateComputation(c);\n}\nfunction createMemo(fn, value, options) {\n options = options ? Object.assign({}, signalOptions, options) : signalOptions;\n const c = createComputation(fn, value, true, 0);\n c.observers = null;\n c.observerSlots = null;\n c.comparator = options.equals || void 0;\n if (Scheduler && Transition && Transition.running) {\n c.tState = STALE;\n Updates.push(c);\n } else updateComputation(c);\n return readSignal.bind(c);\n}\nfunction batch(fn) {\n return runUpdates(fn, false);\n}\nfunction untrack(fn) {\n if (!ExternalSourceConfig && Listener === null) return fn();\n const listener = Listener;\n Listener = null;\n try {\n if (ExternalSourceConfig) return ExternalSourceConfig.untrack(fn);\n return fn();\n } finally {\n Listener = listener;\n }\n}\nfunction onMount(fn) {\n createEffect(() => untrack(fn));\n}\nfunction onCleanup(fn) {\n if (Owner === null);\n else if (Owner.cleanups === null) Owner.cleanups = [fn];\n else Owner.cleanups.push(fn);\n return fn;\n}\nfunction getListener() {\n return Listener;\n}\nfunction startTransition(fn) {\n if (Transition && Transition.running) {\n fn();\n return Transition.done;\n }\n const l = Listener;\n const o = Owner;\n return Promise.resolve().then(() => {\n Listener = l;\n Owner = o;\n let t;\n if (Scheduler || SuspenseContext) {\n t = Transition || (Transition = {\n sources: /* @__PURE__ */ new Set(),\n effects: [],\n promises: /* @__PURE__ */ new Set(),\n disposed: /* @__PURE__ */ new Set(),\n queue: /* @__PURE__ */ new Set(),\n running: true\n });\n t.done || (t.done = new Promise((res) => t.resolve = res));\n t.running = true;\n }\n runUpdates(fn, false);\n Listener = Owner = null;\n return t ? t.done : void 0;\n });\n}\nvar [transPending, setTransPending] = /* @__PURE__ */ createSignal(false);\nfunction useContext(context) {\n let value;\n return Owner && Owner.context && (value = Owner.context[context.id]) !== void 0 ? value : context.defaultValue;\n}\nvar SuspenseContext;\nfunction readSignal() {\n const runningTransition = Transition && Transition.running;\n if (this.sources && (runningTransition ? this.tState : this.state)) if ((runningTransition ? this.tState : this.state) === STALE) updateComputation(this);\n else {\n const updates = Updates;\n Updates = null;\n runUpdates(() => lookUpstream(this), false);\n Updates = updates;\n }\n if (Listener) {\n const sSlot = this.observers ? this.observers.length : 0;\n if (!Listener.sources) {\n Listener.sources = [this];\n Listener.sourceSlots = [sSlot];\n } else {\n Listener.sources.push(this);\n Listener.sourceSlots.push(sSlot);\n }\n if (!this.observers) {\n this.observers = [Listener];\n this.observerSlots = [Listener.sources.length - 1];\n } else {\n this.observers.push(Listener);\n this.observerSlots.push(Listener.sources.length - 1);\n }\n }\n if (runningTransition && Transition.sources.has(this)) return this.tValue;\n return this.value;\n}\nfunction writeSignal(node, value, isComp) {\n let current = Transition && Transition.running && Transition.sources.has(node) ? node.tValue : node.value;\n if (!node.comparator || !node.comparator(current, value)) {\n if (Transition) {\n const TransitionRunning = Transition.running;\n if (TransitionRunning || !isComp && Transition.sources.has(node)) {\n Transition.sources.add(node);\n node.tValue = value;\n }\n if (!TransitionRunning) node.value = value;\n } else node.value = value;\n if (node.observers && node.observers.length) runUpdates(() => {\n for (let i = 0; i < node.observers.length; i += 1) {\n const o = node.observers[i];\n const TransitionRunning = Transition && Transition.running;\n if (TransitionRunning && Transition.disposed.has(o)) continue;\n if (TransitionRunning ? !o.tState : !o.state) {\n if (o.pure) Updates.push(o);\n else Effects.push(o);\n if (o.observers) markDownstream(o);\n }\n if (!TransitionRunning) o.state = STALE;\n else o.tState = STALE;\n }\n if (Updates.length > 1e6) {\n Updates = [];\n throw new Error();\n }\n }, false);\n }\n return value;\n}\nfunction updateComputation(node) {\n if (!node.fn) return;\n cleanNode(node);\n const time = ExecCount;\n runComputation(node, Transition && Transition.running && Transition.sources.has(node) ? node.tValue : node.value, time);\n if (Transition && !Transition.running && Transition.sources.has(node)) queueMicrotask(() => {\n runUpdates(() => {\n Transition && (Transition.running = true);\n Listener = Owner = node;\n runComputation(node, node.tValue, time);\n Listener = Owner = null;\n }, false);\n });\n}\nfunction runComputation(node, value, time) {\n let nextValue;\n const owner = Owner, listener = Listener;\n Listener = Owner = node;\n try {\n nextValue = node.fn(value);\n } catch (err) {\n if (node.pure) if (Transition && Transition.running) {\n node.tState = STALE;\n node.tOwned && node.tOwned.forEach(cleanNode);\n node.tOwned = void 0;\n } else {\n node.state = STALE;\n node.owned && node.owned.forEach(cleanNode);\n node.owned = null;\n }\n node.updatedAt = time + 1;\n return handleError(err);\n } finally {\n Listener = listener;\n Owner = owner;\n }\n if (!node.updatedAt || node.updatedAt <= time) {\n if (node.updatedAt != null && \"observers\" in node) writeSignal(node, nextValue, true);\n else if (Transition && Transition.running && node.pure) {\n if (!Transition.sources.has(node)) node.value = nextValue;\n Transition.sources.add(node);\n node.tValue = nextValue;\n } else node.value = nextValue;\n node.updatedAt = time;\n }\n}\nfunction createComputation(fn, init, pure, state = STALE, options) {\n const c = {\n fn,\n state,\n updatedAt: null,\n owned: null,\n sources: null,\n sourceSlots: null,\n cleanups: null,\n value: init,\n owner: Owner,\n context: Owner ? Owner.context : null,\n pure\n };\n if (Transition && Transition.running) {\n c.state = 0;\n c.tState = state;\n }\n if (Owner === null);\n else if (Owner !== UNOWNED) if (Transition && Transition.running && Owner.pure) if (!Owner.tOwned) Owner.tOwned = [c];\n else Owner.tOwned.push(c);\n else if (!Owner.owned) Owner.owned = [c];\n else Owner.owned.push(c);\n if (ExternalSourceConfig && c.fn) {\n const sourceFn = c.fn;\n const [track, trigger] = createSignal(void 0, { equals: false });\n const ordinary = ExternalSourceConfig.factory(sourceFn, trigger);\n onCleanup(() => ordinary.dispose());\n let inTransition;\n const triggerInTransition = () => startTransition(trigger).then(() => {\n if (inTransition) {\n inTransition.dispose();\n inTransition = void 0;\n }\n });\n c.fn = (x) => {\n track();\n if (Transition && Transition.running) {\n if (!inTransition) inTransition = ExternalSourceConfig.factory(sourceFn, triggerInTransition);\n return inTransition.track(x);\n }\n return ordinary.track(x);\n };\n }\n return c;\n}\nfunction runTop(node) {\n const runningTransition = Transition && Transition.running;\n if ((runningTransition ? node.tState : node.state) === 0) return;\n if ((runningTransition ? node.tState : node.state) === PENDING) return lookUpstream(node);\n if (node.suspense && untrack(node.suspense.inFallback)) return node.suspense.effects.push(node);\n const ancestors = [node];\n while ((node = node.owner) && (!node.updatedAt || node.updatedAt < ExecCount)) {\n if (runningTransition && Transition.disposed.has(node)) return;\n if (runningTransition ? node.tState : node.state) ancestors.push(node);\n }\n for (let i = ancestors.length - 1; i >= 0; i--) {\n node = ancestors[i];\n if (runningTransition) {\n let top = node, prev = ancestors[i + 1];\n while ((top = top.owner) && top !== prev) if (Transition.disposed.has(top)) return;\n }\n if ((runningTransition ? node.tState : node.state) === STALE) updateComputation(node);\n else if ((runningTransition ? node.tState : node.state) === PENDING) {\n const updates = Updates;\n Updates = null;\n runUpdates(() => lookUpstream(node, ancestors[0]), false);\n Updates = updates;\n }\n }\n}\nfunction runUpdates(fn, init) {\n if (Updates) return fn();\n let wait = false;\n if (!init) Updates = [];\n if (Effects) wait = true;\n else Effects = [];\n ExecCount++;\n try {\n const res = fn();\n completeUpdates(wait);\n return res;\n } catch (err) {\n if (!wait) Effects = null;\n Updates = null;\n handleError(err);\n }\n}\nfunction completeUpdates(wait) {\n if (Updates) {\n if (Scheduler && Transition && Transition.running) scheduleQueue(Updates);\n else runQueue(Updates);\n Updates = null;\n }\n if (wait) return;\n let res;\n if (Transition) {\n if (!Transition.promises.size && !Transition.queue.size) {\n const sources = Transition.sources;\n const disposed = Transition.disposed;\n Effects.push.apply(Effects, Transition.effects);\n res = Transition.resolve;\n for (const e of Effects) {\n \"tState\" in e && (e.state = e.tState);\n delete e.tState;\n }\n Transition = null;\n runUpdates(() => {\n for (const d of disposed) cleanNode(d);\n for (const v of sources) {\n v.value = v.tValue;\n if (v.owned) for (let i = 0, len = v.owned.length; i < len; i++) cleanNode(v.owned[i]);\n if (v.tOwned) v.owned = v.tOwned;\n delete v.tValue;\n delete v.tOwned;\n v.tState = 0;\n }\n setTransPending(false);\n }, false);\n } else if (Transition.running) {\n Transition.running = false;\n Transition.effects.push.apply(Transition.effects, Effects);\n Effects = null;\n setTransPending(true);\n return;\n }\n }\n const e = Effects;\n Effects = null;\n if (e.length) runUpdates(() => runEffects(e), false);\n if (res) res();\n}\nfunction runQueue(queue) {\n for (let i = 0; i < queue.length; i++) runTop(queue[i]);\n}\nfunction scheduleQueue(queue) {\n for (let i = 0; i < queue.length; i++) {\n const item = queue[i];\n const tasks = Transition.queue;\n if (!tasks.has(item)) {\n tasks.add(item);\n Scheduler(() => {\n tasks.delete(item);\n runUpdates(() => {\n Transition.running = true;\n runTop(item);\n }, false);\n Transition && (Transition.running = false);\n });\n }\n }\n}\nfunction runUserEffects(queue) {\n let i, userLength = 0;\n for (i = 0; i < queue.length; i++) {\n const e = queue[i];\n if (!e.user) runTop(e);\n else queue[userLength++] = e;\n }\n if (sharedConfig.context) {\n if (sharedConfig.count) {\n sharedConfig.effects || (sharedConfig.effects = []);\n sharedConfig.effects.push(...queue.slice(0, userLength));\n return;\n }\n setHydrateContext();\n }\n if (sharedConfig.effects && (sharedConfig.done || !sharedConfig.count)) {\n queue = [...sharedConfig.effects, ...queue];\n userLength += sharedConfig.effects.length;\n delete sharedConfig.effects;\n }\n for (i = 0; i < userLength; i++) runTop(queue[i]);\n}\nfunction lookUpstream(node, ignore) {\n const runningTransition = Transition && Transition.running;\n if (runningTransition) node.tState = 0;\n else node.state = 0;\n for (let i = 0; i < node.sources.length; i += 1) {\n const source = node.sources[i];\n if (source.sources) {\n const state = runningTransition ? source.tState : source.state;\n if (state === STALE) {\n if (source !== ignore && (!source.updatedAt || source.updatedAt < ExecCount)) runTop(source);\n } else if (state === PENDING) lookUpstream(source, ignore);\n }\n }\n}\nfunction markDownstream(node) {\n const runningTransition = Transition && Transition.running;\n for (let i = 0; i < node.observers.length; i += 1) {\n const o = node.observers[i];\n if (runningTransition ? !o.tState : !o.state) {\n if (runningTransition) o.tState = PENDING;\n else o.state = PENDING;\n if (o.pure) Updates.push(o);\n else Effects.push(o);\n o.observers && markDownstream(o);\n }\n }\n}\nfunction cleanNode(node) {\n let i;\n if (node.sources) while (node.sources.length) {\n const source = node.sources.pop(), index = node.sourceSlots.pop(), obs = source.observers;\n if (obs && obs.length) {\n const n = obs.pop(), s = source.observerSlots.pop();\n if (index < obs.length) {\n n.sourceSlots[s] = index;\n obs[index] = n;\n source.observerSlots[index] = s;\n }\n }\n }\n if (node.tOwned) {\n for (i = node.tOwned.length - 1; i >= 0; i--) cleanNode(node.tOwned[i]);\n delete node.tOwned;\n }\n if (Transition && Transition.running && node.pure) reset(node, true);\n else if (node.owned) {\n for (i = node.owned.length - 1; i >= 0; i--) cleanNode(node.owned[i]);\n node.owned = null;\n }\n if (node.cleanups) {\n for (i = node.cleanups.length - 1; i >= 0; i--) node.cleanups[i]();\n node.cleanups = null;\n }\n if (Transition && Transition.running) node.tState = 0;\n else node.state = 0;\n}\nfunction reset(node, top) {\n if (!top) {\n node.tState = 0;\n Transition.disposed.add(node);\n }\n if (node.owned) for (let i = 0; i < node.owned.length; i++) reset(node.owned[i]);\n}\nfunction castError(err) {\n if (err instanceof Error) return err;\n return new Error(typeof err === \"string\" ? err : \"Unknown error\", { cause: err });\n}\nfunction runErrors(err, fns, owner) {\n try {\n for (const f of fns) f(err);\n } catch (e) {\n handleError(e, owner && owner.owner || null);\n }\n}\nfunction handleError(err, owner = Owner) {\n const fns = ERROR && owner && owner.context && owner.context[ERROR];\n const error = castError(err);\n if (!fns) throw error;\n if (Effects) Effects.push({\n fn() {\n runErrors(error, fns, owner);\n },\n state: STALE\n });\n else runErrors(error, fns, owner);\n}\nvar FALLBACK = Symbol(\"fallback\");\nfunction dispose(d) {\n for (let i = 0; i < d.length; i++) d[i]();\n}\nfunction mapArray(list, mapFn, options = {}) {\n let items = [], mapped = [], disposers = [], len = 0, indexes = mapFn.length > 1 ? [] : null;\n onCleanup(() => dispose(disposers));\n return () => {\n let newItems = list() || [], newLen = newItems.length, i, j;\n newItems[$TRACK];\n return untrack(() => {\n let newIndices, newIndicesNext, temp, tempdisposers, tempIndexes, start, end, newEnd, item;\n if (newLen === 0) {\n if (len !== 0) {\n dispose(disposers);\n disposers = [];\n items = [];\n mapped = [];\n len = 0;\n indexes && (indexes = []);\n }\n if (options.fallback) {\n items = [FALLBACK];\n mapped[0] = createRoot((disposer) => {\n disposers[0] = disposer;\n return options.fallback();\n });\n len = 1;\n }\n } else if (len === 0) {\n mapped = new Array(newLen);\n for (j = 0; j < newLen; j++) {\n items[j] = newItems[j];\n mapped[j] = createRoot(mapper);\n }\n len = newLen;\n } else {\n temp = new Array(newLen);\n tempdisposers = new Array(newLen);\n indexes && (tempIndexes = new Array(newLen));\n for (start = 0, end = Math.min(len, newLen); start < end && items[start] === newItems[start]; start++);\n for (end = len - 1, newEnd = newLen - 1; end >= start && newEnd >= start && items[end] === newItems[newEnd]; end--, newEnd--) {\n temp[newEnd] = mapped[end];\n tempdisposers[newEnd] = disposers[end];\n indexes && (tempIndexes[newEnd] = indexes[end]);\n }\n newIndices = /* @__PURE__ */ new Map();\n newIndicesNext = new Array(newEnd + 1);\n for (j = newEnd; j >= start; j--) {\n item = newItems[j];\n i = newIndices.get(item);\n newIndicesNext[j] = i === void 0 ? -1 : i;\n newIndices.set(item, j);\n }\n for (i = start; i <= end; i++) {\n item = items[i];\n j = newIndices.get(item);\n if (j !== void 0 && j !== -1) {\n temp[j] = mapped[i];\n tempdisposers[j] = disposers[i];\n indexes && (tempIndexes[j] = indexes[i]);\n j = newIndicesNext[j];\n newIndices.set(item, j);\n } else disposers[i]();\n }\n for (j = start; j < newLen; j++) if (j in temp) {\n mapped[j] = temp[j];\n disposers[j] = tempdisposers[j];\n if (indexes) {\n indexes[j] = tempIndexes[j];\n indexes[j](j);\n }\n } else mapped[j] = createRoot(mapper);\n mapped = mapped.slice(0, len = newLen);\n items = newItems.slice(0);\n }\n return mapped;\n });\n function mapper(disposer) {\n disposers[j] = disposer;\n if (indexes) {\n const [s, set] = createSignal(j);\n indexes[j] = set;\n return mapFn(newItems[j], s);\n }\n return mapFn(newItems[j]);\n }\n };\n}\nvar hydrationEnabled = false;\nfunction createComponent(Comp, props) {\n if (hydrationEnabled) {\n if (sharedConfig.context) {\n const c = sharedConfig.context;\n setHydrateContext(nextHydrateContext());\n const r = untrack(() => Comp(props || {}));\n setHydrateContext(c);\n return r;\n }\n }\n return untrack(() => Comp(props || {}));\n}\nvar narrowedError = (name) => `Stale read from <${name}>.`;\nfunction For(props) {\n const fallback = \"fallback\" in props && { fallback: () => props.fallback };\n return createMemo(mapArray(() => props.each, props.children, fallback || void 0));\n}\nfunction Show(props) {\n const keyed = props.keyed;\n const conditionValue = createMemo(() => props.when, void 0, void 0);\n const condition = keyed ? conditionValue : createMemo(conditionValue, void 0, { equals: (a, b) => !a === !b });\n return createMemo(() => {\n const c = condition();\n if (c) {\n const child = props.children;\n return typeof child === \"function\" && child.length > 0 ? untrack(() => child(keyed ? c : () => {\n if (!untrack(condition)) throw narrowedError(\"Show\");\n return conditionValue();\n })) : child;\n }\n return props.fallback;\n }, void 0, void 0);\n}\n//#endregion\n//#region node_modules/solid-js/web/dist/web.js\nvar memo = (fn) => createMemo(() => fn());\nfunction reconcileArrays(parentNode, a, b) {\n let bLength = b.length, aEnd = a.length, bEnd = bLength, aStart = 0, bStart = 0, after = a[aEnd - 1].nextSibling, map = null;\n while (aStart < aEnd || bStart < bEnd) {\n if (a[aStart] === b[bStart]) {\n aStart++;\n bStart++;\n continue;\n }\n while (a[aEnd - 1] === b[bEnd - 1]) {\n aEnd--;\n bEnd--;\n }\n if (aEnd === aStart) {\n const node = bEnd < bLength ? bStart ? b[bStart - 1].nextSibling : b[bEnd - bStart] : after;\n while (bStart < bEnd) parentNode.insertBefore(b[bStart++], node);\n } else if (bEnd === bStart) while (aStart < aEnd) {\n if (!map || !map.has(a[aStart])) a[aStart].remove();\n aStart++;\n }\n else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {\n const node = a[--aEnd].nextSibling;\n parentNode.insertBefore(b[bStart++], a[aStart++].nextSibling);\n parentNode.insertBefore(b[--bEnd], node);\n a[aEnd] = b[bEnd];\n } else {\n if (!map) {\n map = /* @__PURE__ */ new Map();\n let i = bStart;\n while (i < bEnd) map.set(b[i], i++);\n }\n const index = map.get(a[aStart]);\n if (index != null) if (bStart < index && index < bEnd) {\n let i = aStart, sequence = 1, t;\n while (++i < aEnd && i < bEnd) {\n if ((t = map.get(a[i])) == null || t !== index + sequence) break;\n sequence++;\n }\n if (sequence > index - bStart) {\n const node = a[aStart];\n while (bStart < index) parentNode.insertBefore(b[bStart++], node);\n } else parentNode.replaceChild(b[bStart++], a[aStart++]);\n } else aStart++;\n else a[aStart++].remove();\n }\n }\n}\nvar $$EVENTS = \"_$DX_DELEGATE\";\nfunction render(code, element, init, options = {}) {\n let disposer;\n createRoot((dispose) => {\n disposer = dispose;\n element === document ? code() : insert(element, code(), element.firstChild ? null : void 0, init);\n }, options.owner);\n return () => {\n disposer();\n element.textContent = \"\";\n };\n}\nfunction template(html, isImportNode, isSVG, isMathML) {\n let node;\n const create = () => {\n const t = isMathML ? document.createElementNS(\"http://www.w3.org/1998/Math/MathML\", \"template\") : document.createElement(\"template\");\n t.innerHTML = html;\n return isSVG ? t.content.firstChild.firstChild : isMathML ? t.firstChild : t.content.firstChild;\n };\n const fn = isImportNode ? () => untrack(() => document.importNode(node || (node = create()), true)) : () => (node || (node = create())).cloneNode(true);\n fn.cloneNode = fn;\n return fn;\n}\nfunction delegateEvents(eventNames, document = window.document) {\n const e = document[$$EVENTS] || (document[$$EVENTS] = /* @__PURE__ */ new Set());\n for (let i = 0, l = eventNames.length; i < l; i++) {\n const name = eventNames[i];\n if (!e.has(name)) {\n e.add(name);\n document.addEventListener(name, eventHandler);\n }\n }\n}\nfunction setAttribute(node, name, value) {\n if (isHydrating(node)) return;\n if (value == null) node.removeAttribute(name);\n else node.setAttribute(name, value);\n}\nfunction addEventListener(node, name, handler, delegate) {\n if (delegate) if (Array.isArray(handler)) {\n node[`$$${name}`] = handler[0];\n node[`$$${name}Data`] = handler[1];\n } else node[`$$${name}`] = handler;\n else if (Array.isArray(handler)) {\n const handlerFn = handler[0];\n node.addEventListener(name, handler[0] = (e) => handlerFn.call(node, handler[1], e));\n } else node.addEventListener(name, handler, typeof handler !== \"function\" && handler);\n}\nfunction insert(parent, accessor, marker, initial) {\n if (marker !== void 0 && !initial) initial = [];\n if (typeof accessor !== \"function\") return insertExpression(parent, accessor, initial, marker);\n createRenderEffect((current) => insertExpression(parent, accessor(), current, marker), initial);\n}\nfunction isHydrating(node) {\n return !!sharedConfig.context && !sharedConfig.done && (!node || node.isConnected);\n}\nfunction eventHandler(e) {\n if (sharedConfig.registry && sharedConfig.events) {\n if (sharedConfig.events.find(([el, ev]) => ev === e)) return;\n }\n let node = e.target;\n const key = `$$${e.type}`;\n const oriTarget = e.target;\n const oriCurrentTarget = e.currentTarget;\n const retarget = (value) => Object.defineProperty(e, \"target\", {\n configurable: true,\n value\n });\n const handleNode = () => {\n const handler = node[key];\n if (handler && !node.disabled) {\n const data = node[`${key}Data`];\n data !== void 0 ? handler.call(node, data, e) : handler.call(node, e);\n if (e.cancelBubble) return;\n }\n node.host && typeof node.host !== \"string\" && !node.host._$host && node.contains(e.target) && retarget(node.host);\n return true;\n };\n const walkUpTree = () => {\n while (handleNode() && (node = node._$host || node.parentNode || node.host));\n };\n Object.defineProperty(e, \"currentTarget\", {\n configurable: true,\n get() {\n return node || document;\n }\n });\n if (sharedConfig.registry && !sharedConfig.done) sharedConfig.done = _$HY.done = true;\n if (e.composedPath) {\n const path = e.composedPath();\n retarget(path[0]);\n for (let i = 0; i < path.length - 2; i++) {\n node = path[i];\n if (!handleNode()) break;\n if (node._$host) {\n node = node._$host;\n walkUpTree();\n break;\n }\n if (node.parentNode === oriCurrentTarget) break;\n }\n } else walkUpTree();\n retarget(oriTarget);\n}\nfunction insertExpression(parent, value, current, marker, unwrapArray) {\n const hydrating = isHydrating(parent);\n if (hydrating) {\n !current && (current = [...parent.childNodes]);\n let cleaned = [];\n for (let i = 0; i < current.length; i++) {\n const node = current[i];\n if (node.nodeType === 8 && node.data.slice(0, 2) === \"!$\") node.remove();\n else cleaned.push(node);\n }\n current = cleaned;\n }\n while (typeof current === \"function\") current = current();\n if (value === current) return current;\n const t = typeof value, multi = marker !== void 0;\n parent = multi && current[0] && current[0].parentNode || parent;\n if (t === \"string\" || t === \"number\") {\n if (hydrating) return current;\n if (t === \"number\") {\n value = value.toString();\n if (value === current) return current;\n }\n if (multi) {\n let node = current[0];\n if (node && node.nodeType === 3) node.data !== value && (node.data = value);\n else node = document.createTextNode(value);\n current = cleanChildren(parent, current, marker, node);\n } else if (current !== \"\" && typeof current === \"string\") current = parent.firstChild.data = value;\n else current = parent.textContent = value;\n } else if (value == null || t === \"boolean\") {\n if (hydrating) return current;\n current = cleanChildren(parent, current, marker);\n } else if (t === \"function\") {\n createRenderEffect(() => {\n let v = value();\n while (typeof v === \"function\") v = v();\n current = insertExpression(parent, v, current, marker);\n });\n return () => current;\n } else if (Array.isArray(value)) {\n const array = [];\n const currentArray = current && Array.isArray(current);\n if (normalizeIncomingArray(array, value, current, unwrapArray)) {\n createRenderEffect(() => current = insertExpression(parent, array, current, marker, true));\n return () => current;\n }\n if (hydrating) {\n if (!array.length) return current;\n if (marker === void 0) return current = [...parent.childNodes];\n let node = array[0];\n if (node.parentNode !== parent) return current;\n const nodes = [node];\n while ((node = node.nextSibling) !== marker) nodes.push(node);\n return current = nodes;\n }\n if (array.length === 0) {\n current = cleanChildren(parent, current, marker);\n if (multi) return current;\n } else if (currentArray) if (current.length === 0) appendNodes(parent, array, marker);\n else reconcileArrays(parent, current, array);\n else {\n current && cleanChildren(parent);\n appendNodes(parent, array);\n }\n current = array;\n } else if (value.nodeType) {\n if (hydrating && value.parentNode) return current = multi ? [value] : value;\n if (Array.isArray(current)) {\n if (multi) return current = cleanChildren(parent, current, marker, value);\n cleanChildren(parent, current, null, value);\n } else if (current == null || current === \"\" || !parent.firstChild) parent.appendChild(value);\n else parent.replaceChild(value, parent.firstChild);\n current = value;\n }\n return current;\n}\nfunction normalizeIncomingArray(normalized, array, current, unwrap) {\n let dynamic = false;\n for (let i = 0, len = array.length; i < len; i++) {\n let item = array[i], prev = current && current[normalized.length], t;\n if (item == null || item === true || item === false);\n else if ((t = typeof item) === \"object\" && item.nodeType) normalized.push(item);\n else if (Array.isArray(item)) dynamic = normalizeIncomingArray(normalized, item, prev) || dynamic;\n else if (t === \"function\") if (unwrap) {\n while (typeof item === \"function\") item = item();\n dynamic = normalizeIncomingArray(normalized, Array.isArray(item) ? item : [item], Array.isArray(prev) ? prev : [prev]) || dynamic;\n } else {\n normalized.push(item);\n dynamic = true;\n }\n else {\n const value = String(item);\n if (prev && prev.nodeType === 3 && prev.data === value) normalized.push(prev);\n else normalized.push(document.createTextNode(value));\n }\n }\n return dynamic;\n}\nfunction appendNodes(parent, array, marker = null) {\n for (let i = 0, len = array.length; i < len; i++) parent.insertBefore(array[i], marker);\n}\nfunction cleanChildren(parent, current, marker, replacement) {\n if (marker === void 0) return parent.textContent = \"\";\n const node = replacement || document.createTextNode(\"\");\n if (current.length) {\n let inserted = false;\n for (let i = current.length - 1; i >= 0; i--) {\n const el = current[i];\n if (node !== el) {\n const isParent = el.parentNode === parent;\n if (!inserted && !i) isParent ? parent.replaceChild(node, el) : parent.insertBefore(node, marker);\n else isParent && el.remove();\n } else inserted = true;\n }\n } else parent.insertBefore(node, marker);\n return [node];\n}\n//#endregion\n//#region node_modules/solid-js/store/dist/store.js\nvar $RAW = Symbol(\"store-raw\"), $NODE = Symbol(\"store-node\"), $HAS = Symbol(\"store-has\"), $SELF = Symbol(\"store-self\");\nfunction wrap$1(value) {\n let p = value[$PROXY];\n if (!p) {\n Object.defineProperty(value, $PROXY, { value: p = new Proxy(value, proxyTraps$1) });\n if (!Array.isArray(value)) {\n const keys = Object.keys(value), desc = Object.getOwnPropertyDescriptors(value);\n for (let i = 0, l = keys.length; i < l; i++) {\n const prop = keys[i];\n if (desc[prop].get) Object.defineProperty(value, prop, {\n enumerable: desc[prop].enumerable,\n get: desc[prop].get.bind(p)\n });\n }\n }\n }\n return p;\n}\nfunction isWrappable(obj) {\n let proto;\n return obj != null && typeof obj === \"object\" && (obj[$PROXY] || !(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype || Array.isArray(obj));\n}\nfunction unwrap(item, set = /* @__PURE__ */ new Set()) {\n let result, unwrapped, v, prop;\n if (result = item != null && item[$RAW]) return result;\n if (!isWrappable(item) || set.has(item)) return item;\n if (Array.isArray(item)) {\n if (Object.isFrozen(item)) item = item.slice(0);\n else set.add(item);\n for (let i = 0, l = item.length; i < l; i++) {\n v = item[i];\n if ((unwrapped = unwrap(v, set)) !== v) item[i] = unwrapped;\n }\n } else {\n if (Object.isFrozen(item)) item = Object.assign({}, item);\n else set.add(item);\n const keys = Object.keys(item), desc = Object.getOwnPropertyDescriptors(item);\n for (let i = 0, l = keys.length; i < l; i++) {\n prop = keys[i];\n if (desc[prop].get) continue;\n v = item[prop];\n if ((unwrapped = unwrap(v, set)) !== v) item[prop] = unwrapped;\n }\n }\n return item;\n}\nfunction getNodes(target, symbol) {\n let nodes = target[symbol];\n if (!nodes) Object.defineProperty(target, symbol, { value: nodes = Object.create(null) });\n return nodes;\n}\nfunction getNode(nodes, property, value) {\n if (nodes[property]) return nodes[property];\n const [s, set] = createSignal(value, {\n equals: false,\n internal: true\n });\n s.$ = set;\n return nodes[property] = s;\n}\nfunction proxyDescriptor$1(target, property) {\n const desc = Reflect.getOwnPropertyDescriptor(target, property);\n if (!desc || desc.get || !desc.configurable || property === $PROXY || property === $NODE) return desc;\n delete desc.value;\n delete desc.writable;\n desc.get = () => target[$PROXY][property];\n return desc;\n}\nfunction trackSelf(target) {\n getListener() && getNode(getNodes(target, $NODE), $SELF)();\n}\nfunction ownKeys(target) {\n trackSelf(target);\n return Reflect.ownKeys(target);\n}\nvar proxyTraps$1 = {\n get(target, property, receiver) {\n if (property === $RAW) return target;\n if (property === $PROXY) return receiver;\n if (property === $TRACK) {\n trackSelf(target);\n return receiver;\n }\n const nodes = getNodes(target, $NODE);\n const tracked = nodes[property];\n let value = tracked ? tracked() : target[property];\n if (property === $NODE || property === $HAS || property === \"__proto__\") return value;\n if (!tracked) {\n const desc = Object.getOwnPropertyDescriptor(target, property);\n if (getListener() && (typeof value !== \"function\" || target.hasOwnProperty(property)) && !(desc && desc.get)) value = getNode(nodes, property, value)();\n }\n return isWrappable(value) ? wrap$1(value) : value;\n },\n has(target, property) {\n if (property === $RAW || property === $PROXY || property === $TRACK || property === $NODE || property === $HAS || property === \"__proto__\") return true;\n getListener() && getNode(getNodes(target, $HAS), property)();\n return property in target;\n },\n set() {\n return true;\n },\n deleteProperty() {\n return true;\n },\n ownKeys,\n getOwnPropertyDescriptor: proxyDescriptor$1\n};\nfunction setProperty(state, property, value, deleting = false) {\n if (!deleting && state[property] === value) return;\n const prev = state[property], len = state.length;\n if (value === void 0) {\n delete state[property];\n if (state[$HAS] && state[$HAS][property] && prev !== void 0) state[$HAS][property].$();\n } else {\n state[property] = value;\n if (state[$HAS] && state[$HAS][property] && prev === void 0) state[$HAS][property].$();\n }\n let nodes = getNodes(state, $NODE), node;\n if (node = getNode(nodes, property, prev)) node.$(() => value);\n if (Array.isArray(state) && state.length !== len) {\n for (let i = state.length; i < len; i++) (node = nodes[i]) && node.$();\n (node = getNode(nodes, \"length\", len)) && node.$(state.length);\n }\n (node = nodes[$SELF]) && node.$();\n}\nfunction mergeStoreNode(state, value) {\n const keys = Object.keys(value);\n for (let i = 0; i < keys.length; i += 1) {\n const key = keys[i];\n setProperty(state, key, value[key]);\n }\n}\nfunction updateArray(current, next) {\n if (typeof next === \"function\") next = next(current);\n next = unwrap(next);\n if (Array.isArray(next)) {\n if (current === next) return;\n let i = 0, len = next.length;\n for (; i < len; i++) {\n const value = next[i];\n if (current[i] !== value) setProperty(current, i, value);\n }\n setProperty(current, \"length\", len);\n } else mergeStoreNode(current, next);\n}\nfunction updatePath(current, path, traversed = []) {\n let part, prev = current;\n if (path.length > 1) {\n part = path.shift();\n const partType = typeof part, isArray = Array.isArray(current);\n if (Array.isArray(part)) {\n for (let i = 0; i < part.length; i++) updatePath(current, [part[i]].concat(path), traversed);\n return;\n } else if (isArray && partType === \"function\") {\n for (let i = 0; i < current.length; i++) if (part(current[i], i)) updatePath(current, [i].concat(path), traversed);\n return;\n } else if (isArray && partType === \"object\") {\n const { from = 0, to = current.length - 1, by = 1 } = part;\n for (let i = from; i <= to; i += by) updatePath(current, [i].concat(path), traversed);\n return;\n } else if (path.length > 1) {\n updatePath(current[part], path, [part].concat(traversed));\n return;\n }\n prev = current[part];\n traversed = [part].concat(traversed);\n }\n let value = path[0];\n if (typeof value === \"function\") {\n value = value(prev, traversed);\n if (value === prev) return;\n }\n if (part === void 0 && value == void 0) return;\n value = unwrap(value);\n if (part === void 0 || isWrappable(prev) && isWrappable(value) && !Array.isArray(value)) mergeStoreNode(prev, value);\n else setProperty(current, part, value);\n}\nfunction createStore(...[store, options]) {\n const unwrappedStore = unwrap(store || {});\n const isArray = Array.isArray(unwrappedStore);\n const wrappedStore = wrap$1(unwrappedStore);\n function setStore(...args) {\n batch(() => {\n isArray && args.length === 1 ? updateArray(unwrappedStore, args[0]) : updatePath(unwrappedStore, args);\n });\n }\n return [wrappedStore, setStore];\n}\n//#endregion\n//#region src/review-inbox.ts\nfunction issuePriority(issue) {\n if (issue.severity === \"error\" && issue.recoverable) return 0;\n if (issue.severity === \"error\") return 1;\n return 4;\n}\nfunction runPriority(_run) {\n return 3;\n}\nfunction artefactPriority(_artefact) {\n return 2;\n}\nfunction buildGranolaReviewInbox(options) {\n const items = [];\n for (const issue of options.issues) items.push({\n issue,\n key: `issue:${issue.id}`,\n kind: \"issue\",\n meetingId: issue.meetingId,\n priority: issuePriority(issue),\n status: issue.severity,\n subtitle: issue.kind,\n summary: issue.detail,\n timestamp: issue.detectedAt,\n title: issue.title\n });\n for (const artefact of options.artefacts) {\n if (artefact.status !== \"generated\") continue;\n items.push({\n artefact,\n key: `artefact:${artefact.id}`,\n kind: \"artefact\",\n meetingId: artefact.meetingId,\n priority: artefactPriority(artefact),\n status: artefact.status,\n subtitle: `${artefact.kind} • ${artefact.ruleName}`,\n summary: artefact.structured.summary || artefact.structured.markdown || artefact.structured.title,\n timestamp: artefact.updatedAt,\n title: artefact.structured.title\n });\n }\n for (const run of options.runs) {\n if (run.status !== \"pending\") continue;\n items.push({\n key: `run:${run.id}`,\n kind: \"run\",\n meetingId: run.meetingId,\n priority: runPriority(run),\n run,\n status: run.status,\n subtitle: `${run.actionName} • ${run.ruleName}`,\n summary: run.prompt || run.result || run.error || run.eventKind,\n timestamp: run.startedAt,\n title: run.title\n });\n }\n return items.sort((left, right) => left.priority - right.priority || right.timestamp.localeCompare(left.timestamp) || left.title.localeCompare(right.title) || left.key.localeCompare(right.key));\n}\nfunction summariseGranolaReviewInbox(items) {\n return items.reduce((summary, item) => {\n summary.total += 1;\n if (item.kind === \"artefact\") summary.artefacts += 1;\n else if (item.kind === \"issue\") summary.issues += 1;\n else summary.runs += 1;\n return summary;\n }, {\n artefacts: 0,\n issues: 0,\n runs: 0,\n total: 0\n });\n}\n//#endregion\n//#region src/transport.ts\nvar granolaTransportPaths = {\n authLock: \"/auth/lock\",\n authLogin: \"/auth/login\",\n authLogout: \"/auth/logout\",\n authMode: \"/auth/mode\",\n authRefresh: \"/auth/refresh\",\n authStatus: \"/auth/status\",\n authUnlock: \"/auth/unlock\",\n automationEvaluate: \"/automation/evaluate\",\n automationHarnesses: \"/automation/harnesses\",\n automationHarnessExplain: \"/automation/harnesses/explain\",\n automationMatches: \"/automation/matches\",\n automationArtefacts: \"/automation/artefacts\",\n automationRules: \"/automation/rules\",\n automationRuns: \"/automation/runs\",\n events: \"/events\",\n exportJobs: \"/exports/jobs\",\n exportNotes: \"/exports/notes\",\n exportTranscripts: \"/exports/transcripts\",\n folderResolve: \"/folders/resolve\",\n folders: \"/folders\",\n health: \"/health\",\n meetingResolve: \"/meetings/resolve\",\n meetings: \"/meetings\",\n processingIssues: \"/processing/issues\",\n root: \"/\",\n serverInfo: \"/server/info\",\n syncRun: \"/sync\",\n syncEvents: \"/sync/events\",\n state: \"/state\"\n};\nfunction appendSearchParams(path, params) {\n const url = new URL(path, \"http://localhost\");\n for (const [key, value] of Object.entries(params)) {\n if (value === void 0 || value === false || value === \"\") continue;\n url.searchParams.set(key, String(value));\n }\n return `${url.pathname}${url.search}`;\n}\nfunction granolaMeetingPath(id) {\n return `${granolaTransportPaths.meetings}/${encodeURIComponent(id)}`;\n}\nfunction granolaMeetingResolvePath(query, options = {}) {\n return appendSearchParams(granolaTransportPaths.meetingResolve, {\n includeTranscript: options.includeTranscript ? \"true\" : void 0,\n q: query\n });\n}\nfunction granolaMeetingsPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.meetings, {\n folderId: options.folderId,\n limit: options.limit,\n refresh: options.forceRefresh ? \"true\" : void 0,\n search: options.search,\n sort: options.sort,\n updatedFrom: options.updatedFrom,\n updatedTo: options.updatedTo\n });\n}\nfunction granolaFolderPath(id) {\n return `${granolaTransportPaths.folders}/${encodeURIComponent(id)}`;\n}\nfunction granolaFolderResolvePath(query) {\n return appendSearchParams(granolaTransportPaths.folderResolve, { q: query });\n}\nfunction granolaFoldersPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.folders, {\n limit: options.limit,\n refresh: options.forceRefresh ? \"true\" : void 0,\n search: options.search\n });\n}\nfunction granolaExportJobsPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.exportJobs, { limit: options.limit });\n}\nfunction granolaAutomationRunsPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.automationRuns, {\n limit: options.limit,\n status: options.status\n });\n}\nfunction granolaAutomationHarnessExplainPath(meetingId) {\n return appendSearchParams(granolaTransportPaths.automationHarnessExplain, { meetingId });\n}\nfunction granolaAutomationArtefactsPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.automationArtefacts, {\n kind: options.kind,\n limit: options.limit,\n meetingId: options.meetingId,\n status: options.status\n });\n}\nfunction granolaAutomationRunDecisionPath(id, decision) {\n return `${granolaTransportPaths.automationRuns}/${encodeURIComponent(id)}/${decision}`;\n}\nfunction granolaAutomationArtefactRerunPath(id) {\n return `${granolaTransportPaths.automationArtefacts}/${encodeURIComponent(id)}/rerun`;\n}\nfunction granolaAutomationArtefactPath(id) {\n return `${granolaTransportPaths.automationArtefacts}/${encodeURIComponent(id)}`;\n}\nfunction granolaAutomationArtefactDecisionPath(id, decision) {\n return `${granolaAutomationArtefactPath(id)}/${decision}`;\n}\nfunction granolaAutomationArtefactUpdatePath(id) {\n return `${granolaAutomationArtefactPath(id)}/update`;\n}\nfunction granolaProcessingIssuesPath(options = {}) {\n return appendSearchParams(granolaTransportPaths.processingIssues, {\n limit: options.limit,\n meetingId: options.meetingId,\n severity: options.severity\n });\n}\nfunction granolaProcessingIssueRecoverPath(id) {\n return `${granolaTransportPaths.processingIssues}/${encodeURIComponent(id)}/recover`;\n}\nfunction granolaExportJobRerunPath(id) {\n return `${granolaTransportPaths.exportJobs}/${encodeURIComponent(id)}/rerun`;\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/checkPrivateRedeclaration.js\nfunction _checkPrivateRedeclaration(e, t) {\n if (t.has(e)) throw new TypeError(\"Cannot initialize the same private elements twice on an object\");\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldInitSpec.js\nfunction _classPrivateFieldInitSpec(e, t, a) {\n _checkPrivateRedeclaration(e, t), t.set(e, a);\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/typeof.js\nfunction _typeof(o) {\n \"@babel/helpers - typeof\";\n return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function(o) {\n return typeof o;\n } : function(o) {\n return o && \"function\" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? \"symbol\" : typeof o;\n }, _typeof(o);\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/toPrimitive.js\nfunction toPrimitive(t, r) {\n if (\"object\" != _typeof(t) || !t) return t;\n var e = t[Symbol.toPrimitive];\n if (void 0 !== e) {\n var i = e.call(t, r || \"default\");\n if (\"object\" != _typeof(i)) return i;\n throw new TypeError(\"@@toPrimitive must return a primitive value.\");\n }\n return (\"string\" === r ? String : Number)(t);\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/toPropertyKey.js\nfunction toPropertyKey(t) {\n var i = toPrimitive(t, \"string\");\n return \"symbol\" == _typeof(i) ? i : i + \"\";\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/defineProperty.js\nfunction _defineProperty(e, r, t) {\n return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {\n value: t,\n enumerable: !0,\n configurable: !0,\n writable: !0\n }) : e[r] = t, e;\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/assertClassBrand.js\nfunction _assertClassBrand(e, t, n) {\n if (\"function\" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;\n throw new TypeError(\"Private element is not present on this object\");\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldSet2.js\nfunction _classPrivateFieldSet2(s, a, r) {\n return s.set(_assertClassBrand(s, a), r), r;\n}\n//#endregion\n//#region \\0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldGet2.js\nfunction _classPrivateFieldGet2(s, a) {\n return s.get(_assertClassBrand(s, a));\n}\n//#endregion\n//#region src/server/client.ts\nfunction resolveFetchImpl(fetchImpl) {\n if (fetchImpl) return ((input, init) => fetchImpl(input, init));\n return ((input, init) => globalThis.fetch(input, init));\n}\nfunction cloneValue(value) {\n return structuredClone(value);\n}\nfunction normaliseServerUrl(serverUrl) {\n const raw = serverUrl instanceof URL ? serverUrl.href : serverUrl.trim();\n if (!raw) throw new Error(\"server URL is required\");\n const withProtocol = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw) ? raw : `http://${raw}`;\n const parsed = new URL(withProtocol);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") throw new Error(\"server URL must use http or https\");\n parsed.pathname = \"/\";\n parsed.search = \"\";\n parsed.hash = \"\";\n return parsed;\n}\nfunction mergeHeaders(...values) {\n const headers = new Headers();\n for (const value of values) {\n if (!value) continue;\n new Headers(value).forEach((headerValue, headerName) => {\n headers.set(headerName, headerValue);\n });\n }\n return headers;\n}\nasync function responseError(response) {\n let message = `${response.status} ${response.statusText}`.trim();\n try {\n const payload = await response.json();\n if (typeof payload.error === \"string\" && payload.error.trim()) message = payload.error;\n else if (typeof payload.message === \"string\" && payload.message.trim()) message = payload.message;\n } catch {\n const text = (await response.text()).trim();\n if (text) message = text;\n }\n return new Error(message);\n}\nfunction parseSseEvent(payload) {\n const data = payload.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\").filter((line) => line.startsWith(\"data:\")).map((line) => line.slice(5).trimStart()).join(\"\\n\");\n if (!data) return;\n return JSON.parse(data);\n}\nvar _closed = /* @__PURE__ */ new WeakMap();\nvar _eventLoop = /* @__PURE__ */ new WeakMap();\nvar _listeners = /* @__PURE__ */ new WeakMap();\nvar _fetchImpl = /* @__PURE__ */ new WeakMap();\nvar _password = /* @__PURE__ */ new WeakMap();\nvar _reconnectDelayMs = /* @__PURE__ */ new WeakMap();\nvar _streamAbortController = /* @__PURE__ */ new WeakMap();\nvar _state = /* @__PURE__ */ new WeakMap();\nvar GranolaServerClient = class GranolaServerClient {\n constructor(info, url, initialState, options = {}) {\n _classPrivateFieldInitSpec(this, _closed, false);\n _classPrivateFieldInitSpec(this, _eventLoop, void 0);\n _classPrivateFieldInitSpec(this, _listeners, /* @__PURE__ */ new Set());\n _classPrivateFieldInitSpec(this, _fetchImpl, void 0);\n _classPrivateFieldInitSpec(this, _password, void 0);\n _classPrivateFieldInitSpec(this, _reconnectDelayMs, void 0);\n _defineProperty(this, \"info\", void 0);\n _classPrivateFieldInitSpec(this, _streamAbortController, void 0);\n _classPrivateFieldInitSpec(this, _state, void 0);\n this.url = url;\n _classPrivateFieldSet2(_fetchImpl, this, resolveFetchImpl(options.fetchImpl));\n this.info = cloneValue(info);\n _classPrivateFieldSet2(_password, this, options.password?.trim() || void 0);\n _classPrivateFieldSet2(_reconnectDelayMs, this, options.reconnectDelayMs ?? 1e3);\n _classPrivateFieldSet2(_state, this, cloneValue(initialState));\n }\n static async connect(serverUrl, options = {}) {\n const url = normaliseServerUrl(serverUrl);\n const fetchImpl = resolveFetchImpl(options.fetchImpl);\n const infoResponse = await fetchImpl(new URL(granolaTransportPaths.serverInfo, url), { headers: mergeHeaders({\n ...options.password?.trim() ? { \"x-granola-password\": options.password.trim() } : {},\n accept: \"application/json\"\n }) });\n if (!infoResponse.ok) throw await responseError(infoResponse);\n const info = await infoResponse.json();\n if (info.protocolVersion !== 2) throw new Error(`unsupported Granola transport protocol: expected 2, got ${info.protocolVersion}`);\n const response = await fetchImpl(new URL(granolaTransportPaths.state, url), { headers: mergeHeaders({\n ...options.password?.trim() ? { \"x-granola-password\": options.password.trim() } : {},\n accept: \"application/json\"\n }) });\n if (!response.ok) throw await responseError(response);\n const client = new GranolaServerClient(info, url, await response.json(), options);\n client.startEvents();\n return client;\n }\n async close() {\n _classPrivateFieldSet2(_closed, this, true);\n _classPrivateFieldGet2(_streamAbortController, this)?.abort();\n try {\n await _classPrivateFieldGet2(_eventLoop, this);\n } catch {}\n }\n getState() {\n return cloneValue(_classPrivateFieldGet2(_state, this));\n }\n subscribe(listener) {\n _classPrivateFieldGet2(_listeners, this).add(listener);\n return () => {\n _classPrivateFieldGet2(_listeners, this).delete(listener);\n };\n }\n async inspectAuth() {\n return await this.requestJson(granolaTransportPaths.authStatus);\n }\n async listAgentHarnesses() {\n return await this.requestJson(granolaTransportPaths.automationHarnesses);\n }\n async saveAgentHarnesses(harnesses) {\n return await this.requestJson(granolaTransportPaths.automationHarnesses, {\n body: JSON.stringify({ harnesses }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async explainAgentHarnesses(meetingId) {\n return await this.requestJson(granolaAutomationHarnessExplainPath(meetingId));\n }\n async listAutomationArtefacts(options = {}) {\n return await this.requestJson(granolaAutomationArtefactsPath(options));\n }\n async evaluateAutomationCases(cases, options) {\n return await this.requestJson(granolaTransportPaths.automationEvaluate, {\n body: JSON.stringify({\n cases,\n options\n }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async listProcessingIssues(options = {}) {\n return await this.requestJson(granolaProcessingIssuesPath(options));\n }\n async getAutomationArtefact(id) {\n return await this.requestJson(granolaAutomationArtefactPath(id));\n }\n async listAutomationRules() {\n return await this.requestJson(granolaTransportPaths.automationRules);\n }\n async saveAutomationRules(rules) {\n return await this.requestJson(granolaTransportPaths.automationRules, {\n body: JSON.stringify({ rules }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async listAutomationMatches(options = {}) {\n const path = options.limit ? `${granolaTransportPaths.automationMatches}?limit=${encodeURIComponent(String(options.limit))}` : granolaTransportPaths.automationMatches;\n return await this.requestJson(path);\n }\n async listAutomationRuns(options = {}) {\n return await this.requestJson(granolaAutomationRunsPath(options));\n }\n async resolveAutomationRun(id, decision, options = {}) {\n return await this.requestJson(granolaAutomationRunDecisionPath(id, decision), {\n body: JSON.stringify(options),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async resolveAutomationArtefact(id, decision, options = {}) {\n return await this.requestJson(granolaAutomationArtefactDecisionPath(id, decision), {\n body: JSON.stringify(options),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async recoverProcessingIssue(id) {\n return await this.requestJson(granolaProcessingIssueRecoverPath(id), { method: \"POST\" });\n }\n async updateAutomationArtefact(id, patch) {\n return await this.requestJson(granolaAutomationArtefactUpdatePath(id), {\n body: JSON.stringify(patch),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async rerunAutomationArtefact(id) {\n return await this.requestJson(granolaAutomationArtefactRerunPath(id), { method: \"POST\" });\n }\n async inspectSync() {\n return cloneValue(_classPrivateFieldGet2(_state, this).sync);\n }\n async listSyncEvents(options = {}) {\n const path = options.limit ? `${granolaTransportPaths.syncEvents}?limit=${encodeURIComponent(String(options.limit))}` : granolaTransportPaths.syncEvents;\n return await this.requestJson(path);\n }\n async loginAuth(options = {}) {\n return await this.requestJson(granolaTransportPaths.authLogin, {\n body: JSON.stringify(options),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async logoutAuth() {\n return await this.requestJson(granolaTransportPaths.authLogout, { method: \"POST\" });\n }\n async refreshAuth() {\n return await this.requestJson(granolaTransportPaths.authRefresh, { method: \"POST\" });\n }\n async switchAuthMode(mode) {\n return await this.requestJson(granolaTransportPaths.authMode, {\n body: JSON.stringify({ mode }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async sync(options = {}) {\n return await this.requestJson(granolaTransportPaths.syncRun, {\n body: JSON.stringify(options),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async listFolders(options = {}) {\n return await this.requestJson(granolaFoldersPath(options));\n }\n async getFolder(id) {\n return await this.requestJson(granolaFolderPath(id));\n }\n async findFolder(query) {\n return await this.requestJson(granolaFolderResolvePath(query));\n }\n async listMeetings(options = {}) {\n return await this.requestJson(granolaMeetingsPath(options));\n }\n async getMeeting(id, options = {}) {\n return await this.requestJson(`${granolaMeetingPath(id)}${options.requireCache ? \"?includeTranscript=true\" : \"\"}`);\n }\n async findMeeting(query, options = {}) {\n return await this.requestJson(granolaMeetingResolvePath(query, { includeTranscript: options.requireCache }));\n }\n async listExportJobs(options = {}) {\n return await this.requestJson(granolaExportJobsPath(options));\n }\n async exportNotes(format = \"markdown\", options = {}) {\n return await this.requestJson(granolaTransportPaths.exportNotes, {\n body: JSON.stringify({\n folderId: options.folderId,\n format\n }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async exportTranscripts(format = \"text\", options = {}) {\n return await this.requestJson(granolaTransportPaths.exportTranscripts, {\n body: JSON.stringify({\n folderId: options.folderId,\n format\n }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n }\n async rerunExportJob(id) {\n return await this.requestJson(granolaExportJobRerunPath(id), { method: \"POST\" });\n }\n async request(path, init = {}) {\n const response = await _classPrivateFieldGet2(_fetchImpl, this).call(this, new URL(path, this.url), {\n ...init,\n headers: mergeHeaders({\n ..._classPrivateFieldGet2(_password, this) ? { \"x-granola-password\": _classPrivateFieldGet2(_password, this) } : {},\n accept: \"application/json\"\n }, init.headers)\n });\n if (!response.ok) throw await responseError(response);\n return response;\n }\n async requestJson(path, init = {}) {\n return cloneValue(await (await this.request(path, init)).json());\n }\n emit(event) {\n _classPrivateFieldSet2(_state, this, cloneValue(event.state));\n const nextEvent = cloneValue(event);\n for (const listener of _classPrivateFieldGet2(_listeners, this)) listener(nextEvent);\n }\n startEvents() {\n if (_classPrivateFieldGet2(_eventLoop, this)) return;\n _classPrivateFieldSet2(_eventLoop, this, this.runEventsLoop());\n }\n async runEventsLoop() {\n while (!_classPrivateFieldGet2(_closed, this)) {\n const controller = new AbortController();\n _classPrivateFieldSet2(_streamAbortController, this, controller);\n try {\n const response = await this.request(granolaTransportPaths.events, {\n headers: { accept: \"text/event-stream\" },\n signal: controller.signal\n });\n await this.consumeEventStream(response);\n } catch {\n if (_classPrivateFieldGet2(_closed, this) || controller.signal.aborted) break;\n await new Promise((resolve) => {\n setTimeout(resolve, _classPrivateFieldGet2(_reconnectDelayMs, this));\n });\n }\n }\n }\n async consumeEventStream(response) {\n const reader = response.body?.getReader();\n if (!reader) throw new Error(\"server did not provide an event stream\");\n const decoder = new TextDecoder();\n let buffer = \"\";\n while (!_classPrivateFieldGet2(_closed, this)) {\n const { done, value } = await reader.read();\n if (done) return;\n buffer += decoder.decode(value, { stream: true });\n buffer = buffer.replaceAll(\"\\r\\n\", \"\\n\");\n while (true) {\n const boundary = buffer.indexOf(\"\\n\\n\");\n if (boundary < 0) break;\n const chunk = buffer.slice(0, boundary);\n buffer = buffer.slice(boundary + 2);\n const event = parseSseEvent(chunk);\n if (event) this.emit(event);\n }\n }\n }\n};\nasync function createGranolaServerClient(serverUrl, options = {}) {\n return await GranolaServerClient.connect(serverUrl, options);\n}\n//#endregion\n//#region src/web/client-state.ts\nvar granolaWebWorkspaceStorageKey = \"granola-toolkit.web-workspace\";\nvar maxRecentMeetings = 6;\nvar maxSavedFilters = 6;\nfunction normaliseFilterValue(value) {\n const trimmed = value?.trim();\n return trimmed ? trimmed : void 0;\n}\nfunction normaliseFilters(filters) {\n const selectedFolderId = normaliseFilterValue(filters.selectedFolderId);\n return {\n search: normaliseFilterValue(filters.search),\n selectedFolderId,\n sort: normaliseFilterValue(filters.sort) ?? \"updated-desc\",\n updatedFrom: normaliseFilterValue(filters.updatedFrom),\n updatedTo: normaliseFilterValue(filters.updatedTo)\n };\n}\nfunction filtersKey(filters) {\n return JSON.stringify(normaliseFilters(filters));\n}\nfunction defaultWorkspacePreferences() {\n return {\n recentMeetings: [],\n savedFilters: []\n };\n}\nfunction parseWorkspacePreferences(raw) {\n if (!raw) return defaultWorkspacePreferences();\n try {\n const parsed = JSON.parse(raw);\n return {\n recentMeetings: Array.isArray(parsed?.recentMeetings) ? parsed.recentMeetings.map((entry) => ({\n folderId: normaliseFilterValue(entry?.folderId),\n id: normaliseFilterValue(entry?.id) || \"\",\n title: normaliseFilterValue(entry?.title) || \"\",\n updatedAt: normaliseFilterValue(entry?.updatedAt) || \"\"\n })).filter((entry) => entry.id && entry.title).slice(0, maxRecentMeetings) : [],\n savedFilters: Array.isArray(parsed?.savedFilters) ? parsed.savedFilters.map((preset) => ({\n filters: normaliseFilters(preset?.filters ?? {}),\n id: normaliseFilterValue(preset?.id) || \"\",\n label: normaliseFilterValue(preset?.label) || \"\"\n })).filter((preset) => preset.id && preset.label).slice(0, maxSavedFilters) : []\n };\n } catch {\n return defaultWorkspacePreferences();\n }\n}\nfunction serialiseWorkspacePreferences(preferences) {\n return JSON.stringify({\n recentMeetings: preferences.recentMeetings.slice(0, maxRecentMeetings),\n savedFilters: preferences.savedFilters.slice(0, maxSavedFilters)\n });\n}\nfunction hasActiveFilters(filters) {\n const normalised = normaliseFilters(filters);\n return Boolean(normalised.search || normalised.selectedFolderId || normalised.updatedFrom || normalised.updatedTo || normalised.sort !== \"updated-desc\");\n}\nfunction filterLabel(filters) {\n const summary = currentFilterSummary(filters);\n if (!summary) return \"Current workspace\";\n return summary;\n}\nfunction rememberRecentMeeting(preferences, meeting) {\n const nextEntry = {\n folderId: meeting.folders?.[0]?.id,\n id: meeting.id,\n title: meeting.title?.trim() || meeting.id,\n updatedAt: meeting.updatedAt\n };\n return {\n ...preferences,\n recentMeetings: [nextEntry, ...preferences.recentMeetings.filter((entry) => entry.id !== nextEntry.id)].slice(0, maxRecentMeetings)\n };\n}\nfunction saveWorkspaceFilter(preferences, filters, options = {}) {\n const nextFilters = normaliseFilters(filters);\n if (!hasActiveFilters(nextFilters)) return preferences;\n const key = filtersKey(nextFilters);\n const nextPreset = {\n filters: nextFilters,\n id: preferences.savedFilters.find((preset) => filtersKey(preset.filters) === key)?.id ?? options.idFactory?.() ?? `filter-${preferences.savedFilters.length + 1}`,\n label: filterLabel(filters)\n };\n return {\n ...preferences,\n savedFilters: [nextPreset, ...preferences.savedFilters.filter((preset) => preset.id !== nextPreset.id)].slice(0, maxSavedFilters)\n };\n}\nfunction removeWorkspaceFilter(preferences, id) {\n return {\n ...preferences,\n savedFilters: preferences.savedFilters.filter((preset) => preset.id !== id)\n };\n}\nfunction applyWorkspaceFilter(preset) {\n return {\n search: preset.filters.search ?? \"\",\n selectedFolderId: preset.filters.selectedFolderId ?? null,\n sort: preset.filters.sort ?? \"updated-desc\",\n updatedFrom: preset.filters.updatedFrom ?? \"\",\n updatedTo: preset.filters.updatedTo ?? \"\"\n };\n}\nfunction parseWorkspaceTab(value) {\n switch (value) {\n case \"metadata\":\n case \"raw\":\n case \"transcript\": return value;\n default: return \"notes\";\n }\n}\nfunction startupSelectionFromSearch(search) {\n const params = new URLSearchParams(search);\n return {\n folderId: params.get(\"folder\")?.trim() || \"\",\n meetingId: params.get(\"meeting\")?.trim() || \"\",\n workspaceTab: parseWorkspaceTab(params.get(\"tab\"))\n };\n}\nfunction buildBrowserUrlPath(currentHref, selection) {\n const url = new URL(currentHref);\n if (selection.selectedFolderId) url.searchParams.set(\"folder\", selection.selectedFolderId);\n else url.searchParams.delete(\"folder\");\n if (selection.selectedMeetingId) url.searchParams.set(\"meeting\", selection.selectedMeetingId);\n else url.searchParams.delete(\"meeting\");\n if (parseWorkspaceTab(selection.workspaceTab) !== \"notes\") url.searchParams.set(\"tab\", parseWorkspaceTab(selection.workspaceTab));\n else url.searchParams.delete(\"tab\");\n return `${url.pathname}${url.search}${url.hash}`;\n}\nfunction exportScopeLabel(scope) {\n return scope && scope.mode === \"folder\" ? `Folder: ${scope.folderName || scope.folderId}` : \"Scope: All meetings\";\n}\nfunction currentFilterSummary(filters) {\n const parts = [];\n if (filters.selectedFolderId) {\n const folder = filters.folders.find((candidate) => candidate.id === filters.selectedFolderId);\n parts.push(`folder \"${folder ? folder.name : filters.selectedFolderId}\"`);\n }\n if (filters.search) parts.push(`search \"${filters.search}\"`);\n if (filters.updatedFrom) parts.push(`from ${filters.updatedFrom}`);\n if (filters.updatedTo) parts.push(`to ${filters.updatedTo}`);\n if (filters.sort && filters.sort !== \"updated-desc\") parts.push(filters.sort === \"updated-asc\" ? \"oldest first\" : filters.sort === \"title-asc\" ? \"title A-Z\" : \"title Z-A\");\n return parts.join(\", \");\n}\nfunction selectMeetingId(meetings, selectedMeetingId) {\n if (selectedMeetingId && meetings.some((meeting) => meeting.id === selectedMeetingId)) return selectedMeetingId;\n return meetings[0]?.id ?? null;\n}\nfunction nextWorkspaceTab(currentTab, key) {\n const current = parseWorkspaceTab(currentTab);\n switch (key) {\n case \"1\": return \"notes\";\n case \"2\": return \"transcript\";\n case \"3\": return \"metadata\";\n case \"4\": return \"raw\";\n case \"]\":\n switch (current) {\n case \"notes\": return \"transcript\";\n case \"transcript\": return \"metadata\";\n case \"metadata\": return \"raw\";\n case \"raw\": return \"notes\";\n }\n break;\n case \"[\":\n switch (current) {\n case \"notes\": return \"raw\";\n case \"transcript\": return \"notes\";\n case \"metadata\": return \"transcript\";\n case \"raw\": return \"metadata\";\n }\n break;\n default: return;\n }\n}\nfunction describeSyncStatus(sync) {\n if (sync.running) return \"Sync running\";\n if (sync.lastError) return \"Sync needs attention\";\n if (sync.lastCompletedAt) {\n const suffix = sync.summary?.changedCount ? ` · ${sync.summary.changedCount} changes` : \"\";\n return `Synced ${sync.lastCompletedAt.slice(11, 19)}${suffix}`;\n }\n return \"Sync idle\";\n}\nfunction describeAuthStatus(auth) {\n if (!auth) return \"Waiting for auth\";\n if (auth.lastError) return \"Auth needs attention\";\n switch (auth.mode) {\n case \"api-key\": return \"Personal API key active\";\n case \"stored-session\": return \"Stored session active\";\n default: return \"supabase.json fallback active\";\n }\n}\n//#endregion\n//#region src/auth-summary.ts\nfunction granolaAuthModeLabel(mode) {\n switch (mode) {\n case \"api-key\": return \"Personal API key\";\n case \"stored-session\": return \"Stored session\";\n default: return \"supabase.json fallback\";\n }\n}\nfunction granolaAuthRecommendation(auth) {\n if (auth.mode === \"api-key\") return {\n detail: \"This is the recommended default for long-running sync, web, and automation workflows.\",\n status: \"Recommended auth active\"\n };\n if (auth.apiKeyAvailable) return {\n detail: \"A stored Personal API key is available. Switch to it for the cleanest background-service auth path.\",\n nextAction: \"granola auth use api-key\",\n status: \"Stored API key available\"\n };\n return {\n detail: \"Recommended: create a Personal API key in Granola Settings -> API. Desktop-session import remains available as a fallback.\",\n nextAction: \"granola auth login --api-key grn_...\",\n status: \"No stored API key yet\"\n };\n}\n//#endregion\n//#region src/web-app/components.tsx\n/** @jsxImportSource solid-js */\nvar _tmpl$$3 = /* @__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, notes, transcripts, or artefacts\"><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$3 = /* @__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$3 = /* @__PURE__ */ template(`<div class=\"folder-empty folder-empty--error\">`), _tmpl$4$3 = /* @__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$3 = /* @__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$2 = /* @__PURE__ */ template(`<div class=folder-empty>No folders found.`), _tmpl$7$2 = /* @__PURE__ */ template(`<button class=folder-row type=button><span class=folder-row__title></span><span class=folder-row__meta>`), _tmpl$8$2 = /* @__PURE__ */ template(`<section class=folder-panel><div class=folder-panel__head><h2>Saved Filters</h2><p>Keep the slices you revisit often close at hand.</p></div><div class=saved-filter-actions><button class=\"button button--secondary\"type=button>Save current filter</button></div><div class=saved-filter-list>`), _tmpl$9$1 = /* @__PURE__ */ template(`<div class=folder-empty>No saved filters yet.`), _tmpl$0$1 = /* @__PURE__ */ template(`<div class=saved-filter-card><button class=saved-filter-card__main type=button><span class=folder-row__title></span><span class=folder-row__meta></span></button><button class=saved-filter-card__remove type=button>Remove`), _tmpl$1$1 = /* @__PURE__ */ template(`<section class=folder-panel><div class=folder-panel__head><h2>Recent Meetings</h2><p>Jump back into the conversations you opened most recently.</p></div><div class=folder-list>`), _tmpl$10$1 = /* @__PURE__ */ template(`<div class=folder-empty>No recent meetings yet.`), _tmpl$11 = /* @__PURE__ */ template(`<div class=\"meeting-empty meeting-empty--error\">`), _tmpl$12 = /* @__PURE__ */ template(`<section class=meeting-list>`), _tmpl$13 = /* @__PURE__ */ template(`<div class=meeting-empty>`), _tmpl$14 = /* @__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$15 = /* @__PURE__ */ template(`<p>`), _tmpl$16 = /* @__PURE__ */ template(`<section class=detail-head><div><h2>Meeting Workspace</h2></div><div class=state-badge>`), _tmpl$17 = /* @__PURE__ */ template(`<p>Waiting for server state…`), _tmpl$18 = /* @__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></strong></div><div><span class=status-label>Automation</span><strong>`), _tmpl$19 = /* @__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$20 = /* @__PURE__ */ template(`<section class=auth-panel><div class=auth-panel__head><h3>Auth Session</h3><p>Prefer a Granola Personal API key, then keep stored session and <code>supabase.json</code> as fallbacks.</p></div><div class=auth-panel__body>`), _tmpl$21 = /* @__PURE__ */ template(`<div class=auth-card><div class=auth-card__meta>Auth state unavailable.`), _tmpl$22 = /* @__PURE__ */ template(`<div class=auth-card__meta>Client ID: `), _tmpl$23 = /* @__PURE__ */ template(`<div class=auth-card__meta>Sign-in method: `), _tmpl$24 = /* @__PURE__ */ template(`<div class=auth-card__meta>supabase path: `), _tmpl$25 = /* @__PURE__ */ template(`<div class=\"auth-card__meta auth-card__error\">`), _tmpl$26 = /* @__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><strong>.</strong> </div><div class=auth-card__meta>Save a Personal API key here or use <code>granola auth login --api-key <token></code>. Desktop-session import remains the fallback path.</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>Use API key</button><button class=\"button button--secondary\"type=button>Import desktop session fallback</button><button class=\"button button--secondary\"type=button>Refresh stored session</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$27 = /* @__PURE__ */ template(`<div class=auth-card__meta>Next step: `), _tmpl$28 = /* @__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$29 = /* @__PURE__ */ template(`<div class=job-empty>No export jobs yet.`), _tmpl$30 = /* @__PURE__ */ template(`<div class=job-card__meta>`), _tmpl$31 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Rerun`), _tmpl$32 = /* @__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$35 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Approve`), _tmpl$36 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Reject`), _tmpl$41 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Recover`), _tmpl$42 = /* @__PURE__ */ template(`<section class=jobs-panel><div class=jobs-panel__head><h3>Review Inbox</h3><p></p></div><div class=jobs-list>`), _tmpl$43 = /* @__PURE__ */ template(`<div class=job-empty>No review items waiting.`), _tmpl$44 = /* @__PURE__ */ template(`<button class=\"job-card job-card--button\"type=button><div class=job-card__head><div><div class=job-card__title></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>`), _tmpl$47 = /* @__PURE__ */ template(`<section class=review-panel><div class=jobs-panel__head><h3>Issue Review</h3><p>Inspect the issue, jump to the meeting if needed, and run recovery from one place.`), _tmpl$48 = /* @__PURE__ */ template(`<div class=job-empty>Select a processing issue to inspect it.`), _tmpl$49 = /* @__PURE__ */ template(`<div class=detail-chip>`), _tmpl$50 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Open meeting`), _tmpl$51 = /* @__PURE__ */ template(`<div class=review-body><div class=detail-meta><div class=detail-chip></div><div class=detail-chip></div></div><section class=detail-section><h2></h2><p></p><div class=job-card__actions>`), _tmpl$52 = /* @__PURE__ */ template(`<section class=review-panel><div class=jobs-panel__head><h3>Approval Review</h3><p>Approve or reject ask-user automation runs with the meeting context still in view.`), _tmpl$53 = /* @__PURE__ */ template(`<div class=job-empty>Select a pending run to review it.`), _tmpl$54 = /* @__PURE__ */ template(`<div class=review-body><div class=detail-meta><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip></div></div><section class=detail-section><h2></h2><p></p><div class=job-card__actions><button class=\"button button--secondary\"type=button>Open meeting`), _tmpl$55 = /* @__PURE__ */ template(`<section class=review-panel><div class=jobs-panel__head><h3>Artefact Review</h3><p>Review generated candidate notes, compare them to the current meeting, then approve, reject, edit, or rerun.`), _tmpl$56 = /* @__PURE__ */ template(`<div class=job-empty>`), _tmpl$57 = /* @__PURE__ */ template(`<div class=detail-section><h3>Action Items</h3><ul class=detail-list>`), _tmpl$58 = /* @__PURE__ */ template(`<div class=detail-section><h3>Participant Summaries</h3><ul class=detail-list>`), _tmpl$59 = /* @__PURE__ */ template(`<div class=review-grid><section class=detail-section><h2>Current Meeting Notes</h2><pre class=detail-pre></pre></section><section class=detail-section><h2>Candidate</h2><label class=field-row><span class=field-label>Title</span><input class=\"field-input field-input--plain\"></label><label class=field-row><span class=field-label>Summary</span><textarea class=\"review-textarea review-textarea--summary\"></textarea></label><label class=field-row><span class=field-label>Markdown</span><textarea class=review-textarea></textarea></label><label class=field-row><span class=field-label>Review Note</span><textarea class=\"review-textarea review-textarea--summary\"></textarea></label><div class=job-card__actions><button class=\"button button--secondary\"type=button>Save edits</button><button class=\"button button--secondary\"type=button>Approve</button><button class=\"button button--secondary\"type=button>Reject</button><button class=\"button button--secondary\"type=button>Rerun`), _tmpl$60 = /* @__PURE__ */ template(`<div class=review-body><div class=detail-meta><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip></div></div><section class=\"detail-section review-history\"><h2>History</h2><div class=jobs-list>`), _tmpl$61 = /* @__PURE__ */ template(`<div class=empty>`), _tmpl$62 = /* @__PURE__ */ template(`<span>`), _tmpl$63 = /* @__PURE__ */ template(`<li><strong>`), _tmpl$64 = /* @__PURE__ */ template(`<li><strong></strong><div>`), _tmpl$65 = /* @__PURE__ */ template(`<div class=job-card><div class=job-card__head><div class=job-card__title></div><div class=job-card__meta>`), _tmpl$66 = /* @__PURE__ */ template(`<nav class=workspace-tabs><span class=workspace-hint>1-4 switch tabs, [ and ] cycle`), _tmpl$67 = /* @__PURE__ */ template(`<button class=workspace-tab type=button>`), _tmpl$68 = /* @__PURE__ */ template(`<div class=detail-meta><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip>`), _tmpl$69 = /* @__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>`);\nfunction metadataLines(record) {\n return [\n `Title: ${record.meeting.title || record.meeting.id}`,\n `Created: ${record.meeting.createdAt}`,\n `Updated: ${record.meeting.updatedAt}`,\n `Folders: ${record.meeting.folders.length ? record.meeting.folders.map((folder) => folder.name).join(\", \") : \"none\"}`,\n `Tags: ${record.meeting.tags.length ? record.meeting.tags.join(\", \") : \"none\"}`,\n `Transcript loaded: ${record.meeting.transcriptLoaded ? \"yes\" : \"no\"}`,\n `Owner candidates: ${record.roleHelpers.ownerCandidates.length ? record.roleHelpers.ownerCandidates.map((candidate) => candidate.label).join(\", \") : \"none\"}`,\n `Speakers: ${record.roleHelpers.speakers.length ? record.roleHelpers.speakers.map((speaker) => `${speaker.label} (${speaker.segmentCount})`).join(\", \") : \"none\"}`\n ].join(\"\\n\");\n}\nfunction workspaceBody(bundle, record, tab) {\n switch (tab) {\n case \"transcript\": return {\n body: record.transcriptText || \"(Transcript unavailable)\",\n title: \"Transcript\"\n };\n case \"metadata\": return {\n body: metadataLines(record),\n title: \"Metadata\"\n };\n case \"raw\": return {\n body: JSON.stringify(bundle || record, null, 2),\n title: \"Raw Bundle\"\n };\n default: return {\n body: record.noteMarkdown || \"(No notes available)\",\n title: \"Notes\"\n };\n }\n}\nfunction scopeLabel(scope) {\n return exportScopeLabel(scope);\n}\nfunction ToolbarFilters(props) {\n return [(() => {\n var _el$ = _tmpl$$3(), _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;\n _el$4.$$input = (event) => {\n props.onSearchInput(event.currentTarget.value);\n };\n _el$8.addEventListener(\"change\", (event) => {\n props.onSortChange(event.currentTarget.value);\n });\n _el$1.addEventListener(\"change\", (event) => {\n props.onUpdatedFromChange(event.currentTarget.value);\n });\n _el$12.addEventListener(\"change\", (event) => {\n props.onUpdatedToChange(event.currentTarget.value);\n });\n createRenderEffect(() => _el$4.value = props.search);\n createRenderEffect(() => _el$8.value = props.sort);\n createRenderEffect(() => _el$1.value = props.updatedFrom);\n createRenderEffect(() => _el$12.value = props.updatedTo);\n return _el$;\n })(), (() => {\n var _el$13 = _tmpl$2$3(), _el$16 = _el$13.firstChild.nextSibling.firstChild, _el$17 = _el$16.nextSibling;\n _el$16.$$keydown = (event) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n props.onQuickOpen();\n }\n };\n _el$16.$$input = (event) => {\n props.onQuickOpenInput(event.currentTarget.value);\n };\n addEventListener(_el$17, \"click\", props.onQuickOpen, true);\n createRenderEffect(() => _el$16.value = props.quickOpen);\n return _el$13;\n })()];\n}\nfunction FolderList(props) {\n return (() => {\n var _el$18 = _tmpl$4$3(), _el$20 = _el$18.firstChild.nextSibling;\n insert(_el$20, createComponent(Show, {\n get fallback() {\n return [\n (() => {\n var _el$22 = _tmpl$5$3();\n _el$22.$$click = () => {\n props.onSelect(null);\n };\n createRenderEffect(() => setAttribute(_el$22, \"data-selected\", !props.selectedFolderId ? \"true\" : void 0));\n return _el$22;\n })(),\n createComponent(For, {\n get each() {\n return props.folders;\n },\n children: (folder) => (() => {\n var _el$24 = _tmpl$7$2(), _el$25 = _el$24.firstChild, _el$26 = _el$25.nextSibling;\n _el$24.$$click = () => {\n props.onSelect(folder.id);\n };\n insert(_el$25, () => (folder.isFavourite ? \"★ \" : \"\") + (folder.name || folder.id));\n insert(_el$26, () => `${folder.documentCount} meetings`);\n createRenderEffect(() => setAttribute(_el$24, \"data-selected\", folder.id === props.selectedFolderId ? \"true\" : void 0));\n return _el$24;\n })()\n }),\n createComponent(Show, {\n get when() {\n return props.folders.length === 0;\n },\n get children() {\n return _tmpl$6$2();\n }\n })\n ];\n },\n get when() {\n return !props.error;\n },\n get children() {\n var _el$21 = _tmpl$3$3();\n insert(_el$21, () => props.error);\n return _el$21;\n }\n }));\n return _el$18;\n })();\n}\nfunction SavedFiltersPanel(props) {\n const canSaveCurrent = () => hasActiveFilters({\n search: props.search,\n selectedFolderId: props.selectedFolderId,\n sort: props.sort,\n updatedFrom: props.updatedFrom,\n updatedTo: props.updatedTo\n });\n return (() => {\n var _el$27 = _tmpl$8$2(), _el$29 = _el$27.firstChild.nextSibling, _el$30 = _el$29.firstChild, _el$31 = _el$29.nextSibling;\n _el$30.$$click = () => {\n props.onSaveCurrent();\n };\n insert(_el$31, createComponent(Show, {\n get when() {\n return props.savedFilters.length > 0;\n },\n get fallback() {\n return _tmpl$9$1();\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.savedFilters;\n },\n children: (preset) => (() => {\n var _el$33 = _tmpl$0$1(), _el$34 = _el$33.firstChild, _el$35 = _el$34.firstChild, _el$36 = _el$35.nextSibling, _el$37 = _el$34.nextSibling;\n _el$34.$$click = () => {\n props.onApply(preset);\n };\n insert(_el$35, () => preset.label);\n insert(_el$36, () => currentFilterSummary({\n folders: props.folders,\n ...preset.filters\n }) || \"Saved workspace scope\");\n _el$37.$$click = () => {\n props.onRemove(preset.id);\n };\n return _el$33;\n })()\n });\n }\n }));\n createRenderEffect(() => _el$30.disabled = !canSaveCurrent());\n return _el$27;\n })();\n}\nfunction RecentMeetingsPanel(props) {\n return (() => {\n var _el$38 = _tmpl$1$1(), _el$40 = _el$38.firstChild.nextSibling;\n insert(_el$40, createComponent(Show, {\n get when() {\n return props.recentMeetings.length > 0;\n },\n get fallback() {\n return _tmpl$10$1();\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.recentMeetings;\n },\n children: (meeting) => (() => {\n var _el$42 = _tmpl$7$2(), _el$43 = _el$42.firstChild, _el$44 = _el$43.nextSibling;\n _el$42.$$click = () => {\n props.onOpen(meeting);\n };\n insert(_el$43, () => meeting.title);\n insert(_el$44, () => meeting.updatedAt.slice(0, 10));\n return _el$42;\n })()\n });\n }\n }));\n return _el$38;\n })();\n}\nfunction MeetingList(props) {\n const summary = () => currentFilterSummary({\n folders: props.folders,\n search: props.search,\n selectedFolderId: props.selectedFolderId,\n updatedFrom: props.updatedFrom,\n updatedTo: props.updatedTo\n });\n return (() => {\n var _el$45 = _tmpl$12();\n insert(_el$45, createComponent(Show, {\n get fallback() {\n return createComponent(Show, {\n get fallback() {\n return (() => {\n var _el$47 = _tmpl$13();\n insert(_el$47, (() => {\n var _c$ = memo(() => !!summary());\n return () => _c$() ? `No meetings match ${summary()}.` : props.emptyHint || \"No meetings yet. Try Sync now.\";\n })());\n return _el$47;\n })();\n },\n get when() {\n return props.meetings.length > 0;\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.meetings;\n },\n children: (meeting) => (() => {\n var _el$48 = _tmpl$14(), _el$49 = _el$48.firstChild, _el$50 = _el$49.nextSibling, _el$51 = _el$50.nextSibling;\n _el$48.$$click = () => {\n props.onSelect(meeting.id);\n };\n insert(_el$49, () => meeting.title || meeting.id);\n insert(_el$50, (() => {\n var _c$2 = memo(() => !!meeting.tags.length);\n return () => _c$2() ? meeting.tags.map((tag) => `#${tag}`).join(\" \") : \"untagged\";\n })());\n insert(_el$51, (() => {\n var _c$3 = memo(() => !!meeting.updatedAt);\n return () => _c$3() ? meeting.updatedAt.slice(0, 10) : \"unknown\";\n })());\n createRenderEffect(() => setAttribute(_el$48, \"data-selected\", meeting.id === props.selectedMeetingId ? \"true\" : void 0));\n return _el$48;\n })()\n });\n }\n });\n },\n get when() {\n return props.error;\n },\n get children() {\n var _el$46 = _tmpl$11();\n insert(_el$46, () => props.error);\n return _el$46;\n }\n }));\n return _el$45;\n })();\n}\nfunction AppStatePanel(props) {\n const syncStatus = () => describeSyncStatus(props.appState?.sync ?? {});\n const authStatus = () => describeAuthStatus(props.appState?.auth);\n return (() => {\n var _el$52 = _tmpl$16(), _el$53 = _el$52.firstChild;\n _el$53.firstChild;\n var _el$56 = _el$53.nextSibling;\n insert(_el$53, createComponent(Show, {\n get fallback() {\n return _tmpl$17();\n },\n get when() {\n return props.appState;\n },\n children: (appState) => (() => {\n var _el$58 = _tmpl$18(), _el$59 = _el$58.firstChild, _el$61 = _el$59.firstChild.nextSibling, _el$62 = _el$59.nextSibling, _el$64 = _el$62.firstChild.nextSibling, _el$65 = _el$62.nextSibling, _el$67 = _el$65.firstChild.nextSibling, _el$68 = _el$65.nextSibling, _el$70 = _el$68.firstChild.nextSibling, _el$71 = _el$68.nextSibling, _el$73 = _el$71.firstChild.nextSibling, _el$74 = _el$71.nextSibling, _el$76 = _el$74.firstChild.nextSibling, _el$77 = _el$74.nextSibling, _el$79 = _el$77.firstChild.nextSibling, _el$80 = _el$77.nextSibling, _el$82 = _el$80.firstChild.nextSibling, _el$85 = _el$80.nextSibling.firstChild.nextSibling;\n insert(_el$61, () => appState().ui.surface);\n insert(_el$64, () => appState().ui.view);\n insert(_el$67, authStatus);\n insert(_el$70, syncStatus);\n insert(_el$73, (() => {\n var _c$4 = memo(() => !!appState().documents.loaded);\n return () => _c$4() ? String(appState().documents.count) : \"not loaded\";\n })());\n insert(_el$76, (() => {\n var _c$5 = memo(() => !!appState().folders.loaded);\n return () => _c$5() ? String(appState().folders.count) : \"not loaded\";\n })());\n insert(_el$79, (() => {\n var _c$6 = memo(() => !!appState().cache.loaded);\n return () => _c$6() ? `${appState().cache.transcriptCount} transcript sets` : appState().cache.configured ? \"configured\" : \"not configured\";\n })());\n insert(_el$82, (() => {\n var _c$7 = memo(() => !!appState().index.loaded);\n return () => _c$7() ? `${appState().index.meetingCount} meetings` : appState().index.available ? \"available\" : \"not built\";\n })());\n insert(_el$85, () => `${appState().automation.runCount} runs / ${appState().automation.pendingRunCount} pending runs / ${appState().automation.pendingArtefactCount} pending artefacts`);\n return _el$58;\n })()\n }), null);\n insert(_el$53, createComponent(Show, {\n get when() {\n return props.appState?.auth.lastError;\n },\n get children() {\n var _el$55 = _tmpl$15();\n insert(_el$55, () => props.appState?.auth.lastError);\n return _el$55;\n }\n }), null);\n insert(_el$56, () => props.statusLabel);\n createRenderEffect(() => setAttribute(_el$56, \"data-tone\", props.statusTone));\n return _el$52;\n })();\n}\nfunction SecurityPanel(props) {\n return createComponent(Show, {\n get when() {\n return props.visible;\n },\n get children() {\n var _el$86 = _tmpl$19(), _el$89 = _el$86.firstChild.nextSibling.firstChild, _el$91 = _el$89.nextSibling.firstChild, _el$92 = _el$91.nextSibling;\n _el$89.$$keydown = (event) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n props.onUnlock();\n }\n };\n _el$89.$$input = (event) => {\n props.onPasswordChange(event.currentTarget.value);\n };\n addEventListener(_el$91, \"click\", props.onUnlock, true);\n addEventListener(_el$92, \"click\", props.onLock, true);\n createRenderEffect(() => _el$89.value = props.password);\n return _el$86;\n }\n });\n}\nfunction AuthPanel(props) {\n return (() => {\n var _el$93 = _tmpl$20(), _el$95 = _el$93.firstChild.nextSibling;\n insert(_el$95, createComponent(Show, {\n get fallback() {\n return _tmpl$21();\n },\n get when() {\n return props.auth;\n },\n children: (auth) => (() => {\n var _el$97 = _tmpl$26(), _el$98 = _el$97.firstChild, _el$99 = _el$98.firstChild, _el$101 = _el$99.firstChild.nextSibling, _el$102 = _el$99.nextSibling, _el$104 = _el$102.firstChild.nextSibling, _el$105 = _el$102.nextSibling, _el$107 = _el$105.firstChild.nextSibling, _el$108 = _el$105.nextSibling, _el$110 = _el$108.firstChild.nextSibling, _el$113 = _el$108.nextSibling.firstChild.nextSibling, _el$114 = _el$98.nextSibling, _el$115 = _el$114.firstChild, _el$116 = _el$115.firstChild;\n _el$115.nextSibling;\n var _el$125 = _el$114.nextSibling, _el$127 = _el$125.nextSibling.firstChild, _el$128 = _el$127.nextSibling, _el$129 = _el$128.nextSibling, _el$130 = _el$129.nextSibling, _el$131 = _el$130.nextSibling, _el$132 = _el$131.nextSibling, _el$133 = _el$132.nextSibling, _el$134 = _el$133.nextSibling;\n insert(_el$101, () => granolaAuthModeLabel(auth().mode));\n insert(_el$104, () => auth().apiKeyAvailable ? \"available\" : \"missing\");\n insert(_el$107, () => auth().storedSessionAvailable ? \"available\" : \"missing\");\n insert(_el$110, () => auth().supabaseAvailable ? \"available\" : \"missing\");\n insert(_el$113, () => auth().refreshAvailable ? \"available\" : \"missing\");\n insert(_el$115, () => granolaAuthRecommendation(auth()).status, _el$116);\n insert(_el$114, () => granolaAuthRecommendation(auth()).detail, null);\n insert(_el$97, createComponent(Show, {\n get when() {\n return granolaAuthRecommendation(auth()).nextAction;\n },\n children: (nextAction) => (() => {\n var _el$135 = _tmpl$27();\n _el$135.firstChild;\n insert(_el$135, nextAction, null);\n return _el$135;\n })()\n }), _el$125);\n insert(_el$97, createComponent(Show, {\n get when() {\n return auth().clientId;\n },\n get children() {\n var _el$118 = _tmpl$22();\n _el$118.firstChild;\n insert(_el$118, () => auth().clientId, null);\n return _el$118;\n }\n }), _el$125);\n insert(_el$97, createComponent(Show, {\n get when() {\n return auth().signInMethod;\n },\n get children() {\n var _el$120 = _tmpl$23();\n _el$120.firstChild;\n insert(_el$120, () => auth().signInMethod, null);\n return _el$120;\n }\n }), _el$125);\n insert(_el$97, createComponent(Show, {\n get when() {\n return auth().supabasePath;\n },\n get children() {\n var _el$122 = _tmpl$24();\n _el$122.firstChild;\n insert(_el$122, () => auth().supabasePath, null);\n return _el$122;\n }\n }), _el$125);\n insert(_el$97, createComponent(Show, {\n get when() {\n return auth().lastError;\n },\n get children() {\n var _el$124 = _tmpl$25();\n insert(_el$124, () => auth().lastError);\n return _el$124;\n }\n }), _el$125);\n _el$127.$$input = (event) => {\n props.onApiKeyDraftChange(event.currentTarget.value);\n };\n addEventListener(_el$128, \"click\", props.onSaveApiKey, true);\n _el$129.$$click = () => {\n props.onSwitchMode(\"api-key\");\n };\n addEventListener(_el$130, \"click\", props.onImportDesktopSession, true);\n addEventListener(_el$131, \"click\", props.onRefresh, true);\n _el$132.$$click = () => {\n props.onSwitchMode(\"stored-session\");\n };\n _el$133.$$click = () => {\n props.onSwitchMode(\"supabase-file\");\n };\n addEventListener(_el$134, \"click\", props.onLogout, true);\n createRenderEffect((_p$) => {\n var _v$ = !auth().apiKeyAvailable || auth().mode === \"api-key\", _v$2 = !auth().supabaseAvailable, _v$3 = !auth().storedSessionAvailable || !auth().refreshAvailable, _v$4 = !auth().storedSessionAvailable || auth().mode === \"stored-session\", _v$5 = !auth().supabaseAvailable || auth().mode === \"supabase-file\", _v$6 = !auth().apiKeyAvailable && !auth().storedSessionAvailable;\n _v$ !== _p$.e && (_el$129.disabled = _p$.e = _v$);\n _v$2 !== _p$.t && (_el$130.disabled = _p$.t = _v$2);\n _v$3 !== _p$.a && (_el$131.disabled = _p$.a = _v$3);\n _v$4 !== _p$.o && (_el$132.disabled = _p$.o = _v$4);\n _v$5 !== _p$.i && (_el$133.disabled = _p$.i = _v$5);\n _v$6 !== _p$.n && (_el$134.disabled = _p$.n = _v$6);\n return _p$;\n }, {\n e: void 0,\n t: void 0,\n a: void 0,\n o: void 0,\n i: void 0,\n n: void 0\n });\n createRenderEffect(() => _el$127.value = props.apiKeyDraft);\n return _el$97;\n })()\n }));\n return _el$93;\n })();\n}\nfunction ExportJobsPanel(props) {\n return (() => {\n var _el$137 = _tmpl$28(), _el$139 = _el$137.firstChild.nextSibling;\n insert(_el$139, createComponent(Show, {\n get when() {\n return props.jobs.length > 0;\n },\n get fallback() {\n return _tmpl$29();\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.jobs.slice(0, 6);\n },\n children: (job) => (() => {\n var _el$141 = _tmpl$32(), _el$142 = _el$141.firstChild, _el$143 = _el$142.firstChild, _el$144 = _el$143.firstChild, _el$145 = _el$144.firstChild, _el$146 = _el$144.nextSibling, _el$147 = _el$143.nextSibling, _el$148 = _el$142.nextSibling, _el$149 = _el$148.nextSibling;\n _el$149.firstChild;\n var _el$151 = _el$149.nextSibling;\n _el$151.firstChild;\n var _el$154 = _el$151.nextSibling;\n insert(_el$144, () => job.kind, _el$145);\n insert(_el$146, () => job.id);\n insert(_el$147, () => job.status);\n insert(_el$148, () => `Format: ${job.format} • ${scopeLabel(job.scope)} • ${job.itemCount > 0 ? `${job.completedCount}/${job.itemCount} items` : \"0 items\"} • Written: ${job.written}`);\n insert(_el$149, () => job.startedAt.slice(0, 19), null);\n insert(_el$151, () => job.outputDir, null);\n insert(_el$141, createComponent(Show, {\n get when() {\n return job.error;\n },\n get children() {\n var _el$153 = _tmpl$30();\n insert(_el$153, () => job.error);\n return _el$153;\n }\n }), _el$154);\n insert(_el$154, createComponent(Show, {\n get when() {\n return job.status !== \"running\";\n },\n get children() {\n var _el$155 = _tmpl$31();\n _el$155.$$click = () => {\n props.onRerun(job.id);\n };\n return _el$155;\n }\n }));\n createRenderEffect(() => setAttribute(_el$147, \"data-status\", job.status));\n return _el$141;\n })()\n });\n }\n }));\n return _el$137;\n })();\n}\nfunction ReviewInboxPanel(props) {\n return (() => {\n var _el$189 = _tmpl$42(), _el$190 = _el$189.firstChild, _el$192 = _el$190.firstChild.nextSibling, _el$193 = _el$190.nextSibling;\n insert(_el$192, (() => {\n var _c$8 = memo(() => props.summary.total > 0);\n return () => _c$8() ? `${props.summary.total} items need attention: ${props.summary.issues} issues, ${props.summary.artefacts} artefacts, ${props.summary.runs} approvals.` : \"Nothing needs attention right now.\";\n })());\n insert(_el$193, createComponent(Show, {\n get when() {\n return props.items.length > 0;\n },\n get fallback() {\n return _tmpl$43();\n },\n get children() {\n return createComponent(For, {\n get each() {\n return props.items.slice(0, 12);\n },\n children: (item) => (() => {\n var _el$195 = _tmpl$44(), _el$196 = _el$195.firstChild, _el$197 = _el$196.firstChild, _el$198 = _el$197.firstChild, _el$199 = _el$198.nextSibling, _el$200 = _el$197.nextSibling, _el$202 = _el$196.nextSibling, _el$203 = _el$202.nextSibling;\n _el$195.$$click = () => {\n props.onSelect(item.key);\n };\n insert(_el$198, () => item.title);\n insert(_el$199, () => item.subtitle);\n insert(_el$200, () => item.status);\n insert(_el$195, createComponent(Show, {\n get when() {\n return item.meetingId;\n },\n get children() {\n var _el$201 = _tmpl$30();\n insert(_el$201, () => item.meetingId);\n return _el$201;\n }\n }), _el$202);\n insert(_el$202, () => item.summary);\n insert(_el$203, () => `Updated: ${item.timestamp.slice(0, 19)}`);\n createRenderEffect((_p$) => {\n var _v$7 = item.key === props.selectedKey ? \"true\" : void 0, _v$8 = item.status;\n _v$7 !== _p$.e && setAttribute(_el$195, \"data-selected\", _p$.e = _v$7);\n _v$8 !== _p$.t && setAttribute(_el$200, \"data-status\", _p$.t = _v$8);\n return _p$;\n }, {\n e: void 0,\n t: void 0\n });\n return _el$195;\n })()\n });\n }\n }));\n return _el$189;\n })();\n}\nfunction IssueReviewPanel(props) {\n return (() => {\n var _el$217 = _tmpl$47();\n _el$217.firstChild;\n insert(_el$217, createComponent(Show, {\n get when() {\n return props.issue;\n },\n get fallback() {\n return _tmpl$48();\n },\n children: (issue) => (() => {\n var _el$220 = _tmpl$51(), _el$221 = _el$220.firstChild, _el$222 = _el$221.firstChild, _el$223 = _el$222.nextSibling, _el$226 = _el$221.nextSibling.firstChild, _el$227 = _el$226.nextSibling, _el$228 = _el$227.nextSibling;\n insert(_el$222, () => `Severity: ${issue().severity}`);\n insert(_el$223, () => `Kind: ${issue().kind}`);\n insert(_el$221, createComponent(Show, {\n get when() {\n return issue().meetingId;\n },\n get children() {\n var _el$224 = _tmpl$49();\n insert(_el$224, () => `Meeting: ${issue().meetingId}`);\n return _el$224;\n }\n }), null);\n insert(_el$226, () => issue().title);\n insert(_el$227, () => issue().detail);\n insert(_el$228, createComponent(Show, {\n get when() {\n return issue().meetingId;\n },\n get children() {\n var _el$229 = _tmpl$50();\n _el$229.$$click = () => {\n props.onOpenMeeting(issue().meetingId);\n };\n return _el$229;\n }\n }), null);\n insert(_el$228, createComponent(Show, {\n get when() {\n return issue().recoverable;\n },\n get children() {\n var _el$230 = _tmpl$41();\n _el$230.$$click = () => {\n props.onRecover(issue().id);\n };\n return _el$230;\n }\n }), null);\n return _el$220;\n })()\n }), null);\n return _el$217;\n })();\n}\nfunction RunReviewPanel(props) {\n return (() => {\n var _el$231 = _tmpl$52();\n _el$231.firstChild;\n insert(_el$231, createComponent(Show, {\n get when() {\n return props.run;\n },\n get fallback() {\n return _tmpl$53();\n },\n children: (run) => (() => {\n var _el$234 = _tmpl$54(), _el$235 = _el$234.firstChild, _el$236 = _el$235.firstChild, _el$237 = _el$236.nextSibling, _el$238 = _el$237.nextSibling, _el$239 = _el$238.nextSibling, _el$241 = _el$235.nextSibling.firstChild, _el$242 = _el$241.nextSibling, _el$243 = _el$242.nextSibling, _el$244 = _el$243.firstChild;\n insert(_el$236, () => `Status: ${run().status}`);\n insert(_el$237, () => `Action: ${run().actionName}`);\n insert(_el$238, () => `Rule: ${run().ruleName}`);\n insert(_el$239, () => `Meeting: ${run().meetingId}`);\n insert(_el$241, () => run().title);\n insert(_el$242, () => run().prompt || run().result || run().error || run().eventKind);\n _el$244.$$click = () => {\n props.onOpenMeeting(run().meetingId);\n };\n insert(_el$243, createComponent(Show, {\n get when() {\n return run().status === \"pending\";\n },\n get children() {\n return [(() => {\n var _el$245 = _tmpl$35();\n _el$245.$$click = () => {\n props.onApprove(run().id);\n };\n return _el$245;\n })(), (() => {\n var _el$246 = _tmpl$36();\n _el$246.$$click = () => {\n props.onReject(run().id);\n };\n return _el$246;\n })()];\n }\n }), null);\n return _el$234;\n })()\n }), null);\n return _el$231;\n })();\n}\nfunction ArtefactReviewPanel(props) {\n return (() => {\n var _el$247 = _tmpl$55();\n _el$247.firstChild;\n insert(_el$247, createComponent(Show, {\n get when() {\n return props.artefact;\n },\n get fallback() {\n return (() => {\n var _el$249 = _tmpl$56();\n insert(_el$249, () => props.error || \"Select an automation artefact to review it.\");\n return _el$249;\n })();\n },\n children: (artefact) => (() => {\n var _el$250 = _tmpl$60(), _el$251 = _el$250.firstChild, _el$252 = _el$251.firstChild, _el$253 = _el$252.nextSibling, _el$254 = _el$253.nextSibling, _el$255 = _el$254.nextSibling, _el$285 = _el$251.nextSibling, _el$287 = _el$285.firstChild.nextSibling;\n insert(_el$252, () => `Status: ${artefact().status}`);\n insert(_el$253, () => `Kind: ${artefact().kind}`);\n insert(_el$254, () => `Meeting: ${artefact().meetingId}`);\n insert(_el$255, () => `Provider: ${artefact().provider}/${artefact().model}`);\n insert(_el$250, createComponent(Show, {\n get when() {\n return !props.error;\n },\n get fallback() {\n return (() => {\n var _el$288 = _tmpl$61();\n insert(_el$288, () => props.error);\n return _el$288;\n })();\n },\n get children() {\n var _el$256 = _tmpl$59(), _el$257 = _el$256.firstChild, _el$259 = _el$257.firstChild.nextSibling, _el$260 = _el$257.nextSibling, _el$262 = _el$260.firstChild.nextSibling, _el$264 = _el$262.firstChild.nextSibling, _el$265 = _el$262.nextSibling, _el$267 = _el$265.firstChild.nextSibling, _el$268 = _el$265.nextSibling, _el$270 = _el$268.firstChild.nextSibling, _el$271 = _el$268.nextSibling, _el$273 = _el$271.firstChild.nextSibling, _el$275 = _el$271.nextSibling.firstChild, _el$276 = _el$275.nextSibling, _el$277 = _el$276.nextSibling, _el$278 = _el$277.nextSibling;\n insert(_el$259, () => props.bundle?.meeting.noteMarkdown || \"(No existing meeting notes)\");\n _el$264.$$input = (event) => {\n props.onDraftTitleChange(event.currentTarget.value);\n };\n _el$267.$$input = (event) => {\n props.onDraftSummaryChange(event.currentTarget.value);\n };\n insert(_el$267, () => props.draftSummary);\n _el$270.$$input = (event) => {\n props.onDraftMarkdownChange(event.currentTarget.value);\n };\n insert(_el$270, () => props.draftMarkdown);\n _el$273.$$input = (event) => {\n props.onReviewNoteChange(event.currentTarget.value);\n };\n insert(_el$273, () => props.reviewNote);\n addEventListener(_el$275, \"click\", props.onSave, true);\n addEventListener(_el$276, \"click\", props.onApprove, true);\n addEventListener(_el$277, \"click\", props.onReject, true);\n addEventListener(_el$278, \"click\", props.onRerun, true);\n insert(_el$260, createComponent(Show, {\n get when() {\n return artefact().structured.actionItems.length > 0;\n },\n get children() {\n var _el$279 = _tmpl$57(), _el$281 = _el$279.firstChild.nextSibling;\n insert(_el$281, createComponent(For, {\n get each() {\n return artefact().structured.actionItems;\n },\n children: (item) => (() => {\n var _el$289 = _tmpl$63(), _el$290 = _el$289.firstChild;\n insert(_el$290, () => item.title);\n insert(_el$289, createComponent(Show, {\n get when() {\n return item.owner;\n },\n get children() {\n var _el$291 = _tmpl$62();\n insert(_el$291, () => ` • ${item.owner}`);\n return _el$291;\n }\n }), null);\n insert(_el$289, createComponent(Show, {\n get when() {\n return item.dueDate;\n },\n get children() {\n var _el$292 = _tmpl$62();\n insert(_el$292, () => ` • due ${item.dueDate}`);\n return _el$292;\n }\n }), null);\n return _el$289;\n })()\n }));\n return _el$279;\n }\n }), null);\n insert(_el$260, createComponent(Show, {\n get when() {\n return (artefact().structured.participantSummaries?.length ?? 0) > 0;\n },\n get children() {\n var _el$282 = _tmpl$58(), _el$284 = _el$282.firstChild.nextSibling;\n insert(_el$284, createComponent(For, {\n get each() {\n return artefact().structured.participantSummaries;\n },\n children: (summary) => (() => {\n var _el$293 = _tmpl$64(), _el$294 = _el$293.firstChild, _el$296 = _el$294.nextSibling;\n insert(_el$294, () => summary.speaker);\n insert(_el$293, createComponent(Show, {\n get when() {\n return summary.role;\n },\n get children() {\n var _el$295 = _tmpl$62();\n insert(_el$295, () => ` • ${summary.role}`);\n return _el$295;\n }\n }), _el$296);\n insert(_el$296, () => summary.summary);\n return _el$293;\n })()\n }));\n return _el$282;\n }\n }), null);\n createRenderEffect((_p$) => {\n var _v$1 = artefact().status === \"superseded\", _v$10 = artefact().status === \"superseded\";\n _v$1 !== _p$.e && (_el$276.disabled = _p$.e = _v$1);\n _v$10 !== _p$.t && (_el$277.disabled = _p$.t = _v$10);\n return _p$;\n }, {\n e: void 0,\n t: void 0\n });\n createRenderEffect(() => _el$264.value = props.draftTitle);\n return _el$256;\n }\n }), _el$285);\n insert(_el$287, createComponent(For, {\n get each() {\n return artefact().history.slice().reverse();\n },\n children: (entry) => (() => {\n var _el$297 = _tmpl$65(), _el$299 = _el$297.firstChild.firstChild, _el$300 = _el$299.nextSibling;\n insert(_el$299, () => entry.action);\n insert(_el$300, () => entry.at.slice(0, 19));\n insert(_el$297, createComponent(Show, {\n get when() {\n return entry.note;\n },\n get children() {\n var _el$301 = _tmpl$30();\n insert(_el$301, () => entry.note);\n return _el$301;\n }\n }), null);\n return _el$297;\n })()\n }));\n return _el$250;\n })()\n }), null);\n return _el$247;\n })();\n}\nfunction Workspace(props) {\n const parsedTab = () => parseWorkspaceTab(props.tab);\n const details = () => {\n if (!props.selectedMeeting) return null;\n return workspaceBody(props.bundle, props.selectedMeeting, parsedTab());\n };\n return [(() => {\n var _el$302 = _tmpl$66(), _el$303 = _el$302.firstChild;\n insert(_el$302, createComponent(For, {\n each: [\n \"notes\",\n \"transcript\",\n \"metadata\",\n \"raw\"\n ],\n children: (tab) => (() => {\n var _el$304 = _tmpl$67();\n _el$304.$$click = () => {\n props.onSelectTab(tab);\n };\n insert(_el$304, tab === \"notes\" ? \"Notes\" : tab === \"transcript\" ? \"Transcript\" : tab === \"metadata\" ? \"Metadata\" : \"Raw\");\n createRenderEffect(() => setAttribute(_el$304, \"data-selected\", parsedTab() === tab ? \"true\" : void 0));\n return _el$304;\n })()\n }), _el$303);\n return _el$302;\n })(), createComponent(Show, {\n get when() {\n return props.selectedMeeting;\n },\n get fallback() {\n return (() => {\n var _el$305 = _tmpl$61();\n insert(_el$305, () => props.detailError || \"Select a meeting to inspect its notes and transcript.\");\n return _el$305;\n })();\n },\n children: (meeting) => [(() => {\n var _el$306 = _tmpl$68(), _el$307 = _el$306.firstChild, _el$308 = _el$307.nextSibling, _el$309 = _el$308.nextSibling;\n insert(_el$307, () => `ID: ${meeting().meeting.id}`);\n insert(_el$308, () => `Source: ${meeting().meeting.noteContentSource}`);\n insert(_el$309, () => `Transcript: ${meeting().meeting.transcriptSegmentCount} segments`);\n return _el$306;\n })(), createComponent(Show, {\n get when() {\n return !props.detailError;\n },\n get fallback() {\n return (() => {\n var _el$318 = _tmpl$61();\n insert(_el$318, () => props.detailError);\n return _el$318;\n })();\n },\n get children() {\n var _el$310 = _tmpl$69(), _el$312 = _el$310.firstChild.firstChild, _el$314 = _el$312.firstChild.nextSibling, _el$316 = _el$312.nextSibling.firstChild, _el$317 = _el$316.nextSibling;\n insert(_el$314, () => metadataLines(meeting()));\n insert(_el$316, () => details()?.title);\n insert(_el$317, () => details()?.body);\n return _el$310;\n }\n })]\n })];\n}\ndelegateEvents([\n \"input\",\n \"keydown\",\n \"click\"\n]);\n//#endregion\n//#region src/web-app/harness-editor.tsx\n/** @jsxImportSource solid-js */\nvar _tmpl$$2 = /* @__PURE__ */ template(`<div class=empty>No harnesses configured yet. Create one here and save it to the shared harness file.`), _tmpl$2$2 = /* @__PURE__ */ template(`<section class=harness-panel><div class=jobs-panel__head><h3>Harness Editor</h3><p>Manage meeting playbooks, inspect why they match the selected meeting, and run the selected harness through the current pipeline before changing live automation.</p></div><div class=harness-grid><div class=detail-section><div class=harness-toolbar><button class=\"button button--primary\"type=button>New Harness</button><button class=\"button button--secondary\"type=button>Duplicate</button><button class=\"button button--secondary\"type=button>Remove</button><button class=\"button button--secondary\"type=button>Reload</button><button class=\"button button--primary\"type=button>Save Harnesses</button></div><div class=jobs-list>`), _tmpl$3$2 = /* @__PURE__ */ template(`<button class=\"job-card job-card--button\"type=button><div class=job-card__head><span class=job-card__title></span><span class=job-card__status></span></div><div class=job-card__meta>`), _tmpl$4$2 = /* @__PURE__ */ template(`<div class=detail-section><div class=empty>Select a harness to edit its prompts, rules, and test output.`), _tmpl$5$2 = /* @__PURE__ */ template(`<div class=auth-card__error>`), _tmpl$6$1 = /* @__PURE__ */ template(`<div class=detail-section><div class=harness-status-row><h2></h2><span class=state-badge></span></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Id</span><input class=\"field-input field-input--plain\"></label><label><span class=field-label>Name</span><input class=\"field-input field-input--plain\"></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Provider</span><select class=select><option value>Use default</option><option value=codex>Codex</option><option value=openai>OpenAI</option><option value=openrouter>OpenRouter</option></select></label><label><span class=field-label>Model</span><input class=\"field-input field-input--plain\"placeholder=\"gpt-5-codex or openai/gpt-5-mini\"></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Priority</span><input class=\"field-input field-input--plain\"placeholder=0></label><label><span class=field-label>Working Directory</span><input class=\"field-input field-input--plain\"placeholder=/path/to/project></label></div><div class=field-row><label><span class=field-label>Prompt</span><textarea class=\"review-textarea review-textarea--summary\"></textarea></label><label><span class=field-label>Prompt File</span><input class=\"field-input field-input--plain\"placeholder=./agents/customer-call/AGENT.md></label></div><div class=field-row><label><span class=field-label>System Prompt</span><textarea class=\"review-textarea review-textarea--summary\"></textarea></label><label><span class=field-label>System Prompt File</span><input class=\"field-input field-input--plain\"placeholder=./agents/customer-call/SYSTEM.md></label></div><div class=field-row><label><span class=field-label>Fallback Harnesses</span><input class=\"field-input field-input--plain\"placeholder=\"fallback-a, fallback-b\"></label></div><div class=field-row><label><span class=field-label>Event Kinds</span><input class=\"field-input field-input--plain\"placeholder=\"transcript.ready, meeting.created\"></label><label><span class=field-label>Meeting Ids</span><input class=\"field-input field-input--plain\"placeholder=doc-alpha-1111></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Folder Ids</span><input class=\"field-input field-input--plain\"placeholder=folder-team-1111></label><label><span class=field-label>Folder Names</span><input class=\"field-input field-input--plain\"placeholder=\"Team, Customers\"></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Tags</span><input class=\"field-input field-input--plain\"placeholder=\"customer, weekly\"></label><label><span class=field-label>Title Includes</span><input class=\"field-input field-input--plain\"placeholder=\"sync, review\"></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Title Regex</span><input class=\"field-input field-input--plain\"placeholder=customer.*sync></label><label><span class=field-label>Transcript Loaded</span><select class=select><option value>Either</option><option value=true>Must be loaded</option><option value=false>Must be missing</option></select></label></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Calendar Event Ids</span><input class=\"field-input field-input--plain\"placeholder=event-123></label><label><span class=field-label>Recurring Event Ids</span><input class=\"field-input field-input--plain\"placeholder=recurring-456></label></div><div class=detail-section><h3>Selected Meeting Test</h3><div class=job-card__meta></div><div class=\"field-row field-row--inline\"><label><span class=field-label>Pipeline Kind</span><select class=select><option value=notes>Notes</option><option value=enrichment>Enrichment</option></select></label><label><span class=field-label>Rule Explainer Event</span><input class=\"field-input field-input--plain\"disabled></label></div><div class=harness-toolbar><button class=\"button button--primary\"type=button>Test Harness`), _tmpl$7$1 = /* @__PURE__ */ template(`<div class=job-card__status>`), _tmpl$8$1 = /* @__PURE__ */ template(`<ul class=detail-list>`), _tmpl$9 = /* @__PURE__ */ template(`<li>`), _tmpl$0 = /* @__PURE__ */ template(`<div class=field-row><label><span class=field-label>Resolved Prompt Preview</span><textarea class=review-textarea readonly></textarea></label><label><span class=field-label>Raw Output</span><textarea class=\"review-textarea review-textarea--summary\"readonly>`), _tmpl$1 = /* @__PURE__ */ template(`<div class=harness-test-result><h3>Latest Test Run</h3><div class=job-card__meta>`), _tmpl$10 = /* @__PURE__ */ template(`<div class=field-row><label><span class=field-label>Structured Title</span><input class=\"field-input field-input--plain\"disabled></label><label><span class=field-label>Structured Summary</span><textarea class=\"review-textarea review-textarea--summary\"readonly>`);\nfunction csvValues(values) {\n return values?.join(\", \") ?? \"\";\n}\nfunction parseCsvValues(value) {\n const items = value.split(\",\").map((item) => item.trim()).filter(Boolean);\n return items.length > 0 ? [...new Set(items)] : void 0;\n}\nfunction compactMatch(match) {\n if (!match) return;\n const next = {\n calendarEventIds: match.calendarEventIds,\n eventKinds: match.eventKinds,\n folderIds: match.folderIds,\n folderNames: match.folderNames,\n meetingIds: match.meetingIds,\n recurringEventIds: match.recurringEventIds,\n tags: match.tags,\n titleIncludes: match.titleIncludes,\n titleMatches: match.titleMatches?.trim() || void 0,\n transcriptLoaded: match.transcriptLoaded\n };\n return Object.values(next).some((value) => Array.isArray(value) ? value.length > 0 : value !== void 0 && value !== \"\") ? next : void 0;\n}\nfunction sanitiseHarnessSeed(value) {\n return value.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"new-harness\";\n}\nfunction nextUniqueHarnessId(existing, seed) {\n const base = sanitiseHarnessSeed(seed);\n const ids = new Set(existing.map((harness) => harness.id));\n if (!ids.has(base)) return base;\n let index = 2;\n while (ids.has(`${base}-${index}`)) index += 1;\n return `${base}-${index}`;\n}\nfunction createHarnessTemplate(existing) {\n return {\n id: nextUniqueHarnessId(existing, \"new harness\"),\n name: \"New Harness\",\n prompt: \"Describe the structure and output you want for this meeting type.\",\n provider: \"codex\"\n };\n}\nfunction duplicateHarnessTemplate(existing, harness) {\n return {\n ...structuredClone(harness),\n id: nextUniqueHarnessId(existing, `${harness.id} copy`),\n name: `${harness.name} Copy`\n };\n}\nfunction patchHarness(harness, patch) {\n return {\n ...harness,\n ...patch,\n match: compactMatch(patch.match ?? harness.match)\n };\n}\nfunction patchHarnessMatch(harness, patch) {\n return patchHarness(harness, { match: {\n ...harness.match,\n ...patch\n } });\n}\nfunction HarnessEditorPanel(props) {\n const selectedExplanation = () => props.explanations.find((explanation) => explanation.harness.id === props.selectedHarnessId) ?? null;\n const selectedResult = () => props.testResult;\n const selectedHarness = () => props.selectedHarness;\n const updateHarness = (patch) => {\n const harness = selectedHarness();\n if (!harness) return;\n props.onChange(patchHarness(harness, patch));\n };\n const updateMatch = (patch) => {\n const harness = selectedHarness();\n if (!harness) return;\n props.onChange(patchHarnessMatch(harness, patch));\n };\n return (() => {\n var _el$ = _tmpl$2$2(), _el$3 = _el$.firstChild.nextSibling, _el$5 = _el$3.firstChild.firstChild, _el$6 = _el$5.firstChild, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling, _el$9 = _el$8.nextSibling, _el$0 = _el$9.nextSibling, _el$1 = _el$5.nextSibling;\n addEventListener(_el$6, \"click\", props.onNew, true);\n addEventListener(_el$7, \"click\", props.onDuplicate, true);\n addEventListener(_el$8, \"click\", props.onRemove, true);\n addEventListener(_el$9, \"click\", props.onReload, true);\n addEventListener(_el$0, \"click\", props.onSave, true);\n insert(_el$1, createComponent(For, {\n get each() {\n return props.harnesses;\n },\n children: (harness) => {\n const explanation = () => props.explanations.find((candidate) => candidate.harness.id === harness.id) ?? null;\n return (() => {\n var _el$11 = _tmpl$3$2(), _el$12 = _el$11.firstChild, _el$13 = _el$12.firstChild, _el$14 = _el$13.nextSibling, _el$15 = _el$12.nextSibling;\n _el$11.$$click = () => {\n props.onSelect(harness.id);\n };\n insert(_el$13, () => harness.name);\n insert(_el$14, () => explanation()?.matched ? \"Matched\" : \"Check\");\n insert(_el$15, () => harness.id, null);\n insert(_el$15, (() => {\n var _c$ = memo(() => !!harness.provider);\n return () => _c$() ? ` • ${harness.provider}` : \"\";\n })(), null);\n insert(_el$15, (() => {\n var _c$2 = memo(() => !!harness.model);\n return () => _c$2() ? `/${harness.model}` : \"\";\n })(), null);\n createRenderEffect((_p$) => {\n var _v$4 = props.selectedHarnessId === harness.id, _v$5 = explanation()?.matched ? \"completed\" : \"warning\";\n _v$4 !== _p$.e && setAttribute(_el$11, \"data-selected\", _p$.e = _v$4);\n _v$5 !== _p$.t && setAttribute(_el$14, \"data-status\", _p$.t = _v$5);\n return _p$;\n }, {\n e: void 0,\n t: void 0\n });\n return _el$11;\n })();\n }\n }), null);\n insert(_el$1, createComponent(Show, {\n get when() {\n return props.harnesses.length === 0;\n },\n get children() {\n return _tmpl$$2();\n }\n }), null);\n insert(_el$3, createComponent(Show, {\n get when() {\n return selectedHarness();\n },\n get fallback() {\n return _tmpl$4$2();\n },\n children: (harness) => (() => {\n var _el$17 = _tmpl$6$1(), _el$18 = _el$17.firstChild, _el$19 = _el$18.firstChild, _el$20 = _el$19.nextSibling, _el$21 = _el$18.nextSibling, _el$22 = _el$21.firstChild, _el$24 = _el$22.firstChild.nextSibling, _el$27 = _el$22.nextSibling.firstChild.nextSibling, _el$28 = _el$21.nextSibling, _el$29 = _el$28.firstChild, _el$31 = _el$29.firstChild.nextSibling, _el$34 = _el$29.nextSibling.firstChild.nextSibling, _el$35 = _el$28.nextSibling, _el$36 = _el$35.firstChild, _el$38 = _el$36.firstChild.nextSibling, _el$41 = _el$36.nextSibling.firstChild.nextSibling, _el$42 = _el$35.nextSibling, _el$43 = _el$42.firstChild, _el$45 = _el$43.firstChild.nextSibling, _el$48 = _el$43.nextSibling.firstChild.nextSibling, _el$49 = _el$42.nextSibling, _el$50 = _el$49.firstChild, _el$52 = _el$50.firstChild.nextSibling, _el$55 = _el$50.nextSibling.firstChild.nextSibling, _el$56 = _el$49.nextSibling, _el$59 = _el$56.firstChild.firstChild.nextSibling, _el$60 = _el$56.nextSibling, _el$61 = _el$60.firstChild, _el$63 = _el$61.firstChild.nextSibling, _el$66 = _el$61.nextSibling.firstChild.nextSibling, _el$67 = _el$60.nextSibling, _el$68 = _el$67.firstChild, _el$70 = _el$68.firstChild.nextSibling, _el$73 = _el$68.nextSibling.firstChild.nextSibling, _el$74 = _el$67.nextSibling, _el$75 = _el$74.firstChild, _el$77 = _el$75.firstChild.nextSibling, _el$80 = _el$75.nextSibling.firstChild.nextSibling, _el$81 = _el$74.nextSibling, _el$82 = _el$81.firstChild, _el$84 = _el$82.firstChild.nextSibling, _el$87 = _el$82.nextSibling.firstChild.nextSibling, _el$88 = _el$81.nextSibling, _el$89 = _el$88.firstChild, _el$91 = _el$89.firstChild.nextSibling, _el$94 = _el$89.nextSibling.firstChild.nextSibling, _el$95 = _el$88.nextSibling, _el$97 = _el$95.firstChild.nextSibling, _el$98 = _el$97.nextSibling, _el$99 = _el$98.firstChild, _el$101 = _el$99.firstChild.nextSibling, _el$104 = _el$99.nextSibling.firstChild.nextSibling, _el$106 = _el$98.nextSibling.firstChild;\n insert(_el$19, () => harness().name);\n insert(_el$20, () => props.dirty ? \"Unsaved changes\" : \"Saved\");\n _el$24.$$input = (event) => {\n updateHarness({ id: event.currentTarget.value.trim() });\n };\n _el$27.$$input = (event) => {\n updateHarness({ name: event.currentTarget.value });\n };\n _el$31.addEventListener(\"change\", (event) => {\n updateHarness({ provider: event.currentTarget.value === \"\" ? void 0 : event.currentTarget.value });\n });\n _el$34.$$input = (event) => {\n updateHarness({ model: event.currentTarget.value.trim() || void 0 });\n };\n _el$38.$$input = (event) => {\n const value = event.currentTarget.value.trim();\n updateHarness({ priority: value ? Number(value) : void 0 });\n };\n _el$41.$$input = (event) => {\n updateHarness({ cwd: event.currentTarget.value.trim() || void 0 });\n };\n _el$45.$$input = (event) => {\n updateHarness({ prompt: event.currentTarget.value.trim() || void 0 });\n };\n _el$48.$$input = (event) => {\n updateHarness({ promptFile: event.currentTarget.value.trim() || void 0 });\n };\n _el$52.$$input = (event) => {\n updateHarness({ systemPrompt: event.currentTarget.value.trim() || void 0 });\n };\n _el$55.$$input = (event) => {\n updateHarness({ systemPromptFile: event.currentTarget.value.trim() || void 0 });\n };\n _el$59.$$input = (event) => {\n updateHarness({ fallbackHarnessIds: parseCsvValues(event.currentTarget.value) });\n };\n _el$63.$$input = (event) => {\n updateMatch({ eventKinds: parseCsvValues(event.currentTarget.value) });\n };\n _el$66.$$input = (event) => {\n updateMatch({ meetingIds: parseCsvValues(event.currentTarget.value) });\n };\n _el$70.$$input = (event) => {\n updateMatch({ folderIds: parseCsvValues(event.currentTarget.value) });\n };\n _el$73.$$input = (event) => {\n updateMatch({ folderNames: parseCsvValues(event.currentTarget.value) });\n };\n _el$77.$$input = (event) => {\n updateMatch({ tags: parseCsvValues(event.currentTarget.value) });\n };\n _el$80.$$input = (event) => {\n updateMatch({ titleIncludes: parseCsvValues(event.currentTarget.value) });\n };\n _el$84.$$input = (event) => {\n updateMatch({ titleMatches: event.currentTarget.value.trim() || void 0 });\n };\n _el$87.addEventListener(\"change\", (event) => {\n updateMatch({ transcriptLoaded: event.currentTarget.value === \"\" ? void 0 : event.currentTarget.value === \"true\" });\n });\n _el$91.$$input = (event) => {\n updateMatch({ calendarEventIds: parseCsvValues(event.currentTarget.value) });\n };\n _el$94.$$input = (event) => {\n updateMatch({ recurringEventIds: parseCsvValues(event.currentTarget.value) });\n };\n insert(_el$97, (() => {\n var _c$3 = memo(() => !!props.selectedMeeting);\n return () => _c$3() ? `Run ${harness().name} against ${props.selectedMeeting.meeting.title || props.selectedMeeting.meeting.id}.` : \"Select a meeting to explain and test this harness.\";\n })());\n _el$101.addEventListener(\"change\", (event) => {\n props.onTestKindChange(event.currentTarget.value);\n });\n addEventListener(_el$106, \"click\", props.onTest, true);\n insert(_el$95, createComponent(Show, {\n get when() {\n return selectedExplanation();\n },\n children: (explanation) => [(() => {\n var _el$108 = _tmpl$7$1();\n insert(_el$108, () => explanation().matched ? \"Matched selected meeting\" : \"Did not match selected meeting\");\n createRenderEffect(() => setAttribute(_el$108, \"data-status\", explanation().matched ? \"completed\" : \"warning\"));\n return _el$108;\n })(), (() => {\n var _el$109 = _tmpl$8$1();\n insert(_el$109, createComponent(For, {\n get each() {\n return explanation().reasons;\n },\n children: (reason) => (() => {\n var _el$110 = _tmpl$9();\n insert(_el$110, reason);\n return _el$110;\n })()\n }));\n return _el$109;\n })()]\n }), null);\n insert(_el$95, createComponent(Show, {\n get when() {\n return selectedResult();\n },\n children: (result) => (() => {\n var _el$111 = _tmpl$1(), _el$113 = _el$111.firstChild.nextSibling;\n insert(_el$113, () => result().status, null);\n insert(_el$113, (() => {\n var _c$4 = memo(() => !!result().provider);\n return () => _c$4() ? ` • ${result().provider}` : \"\";\n })(), null);\n insert(_el$113, (() => {\n var _c$5 = memo(() => !!result().model);\n return () => _c$5() ? `/${result().model}` : \"\";\n })(), null);\n insert(_el$113, (() => {\n var _c$6 = memo(() => !!result().parseMode);\n return () => _c$6() ? ` • ${result().parseMode}` : \"\";\n })(), null);\n insert(_el$111, createComponent(Show, {\n get when() {\n return result().status === \"failed\";\n },\n get children() {\n var _el$114 = _tmpl$5$2();\n insert(_el$114, () => result().error);\n return _el$114;\n }\n }), null);\n insert(_el$111, createComponent(Show, {\n get when() {\n return result().status === \"completed\";\n },\n get children() {\n return [(() => {\n var _el$115 = _tmpl$0(), _el$116 = _el$115.firstChild, _el$118 = _el$116.firstChild.nextSibling, _el$121 = _el$116.nextSibling.firstChild.nextSibling;\n createRenderEffect(() => _el$118.value = result().prompt);\n createRenderEffect(() => _el$121.value = result().rawOutput ?? \"\");\n return _el$115;\n })(), createComponent(Show, {\n get when() {\n return result().structured;\n },\n children: (structured) => (() => {\n var _el$122 = _tmpl$10(), _el$123 = _el$122.firstChild, _el$125 = _el$123.firstChild.nextSibling, _el$128 = _el$123.nextSibling.firstChild.nextSibling;\n createRenderEffect(() => _el$125.value = structured().title);\n createRenderEffect(() => _el$128.value = structured().summary ?? \"\");\n return _el$122;\n })()\n })];\n }\n }), null);\n return _el$111;\n })()\n }), null);\n insert(_el$17, createComponent(Show, {\n get when() {\n return props.error;\n },\n get children() {\n var _el$107 = _tmpl$5$2();\n insert(_el$107, () => props.error);\n return _el$107;\n }\n }), null);\n createRenderEffect((_p$) => {\n var _v$6 = props.dirty ? \"busy\" : \"ok\", _v$7 = !props.selectedMeeting;\n _v$6 !== _p$.e && setAttribute(_el$20, \"data-tone\", _p$.e = _v$6);\n _v$7 !== _p$.t && (_el$106.disabled = _p$.t = _v$7);\n return _p$;\n }, {\n e: void 0,\n t: void 0\n });\n createRenderEffect(() => _el$24.value = harness().id);\n createRenderEffect(() => _el$27.value = harness().name);\n createRenderEffect(() => _el$31.value = harness().provider ?? \"\");\n createRenderEffect(() => _el$34.value = harness().model ?? \"\");\n createRenderEffect(() => _el$38.value = harness().priority?.toString() ?? \"\");\n createRenderEffect(() => _el$41.value = harness().cwd ?? \"\");\n createRenderEffect(() => _el$45.value = harness().prompt ?? \"\");\n createRenderEffect(() => _el$48.value = harness().promptFile ?? \"\");\n createRenderEffect(() => _el$52.value = harness().systemPrompt ?? \"\");\n createRenderEffect(() => _el$55.value = harness().systemPromptFile ?? \"\");\n createRenderEffect(() => _el$59.value = csvValues(harness().fallbackHarnessIds));\n createRenderEffect(() => _el$63.value = csvValues(harness().match?.eventKinds));\n createRenderEffect(() => _el$66.value = csvValues(harness().match?.meetingIds));\n createRenderEffect(() => _el$70.value = csvValues(harness().match?.folderIds));\n createRenderEffect(() => _el$73.value = csvValues(harness().match?.folderNames));\n createRenderEffect(() => _el$77.value = csvValues(harness().match?.tags));\n createRenderEffect(() => _el$80.value = csvValues(harness().match?.titleIncludes));\n createRenderEffect(() => _el$84.value = harness().match?.titleMatches ?? \"\");\n createRenderEffect(() => _el$87.value = harness().match?.transcriptLoaded === void 0 ? \"\" : String(harness().match?.transcriptLoaded));\n createRenderEffect(() => _el$91.value = csvValues(harness().match?.calendarEventIds));\n createRenderEffect(() => _el$94.value = csvValues(harness().match?.recurringEventIds));\n createRenderEffect(() => _el$101.value = props.testKind);\n createRenderEffect(() => _el$104.value = props.explanationEventKind ?? \"Select a meeting\");\n return _el$17;\n })()\n }), null);\n createRenderEffect((_p$) => {\n var _v$ = !selectedHarness(), _v$2 = !selectedHarness(), _v$3 = !props.dirty;\n _v$ !== _p$.e && (_el$7.disabled = _p$.e = _v$);\n _v$2 !== _p$.t && (_el$8.disabled = _p$.t = _v$2);\n _v$3 !== _p$.a && (_el$0.disabled = _p$.a = _v$3);\n return _p$;\n }, {\n e: void 0,\n t: void 0,\n a: void 0\n });\n return _el$;\n })();\n}\ndelegateEvents([\"click\", \"input\"]);\n//#endregion\n//#region src/agent-defaults.ts\nvar defaultGranolaAgentModels = {\n codex: \"gpt-5-codex\",\n openai: \"gpt-5-mini\",\n openrouter: \"openai/gpt-5-mini\"\n};\nfunction defaultGranolaAgentModel(provider, explicitModel) {\n return explicitModel?.trim() || defaultGranolaAgentModels[provider];\n}\nfunction granolaAgentProviderLabel(provider) {\n switch (provider) {\n case \"codex\": return \"Codex\";\n case \"openai\": return \"OpenAI\";\n case \"openrouter\": return \"OpenRouter\";\n }\n}\n//#endregion\n//#region src/web-app/onboarding.tsx\n/** @jsxImportSource solid-js */\nvar _tmpl$$1 = /* @__PURE__ */ template(`<section class=onboarding-panel><div class=onboarding-panel__hero><div><p class=onboarding-panel__eyebrow>First-Run Setup</p><h2>Connect Granola, import your meetings, and pick an agent.</h2><p>The toolkit already has the core automation engine. This flow just gets you to the first useful setup without dumping every advanced panel on screen at once.</p></div><div class=onboarding-panel__summary><div class=job-card__status></div><div class=auth-card__meta></div></div></div><div class=onboarding-steps>`), _tmpl$2$1 = /* @__PURE__ */ template(`<div class=auth-card__meta>Single-folder workspace detected: `), _tmpl$3$1 = /* @__PURE__ */ template(`<div class=onboarding-step__body><input class=input placeholder=grn_... type=password><div class=toolbar-actions><button class=\"button button--primary\"type=button>Save API key</button><button class=\"button button--secondary\"type=button>Import desktop session`), _tmpl$4$1 = /* @__PURE__ */ template(`<div class=onboarding-step__body><button class=\"button button--primary\"type=button>Import meetings now`), _tmpl$5$1 = /* @__PURE__ */ template(`<div class=onboarding-step__body><div class=onboarding-providers></div><div class=auth-card__meta>Starter pipeline: transcript-ready meetings will generate reviewable notes with <!>.</div><button class=\"button button--primary\"type=button>Create starter pipeline`), _tmpl$6 = /* @__PURE__ */ template(`<article class=onboarding-step><div class=onboarding-step__head><div><span class=onboarding-step__number>Step </span><h3></h3></div><div class=job-card__status></div></div><p>`), _tmpl$7 = /* @__PURE__ */ template(`<div class=auth-card__meta>`), _tmpl$8 = /* @__PURE__ */ template(`<button class=onboarding-provider type=button><span class=onboarding-provider__title></span><span class=onboarding-provider__body>`);\nvar starterHarnessId = \"starter-meeting-notes\";\nvar starterRuleId = \"starter-meeting-notes-review\";\nvar starterRuleActionId = \"starter-meeting-notes-pipeline\";\nvar providerOptions = [\n {\n description: \"Use the local Codex CLI and your ChatGPT subscription.\",\n provider: \"codex\"\n },\n {\n description: \"Use an OPENROUTER_API_KEY and route to your preferred model.\",\n provider: \"openrouter\"\n },\n {\n description: \"Use an OPENAI_API_KEY with direct OpenAI access.\",\n provider: \"openai\"\n }\n];\nfunction suggestedFolderLabel(folders) {\n if (folders.length === 1) return folders[0]?.name;\n}\nfunction deriveOnboardingState(input) {\n const auth = input.appState?.auth;\n const connected = Boolean(auth?.apiKeyAvailable || auth?.storedSessionAvailable);\n const synced = Boolean(input.appState?.sync.lastCompletedAt);\n const pipelineReady = input.harnesses.length > 0 && input.automationRuleCount > 0;\n const syncedMeetingCount = input.appState?.documents.count ?? input.meetingsLoadedCount;\n const stepCards = [\n {\n body: \"Store a Granola API key for the toolkit. Desktop session import stays available as a fallback.\",\n complete: connected,\n cta: connected ? void 0 : \"Save API key\",\n detail: connected ? `Connected via ${auth?.mode === \"api-key\" ? \"API key\" : auth?.mode === \"stored-session\" ? \"stored session\" : \"fallback auth\"}.` : \"Recommended: use a Granola Personal API key from Settings → API.\",\n title: \"Connect Granola\"\n },\n {\n body: \"Run the first sync so the local index has meetings, folders, and transcript-ready events.\",\n complete: synced,\n cta: synced ? void 0 : \"Import meetings\",\n detail: synced ? `${syncedMeetingCount} meetings indexed locally.` : \"This builds the local meeting index the workspace and automation layer rely on.\",\n title: \"Import Meetings\"\n },\n {\n body: \"Choose the AI agent you want to use by default, then seed a starter reviewable notes pipeline.\",\n complete: pipelineReady,\n cta: pipelineReady ? void 0 : \"Create starter pipeline\",\n detail: pipelineReady ? \"Starter harnesses and automation rules are ready.\" : \"The starter pipeline generates reviewable meeting notes on transcript-ready events.\",\n title: \"Choose An Agent\"\n }\n ];\n return {\n complete: connected && synced && pipelineReady,\n connected,\n pipelineReady,\n stepCards,\n synced,\n syncedMeetingCount\n };\n}\nfunction upsertById(items, nextItem) {\n return [...items.filter((item) => item.id !== nextItem.id), nextItem];\n}\nfunction createStarterHarness(provider) {\n return {\n id: starterHarnessId,\n match: { transcriptLoaded: true },\n model: defaultGranolaAgentModel(provider),\n name: \"Starter Meeting Notes\",\n priority: 10,\n prompt: [\n \"Turn this meeting into concise, high-signal notes for internal follow-up.\",\n \"\",\n \"Requirements:\",\n \"- lead with the most important outcome, not a play-by-play recap\",\n \"- capture decisions, action items, open questions, and risks\",\n \"- assign owners when the transcript makes them clear\",\n \"- call out uncertainty explicitly instead of inventing missing detail\",\n \"- optimise for a team reading this five minutes after the meeting ends\"\n ].join(\"\\n\"),\n provider\n };\n}\nfunction createStarterRule() {\n return {\n actions: [{\n approvalMode: \"manual\",\n harnessId: starterHarnessId,\n id: starterRuleActionId,\n kind: \"agent\",\n name: \"Generate starter meeting notes\",\n pipeline: { kind: \"notes\" }\n }],\n id: starterRuleId,\n name: \"Review starter notes when a transcript is ready\",\n when: {\n eventKinds: [\"transcript.ready\"],\n transcriptLoaded: true\n }\n };\n}\nfunction buildStarterPipeline(existing) {\n const harness = createStarterHarness(existing.provider);\n const rule = createStarterRule();\n return {\n harnesses: upsertById(existing.harnesses, harness),\n rule,\n rules: upsertById(existing.rules, rule)\n };\n}\nfunction OnboardingPanel(props) {\n const dominantFolder = () => suggestedFolderLabel(props.folders);\n return (() => {\n var _el$ = _tmpl$$1(), _el$2 = _el$.firstChild, _el$4 = _el$2.firstChild.nextSibling, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$2.nextSibling;\n insert(_el$5, () => props.state.complete ? \"Ready\" : \"Setup in progress\");\n insert(_el$6, (() => {\n var _c$ = memo(() => !!props.state.synced);\n return () => _c$() ? `${props.state.syncedMeetingCount} meetings indexed` : \"No local sync yet\";\n })());\n insert(_el$4, createComponent(Show, {\n get when() {\n return dominantFolder();\n },\n children: (folderName) => (() => {\n var _el$8 = _tmpl$2$1();\n _el$8.firstChild;\n insert(_el$8, folderName, null);\n return _el$8;\n })()\n }), null);\n insert(_el$7, createComponent(For, {\n get each() {\n return props.state.stepCards;\n },\n children: (step, index) => (() => {\n var _el$0 = _tmpl$6(), _el$1 = _el$0.firstChild, _el$10 = _el$1.firstChild, _el$11 = _el$10.firstChild;\n _el$11.firstChild;\n var _el$13 = _el$11.nextSibling, _el$14 = _el$10.nextSibling, _el$15 = _el$1.nextSibling;\n insert(_el$11, () => index() + 1, null);\n insert(_el$13, () => step.title);\n insert(_el$14, () => step.complete ? \"Done\" : \"Next\");\n insert(_el$15, () => step.body);\n insert(_el$0, createComponent(Show, {\n get when() {\n return step.detail;\n },\n children: (detail) => (() => {\n var _el$31 = _tmpl$7();\n insert(_el$31, detail);\n return _el$31;\n })()\n }), null);\n insert(_el$0, createComponent(Show, {\n get when() {\n return memo(() => index() === 0)() && !step.complete;\n },\n get children() {\n var _el$16 = _tmpl$3$1(), _el$17 = _el$16.firstChild, _el$19 = _el$17.nextSibling.firstChild, _el$20 = _el$19.nextSibling;\n _el$17.$$input = (event) => {\n props.onApiKeyDraftChange(event.currentTarget.value);\n };\n addEventListener(_el$19, \"click\", props.onSaveApiKey, true);\n addEventListener(_el$20, \"click\", props.onImportDesktopSession, true);\n createRenderEffect(() => _el$20.disabled = !props.auth?.supabaseAvailable);\n createRenderEffect(() => _el$17.value = props.apiKeyDraft);\n return _el$16;\n }\n }), null);\n insert(_el$0, createComponent(Show, {\n get when() {\n return memo(() => index() === 1)() && !step.complete;\n },\n get children() {\n var _el$21 = _tmpl$4$1(), _el$22 = _el$21.firstChild;\n addEventListener(_el$22, \"click\", props.onRunSync, true);\n createRenderEffect(() => _el$22.disabled = !props.state.connected);\n return _el$21;\n }\n }), null);\n insert(_el$0, createComponent(Show, {\n get when() {\n return memo(() => index() === 2)() && !step.complete;\n },\n get children() {\n var _el$23 = _tmpl$5$1(), _el$24 = _el$23.firstChild, _el$25 = _el$24.nextSibling, _el$29 = _el$25.firstChild.nextSibling;\n _el$29.nextSibling;\n var _el$30 = _el$25.nextSibling;\n insert(_el$24, createComponent(For, {\n each: providerOptions,\n children: (option) => (() => {\n var _el$32 = _tmpl$8(), _el$33 = _el$32.firstChild, _el$34 = _el$33.nextSibling;\n _el$32.$$click = () => {\n props.onSelectProvider(option.provider);\n };\n insert(_el$33, () => granolaAgentProviderLabel(option.provider));\n insert(_el$34, () => option.description);\n createRenderEffect(() => setAttribute(_el$32, \"data-selected\", props.preferredProvider === option.provider));\n return _el$32;\n })()\n }));\n insert(_el$25, () => granolaAgentProviderLabel(props.preferredProvider), _el$29);\n addEventListener(_el$30, \"click\", props.onCreateStarterPipeline, true);\n createRenderEffect(() => _el$30.disabled = !props.state.synced);\n return _el$23;\n }\n }), null);\n createRenderEffect((_p$) => {\n var _v$ = step.complete, _v$2 = step.complete ? \"completed\" : \"warning\";\n _v$ !== _p$.e && setAttribute(_el$0, \"data-complete\", _p$.e = _v$);\n _v$2 !== _p$.t && setAttribute(_el$14, \"data-status\", _p$.t = _v$2);\n return _p$;\n }, {\n e: void 0,\n t: void 0\n });\n return _el$0;\n })()\n }));\n createRenderEffect(() => setAttribute(_el$5, \"data-status\", props.state.complete ? \"completed\" : \"warning\"));\n return _el$;\n })();\n}\ndelegateEvents([\"input\", \"click\"]);\n//#endregion\n//#region src/web-app/App.tsx\n/** @jsxImportSource solid-js */\nvar _tmpl$ = /* @__PURE__ */ template(`<section class=control-deck__panel>`), _tmpl$2 = /* @__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>Clear Filters</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.</p></section><section class=control-deck><div class=control-deck__tabs></div><div class=control-deck__body>`), _tmpl$3 = /* @__PURE__ */ template(`<div class=\"shell shell--onboarding\"><main class=\"pane detail detail--onboarding\">`), _tmpl$4 = /* @__PURE__ */ template(`<section class=jobs-panel><div class=auth-card><div class=\"auth-card__meta auth-card__error\">`), _tmpl$5 = /* @__PURE__ */ template(`<button class=workspace-tab type=button>`);\nfunction browserConfig() {\n return { passwordRequired: Boolean(window.__GRANOLA_SERVER__?.passwordRequired) };\n}\nasync function requestJson(path, init) {\n const response = await fetch(path, init);\n const payload = await response.json().catch(() => ({}));\n if (!response.ok) {\n const error = typeof payload.error === \"string\" && payload.error.trim() ? payload.error : response.statusText || \"Request failed\";\n throw new Error(error);\n }\n return payload;\n}\nfunction App() {\n const startup = startupSelectionFromSearch(window.location.search);\n const initialPreferences = parseWorkspacePreferences(window.localStorage.getItem(granolaWebWorkspaceStorageKey));\n const [state, setState] = createStore({\n apiKeyDraft: \"\",\n automationArtefactDraftMarkdown: \"\",\n automationArtefactDraftSummary: \"\",\n automationArtefactDraftTitle: \"\",\n automationArtefactError: \"\",\n automationArtefacts: [],\n automationRules: [],\n appState: null,\n automationRuns: [],\n detailError: \"\",\n folderError: \"\",\n folders: [],\n harnessDirty: false,\n harnessError: \"\",\n harnessExplainEventKind: null,\n harnessExplanations: [],\n harnessTestKind: \"notes\",\n harnessTestResult: null,\n harnesses: [],\n listError: \"\",\n meetingSource: \"live\",\n meetings: [],\n processingIssueError: \"\",\n processingIssues: [],\n preferredProvider: \"openrouter\",\n quickOpen: \"\",\n recentMeetings: initialPreferences.recentMeetings,\n savedFilters: initialPreferences.savedFilters,\n search: \"\",\n selectedAutomationArtefactId: null,\n selectedFolderId: startup.folderId || null,\n selectedHarnessId: null,\n selectedMeetingBundle: null,\n selectedMeetingId: startup.meetingId || null,\n selectedMeeting: null,\n selectedReviewInboxKey: null,\n reviewNote: \"\",\n controlPanelTab: \"overview\",\n serverLocked: browserConfig().passwordRequired,\n serverPassword: \"\",\n sort: \"updated-desc\",\n statusLabel: browserConfig().passwordRequired ? \"Server locked\" : \"Connecting…\",\n statusTone: browserConfig().passwordRequired ? \"error\" : \"idle\",\n updatedFrom: \"\",\n updatedTo: \"\",\n workspaceTab: parseWorkspaceTab(startup.workspaceTab)\n });\n let client = null;\n let automationPanelsHydrated = false;\n let unsubscribe;\n const setStatus = (label, tone = \"idle\") => {\n setState({\n statusLabel: label,\n statusTone: tone\n });\n };\n const updatePreferences = (updater) => {\n const next = updater({\n recentMeetings: state.recentMeetings,\n savedFilters: state.savedFilters\n });\n window.localStorage.setItem(granolaWebWorkspaceStorageKey, serialiseWorkspacePreferences(next));\n setState(\"recentMeetings\", next.recentMeetings);\n setState(\"savedFilters\", next.savedFilters);\n };\n const mergeAuthState = async (authState) => {\n if (!client) return;\n const nextState = client.getState();\n if (authState) {\n setState(\"appState\", {\n ...nextState,\n auth: authState\n });\n return;\n }\n try {\n setState(\"appState\", {\n ...nextState,\n auth: await client.inspectAuth()\n });\n } catch {\n setState(\"appState\", nextState);\n }\n };\n const detachClient = async () => {\n unsubscribe?.();\n unsubscribe = void 0;\n if (client) {\n await client.close().catch(() => void 0);\n client = null;\n }\n };\n const attachClient = async () => {\n await detachClient();\n client = await createGranolaServerClient(window.location.origin);\n setState(\"appState\", client.getState());\n unsubscribe = client.subscribe((event) => {\n setState(\"appState\", event.state);\n });\n await mergeAuthState();\n };\n const loadFolders = async (refresh = false) => {\n if (!client) return;\n try {\n setState(\"folderError\", \"\");\n const result = await client.listFolders({\n forceRefresh: refresh,\n limit: 100\n });\n setState(\"folders\", result.folders);\n if (state.selectedFolderId && !result.folders.some((folder) => folder.id === state.selectedFolderId)) setState(\"selectedFolderId\", null);\n } catch (error) {\n setState(\"folderError\", error instanceof Error ? error.message : String(error));\n setState(\"folders\", []);\n setState(\"selectedFolderId\", null);\n }\n };\n const loadAutomationRuns = async () => {\n if (!client) return;\n try {\n setState(\"automationRuns\", (await client.listAutomationRuns({ limit: 20 })).runs);\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n }\n };\n const loadAutomationRules = async () => {\n if (!client) return;\n try {\n setState(\"automationRules\", (await client.listAutomationRules()).rules);\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setState(\"automationRules\", []);\n }\n };\n const applySelectedArtefactDrafts = (artefact) => {\n setState(\"selectedAutomationArtefactId\", artefact?.id ?? null);\n setState(\"automationArtefactDraftTitle\", artefact?.structured.title ?? \"\");\n setState(\"automationArtefactDraftSummary\", artefact?.structured.summary ?? \"\");\n setState(\"automationArtefactDraftMarkdown\", artefact?.structured.markdown ?? \"\");\n setState(\"reviewNote\", \"\");\n };\n const syncSelectedArtefact = (artefacts, options = {}) => {\n applySelectedArtefactDrafts((options.preferredId ? artefacts.find((candidate) => candidate.id === options.preferredId) : void 0) ?? (options.preferredMeetingId ? artefacts.find((candidate) => candidate.meetingId === options.preferredMeetingId && candidate.status === \"generated\") : void 0) ?? artefacts.find((candidate) => candidate.status === \"generated\") ?? artefacts[0] ?? null);\n };\n const loadAutomationArtefacts = async (options = {}) => {\n if (!client) return;\n try {\n setState(\"automationArtefactError\", \"\");\n const result = await client.listAutomationArtefacts({ limit: 30 });\n setState(\"automationArtefacts\", result.artefacts);\n syncSelectedArtefact(result.artefacts, {\n preferredId: options.preferredId ?? state.selectedAutomationArtefactId,\n preferredMeetingId: options.preferredMeetingId ?? state.selectedMeetingId\n });\n } catch (error) {\n setState(\"automationArtefactError\", error instanceof Error ? error.message : String(error));\n setState(\"automationArtefacts\", []);\n syncSelectedArtefact([]);\n }\n };\n const loadProcessingIssues = async () => {\n if (!client) return;\n try {\n setState(\"processingIssueError\", \"\");\n setState(\"processingIssues\", (await client.listProcessingIssues({ limit: 20 })).issues);\n } catch (error) {\n setState(\"processingIssueError\", error instanceof Error ? error.message : String(error));\n setState(\"processingIssues\", []);\n }\n };\n const selectHarnessId = (harnesses, preferredId) => {\n if (preferredId && harnesses.some((harness) => harness.id === preferredId)) return preferredId;\n return harnesses[0]?.id ?? null;\n };\n const selectedHarness = () => state.harnesses.find((harness) => harness.id === state.selectedHarnessId) ?? null;\n const loadHarnessExplanations = async (meetingId) => {\n if (!client || !meetingId) {\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n return;\n }\n try {\n const result = await client.explainAgentHarnesses(meetingId);\n setState(\"harnessExplainEventKind\", result.eventKind);\n setState(\"harnessExplanations\", result.harnesses);\n } catch (error) {\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n setState(\"harnessError\", error instanceof Error ? error.message : String(error));\n }\n };\n const loadHarnesses = async (preferredId) => {\n if (!client) return;\n try {\n setState(\"harnessError\", \"\");\n const result = await client.listAgentHarnesses();\n const nextSelectedHarnessId = selectHarnessId(result.harnesses, preferredId ?? state.selectedHarnessId);\n setState(\"harnesses\", result.harnesses);\n setState(\"selectedHarnessId\", nextSelectedHarnessId);\n const nextPreferredProvider = result.harnesses.find((harness) => harness.provider)?.provider;\n if (nextPreferredProvider) setState(\"preferredProvider\", nextPreferredProvider);\n setState(\"harnessDirty\", false);\n setState(\"harnessTestResult\", null);\n await loadHarnessExplanations(state.selectedMeetingId);\n } catch (error) {\n setState(\"harnessError\", error instanceof Error ? error.message : String(error));\n setState(\"harnesses\", []);\n setState(\"selectedHarnessId\", null);\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n setState(\"harnessTestResult\", null);\n }\n };\n const reviewInboxItems = () => buildGranolaReviewInbox({\n artefacts: state.automationArtefacts,\n issues: state.processingIssues,\n runs: state.automationRuns\n });\n const reviewInboxSummary = () => summariseGranolaReviewInbox(reviewInboxItems());\n const selectedReviewInboxItem = () => reviewInboxItems().find((item) => item.key === state.selectedReviewInboxKey) ?? reviewInboxItems()[0] ?? null;\n const selectedReviewIssue = () => {\n const item = selectedReviewInboxItem();\n return item?.kind === \"issue\" ? item.issue : null;\n };\n const selectedReviewRun = () => {\n const item = selectedReviewInboxItem();\n return item?.kind === \"run\" ? item.run : null;\n };\n const selectedReviewArtefact = () => {\n const item = selectedReviewInboxItem();\n return item?.kind === \"artefact\" ? item.artefact : selectedAutomationArtefact();\n };\n const updateHarness = (nextHarness) => {\n setState(\"harnesses\", state.harnesses.map((harness) => harness.id === nextHarness.id ? nextHarness : harness));\n setState(\"selectedHarnessId\", nextHarness.id);\n setState(\"harnessDirty\", true);\n setState(\"harnessTestResult\", null);\n };\n const createHarness = () => {\n const nextHarness = createHarnessTemplate(state.harnesses);\n setState(\"harnesses\", [...state.harnesses, nextHarness]);\n setState(\"selectedHarnessId\", nextHarness.id);\n setState(\"harnessDirty\", true);\n setState(\"harnessTestResult\", null);\n };\n const duplicateHarness = () => {\n const harness = selectedHarness();\n if (!harness) return;\n const nextHarness = duplicateHarnessTemplate(state.harnesses, harness);\n setState(\"harnesses\", [...state.harnesses, nextHarness]);\n setState(\"selectedHarnessId\", nextHarness.id);\n setState(\"harnessDirty\", true);\n setState(\"harnessTestResult\", null);\n };\n const removeHarness = () => {\n if (!state.selectedHarnessId) return;\n const nextHarnesses = state.harnesses.filter((harness) => harness.id !== state.selectedHarnessId);\n setState(\"harnesses\", nextHarnesses);\n setState(\"selectedHarnessId\", selectHarnessId(nextHarnesses, null));\n setState(\"harnessDirty\", true);\n setState(\"harnessTestResult\", null);\n };\n const saveHarnesses = async () => {\n if (!client) return;\n setStatus(\"Saving harnesses…\", \"busy\");\n try {\n const result = await client.saveAgentHarnesses(state.harnesses);\n const nextSelectedHarnessId = selectHarnessId(result.harnesses, state.selectedHarnessId);\n setState(\"harnesses\", result.harnesses);\n setState(\"selectedHarnessId\", nextSelectedHarnessId);\n const nextPreferredProvider = result.harnesses.find((harness) => harness.provider)?.provider;\n if (nextPreferredProvider) setState(\"preferredProvider\", nextPreferredProvider);\n setState(\"harnessDirty\", false);\n setState(\"harnessError\", \"\");\n await loadHarnessExplanations(state.selectedMeetingId);\n setStatus(\"Harnesses saved\", \"ok\");\n } catch (error) {\n setState(\"harnessError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Harness save failed\", \"error\");\n }\n };\n const reloadHarnesses = async () => {\n setStatus(\"Reloading harnesses…\", \"busy\");\n await loadHarnesses(state.selectedHarnessId);\n setStatus(\"Harnesses reloaded\", \"ok\");\n };\n const createStarterPipeline = async () => {\n if (!client) return;\n setStatus(\"Creating starter pipeline…\", \"busy\");\n try {\n const [currentHarnesses, currentRules] = await Promise.all([client.listAgentHarnesses(), client.listAutomationRules()]);\n const starter = buildStarterPipeline({\n harnesses: currentHarnesses.harnesses,\n provider: state.preferredProvider,\n rules: currentRules.rules\n });\n await client.saveAgentHarnesses(starter.harnesses);\n await client.saveAutomationRules(starter.rules);\n await refreshAll();\n setStatus(\"Starter pipeline ready\", \"ok\");\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Starter pipeline setup failed\", \"error\");\n }\n };\n const testHarness = async () => {\n if (!client) return;\n const harness = selectedHarness();\n if (!harness) {\n setStatus(\"Select a harness first\", \"error\");\n return;\n }\n const meetingId = state.selectedMeetingId;\n if (!meetingId) {\n setStatus(\"Select a meeting first\", \"error\");\n return;\n }\n setStatus(\"Testing harness…\", \"busy\");\n try {\n const bundle = state.selectedMeetingBundle?.document.id === meetingId ? state.selectedMeetingBundle : await client.getMeeting(meetingId, { requireCache: true });\n setState(\"harnessTestResult\", (await client.evaluateAutomationCases([{\n bundle,\n id: `web:${meetingId}`,\n title: bundle.meeting.meeting.title || bundle.document.title || bundle.document.id\n }], {\n harnessIds: [harness.id],\n kind: state.harnessTestKind,\n model: harness.model,\n provider: harness.provider\n })).results[0] ?? null);\n setStatus(\"Harness test complete\", \"ok\");\n } catch (error) {\n setState(\"harnessTestResult\", {\n caseId: `web:${meetingId}`,\n caseTitle: state.selectedMeeting?.meeting.title || meetingId,\n error: error instanceof Error ? error.message : String(error),\n harnessId: harness.id,\n harnessName: harness.name,\n prompt: \"\",\n status: \"failed\"\n });\n setStatus(\"Harness test failed\", \"error\");\n }\n };\n const loadMeeting = async (meetingId) => {\n if (!client) return;\n setState(\"selectedMeetingId\", meetingId);\n try {\n setState(\"detailError\", \"\");\n const bundle = await client.getMeeting(meetingId);\n setState(\"selectedMeetingBundle\", bundle);\n setState(\"selectedMeeting\", bundle.meeting);\n updatePreferences((preferences) => rememberRecentMeeting(preferences, bundle.meeting.meeting));\n await loadHarnessExplanations(bundle.document.id);\n await loadAutomationArtefacts({\n preferredId: state.selectedAutomationArtefactId,\n preferredMeetingId: bundle.document.id\n });\n } catch (error) {\n setState(\"selectedMeetingBundle\", null);\n setState(\"selectedMeeting\", null);\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n }\n };\n const loadMeetings = async (options = {}) => {\n if (!client) return;\n try {\n setState(\"listError\", \"\");\n const result = await client.listMeetings({\n folderId: state.selectedFolderId || void 0,\n forceRefresh: options.refresh,\n limit: 100,\n search: state.search || void 0,\n sort: state.sort,\n updatedFrom: state.updatedFrom || void 0,\n updatedTo: state.updatedTo || void 0\n });\n const preferredMeetingId = options.preferredMeetingId ?? state.selectedMeetingId;\n const nextMeetingId = selectMeetingId(result.meetings, preferredMeetingId);\n setState(\"meetings\", result.meetings);\n setState(\"meetingSource\", result.source);\n setState(\"selectedMeetingId\", nextMeetingId);\n if (nextMeetingId) await loadMeeting(nextMeetingId);\n else {\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n setState(\"detailError\", \"\");\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n await loadAutomationArtefacts({\n preferredId: state.selectedAutomationArtefactId,\n preferredMeetingId: null\n });\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n setState(\"listError\", message);\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n setState(\"detailError\", message);\n setState(\"harnessExplainEventKind\", null);\n setState(\"harnessExplanations\", []);\n }\n };\n const refreshAll = async (forceRefresh = false) => {\n if (!client) await attachClient();\n setStatus(forceRefresh ? \"Syncing…\" : \"Refreshing…\", \"busy\");\n if (forceRefresh) await client?.sync({\n forceRefresh: true,\n foreground: true\n });\n await Promise.all([\n loadFolders(forceRefresh),\n loadHarnesses(),\n loadAutomationRules(),\n loadAutomationRuns(),\n loadAutomationArtefacts(),\n loadProcessingIssues(),\n mergeAuthState()\n ]);\n await loadMeetings({ refresh: forceRefresh });\n setState(\"serverLocked\", false);\n setStatus(forceRefresh ? \"Sync complete\" : state.meetingSource === \"index\" ? \"Loaded from index\" : \"Connected\", \"ok\");\n };\n const connectAndRefresh = async (forceRefresh = false) => {\n try {\n await refreshAll(forceRefresh);\n } catch (error) {\n setStatus(\"Connection failed\", \"error\");\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n }\n };\n const quickOpenMeeting = async () => {\n if (!client) return;\n const query = state.quickOpen.trim();\n if (!query) {\n setStatus(\"Enter a title or id\", \"error\");\n return;\n }\n setStatus(\"Opening meeting…\", \"busy\");\n try {\n const bundle = await client.findMeeting(query);\n setState(\"selectedFolderId\", bundle.meeting.meeting.folders[0]?.id || null);\n setState(\"search\", \"\");\n setState(\"updatedFrom\", \"\");\n setState(\"updatedTo\", \"\");\n await loadMeetings({ preferredMeetingId: bundle.document.id });\n setStatus(\"Connected\", \"ok\");\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Quick open failed\", \"error\");\n }\n };\n const saveApiKey = async () => {\n if (!state.apiKeyDraft.trim()) {\n setStatus(\"Enter a Granola API key\", \"error\");\n return;\n }\n setStatus(\"Saving API key…\", \"busy\");\n try {\n const auth = await requestJson(granolaTransportPaths.authLogin, {\n body: JSON.stringify({ apiKey: state.apiKeyDraft.trim() }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n setState(\"apiKeyDraft\", \"\");\n setState(\"detailError\", \"\");\n if (state.appState) setState(\"appState\", \"auth\", auth);\n setStatus(\"API key saved\", \"ok\");\n } catch (error) {\n await mergeAuthState();\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"API key save failed\", \"error\");\n }\n };\n const importDesktopSession = async () => {\n setStatus(\"Importing desktop session…\", \"busy\");\n try {\n const auth = await requestJson(granolaTransportPaths.authLogin, {\n body: JSON.stringify({}),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n setState(\"detailError\", \"\");\n if (state.appState) setState(\"appState\", \"auth\", auth);\n setStatus(\"Desktop session imported\", \"ok\");\n } catch (error) {\n await mergeAuthState();\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Auth import failed\", \"error\");\n }\n };\n const refreshAuth = async () => {\n setStatus(\"Refreshing session…\", \"busy\");\n try {\n await mergeAuthState(await requestJson(granolaTransportPaths.authRefresh, { method: \"POST\" }));\n await refreshAll();\n } catch (error) {\n await mergeAuthState();\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Refresh failed\", \"error\");\n }\n };\n const switchAuthMode = async (mode) => {\n setStatus(\"Switching auth source…\", \"busy\");\n try {\n await mergeAuthState(await requestJson(granolaTransportPaths.authMode, {\n body: JSON.stringify({ mode }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n }));\n await refreshAll();\n } catch (error) {\n await mergeAuthState();\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Switch failed\", \"error\");\n }\n };\n const logout = async () => {\n setStatus(\"Signing out…\", \"busy\");\n try {\n await mergeAuthState(await requestJson(granolaTransportPaths.authLogout, { method: \"POST\" }));\n await refreshAll();\n } catch (error) {\n await mergeAuthState();\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Sign out failed\", \"error\");\n }\n };\n const exportNotes = async () => {\n if (!client) return;\n setStatus(state.selectedFolderId ? \"Exporting folder notes…\" : \"Exporting notes…\", \"busy\");\n try {\n await client.exportNotes(\"markdown\", { folderId: state.selectedFolderId || void 0 });\n await refreshAll();\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Export failed\", \"error\");\n }\n };\n const clearFilters = async () => {\n setState(\"search\", \"\");\n setState(\"sort\", \"updated-desc\");\n setState(\"updatedFrom\", \"\");\n setState(\"updatedTo\", \"\");\n setState(\"selectedFolderId\", null);\n setState(\"selectedMeetingId\", null);\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n await loadMeetings();\n setStatus(\"Filters cleared\", \"ok\");\n };\n const saveCurrentFilter = () => {\n updatePreferences((preferences) => saveWorkspaceFilter(preferences, {\n folders: state.folders,\n search: state.search,\n selectedFolderId: state.selectedFolderId,\n sort: state.sort,\n updatedFrom: state.updatedFrom,\n updatedTo: state.updatedTo\n }, { idFactory: () => `filter-${Date.now()}` }));\n setStatus(\"Saved filter\", \"ok\");\n };\n const applySavedFilterPreset = async (id) => {\n const preset = state.savedFilters.find((candidate) => candidate.id === id);\n if (!preset) return;\n const nextFilters = applyWorkspaceFilter(preset);\n setState(\"search\", nextFilters.search);\n setState(\"selectedFolderId\", nextFilters.selectedFolderId);\n setState(\"sort\", nextFilters.sort);\n setState(\"updatedFrom\", nextFilters.updatedFrom);\n setState(\"updatedTo\", nextFilters.updatedTo);\n setState(\"selectedMeetingId\", null);\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n await loadMeetings();\n setStatus(`Applied ${preset.label}`, \"ok\");\n };\n const removeSavedFilterPreset = (id) => {\n updatePreferences((preferences) => removeWorkspaceFilter(preferences, id));\n setStatus(\"Removed saved filter\", \"ok\");\n };\n const openRecentMeeting = async (meetingId, folderId) => {\n if (folderId !== void 0) {\n setState(\"selectedFolderId\", folderId || null);\n setState(\"selectedMeetingId\", null);\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n await loadMeetings({ preferredMeetingId: meetingId });\n return;\n }\n await loadMeeting(meetingId);\n };\n const meetingEmptyHint = () => {\n if (!state.appState) return \"Connect to the local server to load meetings.\";\n if (state.appState.auth.lastError) return \"Resolve auth first, then sync again.\";\n if (!state.appState.documents.loaded && !state.appState.sync.lastCompletedAt) return \"Run Sync now to populate your local meeting index.\";\n return \"Try a different folder or filter, or sync again.\";\n };\n const exportTranscripts = async () => {\n if (!client) return;\n setStatus(state.selectedFolderId ? \"Exporting folder transcripts…\" : \"Exporting transcripts…\", \"busy\");\n try {\n await client.exportTranscripts(\"text\", { folderId: state.selectedFolderId || void 0 });\n await refreshAll();\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Export failed\", \"error\");\n }\n };\n const rerunJob = async (jobId) => {\n if (!client) return;\n setStatus(\"Rerunning export…\", \"busy\");\n try {\n await client.rerunExportJob(jobId);\n await refreshAll();\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Rerun failed\", \"error\");\n }\n };\n const selectedAutomationArtefact = () => state.automationArtefacts.find((artefact) => artefact.id === state.selectedAutomationArtefactId) || null;\n const resolveAutomationRun = async (id, decision) => {\n if (!client) return;\n setStatus(decision === \"approve\" ? \"Approving automation…\" : \"Rejecting automation…\", \"busy\");\n try {\n await client.resolveAutomationRun(id, decision);\n await refreshAll();\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Automation decision failed\", \"error\");\n }\n };\n const recoverProcessingIssue = async (id) => {\n if (!client) return;\n setStatus(\"Recovering processing issue…\", \"busy\");\n try {\n const result = await client.recoverProcessingIssue(id);\n await refreshAll();\n setStatus(result.runCount > 0 ? `Recovered ${result.issue.kind} and re-ran ${result.runCount} pipeline${result.runCount === 1 ? \"\" : \"s\"}` : `Recovered ${result.issue.kind}`, \"ok\");\n } catch (error) {\n setState(\"processingIssueError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Recovery failed\", \"error\");\n }\n };\n const selectReviewInboxItem = async (key) => {\n setState(\"selectedReviewInboxKey\", key);\n const item = reviewInboxItems().find((candidate) => candidate.key === key);\n if (!item) return;\n try {\n if (item.kind === \"artefact\") {\n await selectAutomationArtefact(item.artefact.id);\n return;\n }\n if (item.meetingId && item.meetingId !== state.selectedMeetingId) await loadMeeting(item.meetingId);\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Unable to open review item\", \"error\");\n }\n };\n const selectAutomationArtefact = async (id) => {\n if (!client) return;\n try {\n const artefact = state.automationArtefacts.find((candidate) => candidate.id === id) ?? await client.getAutomationArtefact(id);\n setState(\"selectedReviewInboxKey\", `artefact:${artefact.id}`);\n applySelectedArtefactDrafts(artefact);\n if (artefact.meetingId !== state.selectedMeetingId) await loadMeeting(artefact.meetingId);\n } catch (error) {\n setState(\"automationArtefactError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Unable to open artefact\", \"error\");\n }\n };\n const saveAutomationArtefact = async () => {\n if (!client || !state.selectedAutomationArtefactId) return;\n setStatus(\"Saving artefact edits…\", \"busy\");\n try {\n const artefact = await client.updateAutomationArtefact(state.selectedAutomationArtefactId, {\n markdown: state.automationArtefactDraftMarkdown,\n note: state.reviewNote || void 0,\n summary: state.automationArtefactDraftSummary,\n title: state.automationArtefactDraftTitle\n });\n await loadAutomationArtefacts({\n preferredId: artefact.id,\n preferredMeetingId: artefact.meetingId\n });\n await loadAutomationRuns();\n setStatus(\"Artefact updated\", \"ok\");\n } catch (error) {\n setState(\"automationArtefactError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Artefact save failed\", \"error\");\n }\n };\n const resolveAutomationArtefact = async (decision) => {\n if (!client || !state.selectedAutomationArtefactId) return;\n setStatus(decision === \"approve\" ? \"Approving artefact…\" : \"Rejecting artefact…\", \"busy\");\n try {\n const artefact = await client.resolveAutomationArtefact(state.selectedAutomationArtefactId, decision, { note: state.reviewNote || void 0 });\n await loadAutomationArtefacts({\n preferredId: artefact.id,\n preferredMeetingId: artefact.meetingId\n });\n await loadAutomationRuns();\n setStatus(decision === \"approve\" ? \"Artefact approved\" : \"Artefact rejected\", \"ok\");\n } catch (error) {\n setState(\"automationArtefactError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Artefact decision failed\", \"error\");\n }\n };\n const rerunAutomationArtefact = async () => {\n if (!client || !state.selectedAutomationArtefactId) return;\n setStatus(\"Rerunning artefact pipeline…\", \"busy\");\n try {\n const artefact = await client.rerunAutomationArtefact(state.selectedAutomationArtefactId);\n await loadAutomationArtefacts({\n preferredId: artefact.id,\n preferredMeetingId: artefact.meetingId\n });\n await loadAutomationRuns();\n setStatus(\"Artefact rerun complete\", \"ok\");\n } catch (error) {\n setState(\"automationArtefactError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Artefact rerun failed\", \"error\");\n }\n };\n const unlockServer = async () => {\n if (!state.serverPassword.trim()) {\n setStatus(\"Enter the server password\", \"error\");\n return;\n }\n setStatus(\"Unlocking server…\", \"busy\");\n try {\n await requestJson(\"/auth/unlock\", {\n body: JSON.stringify({ password: state.serverPassword }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\"\n });\n setState(\"serverPassword\", \"\");\n setState(\"serverLocked\", false);\n await connectAndRefresh(true);\n } catch (error) {\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\n setStatus(\"Unlock failed\", \"error\");\n }\n };\n const lockServer = async () => {\n try {\n await requestJson(\"/auth/lock\", { method: \"POST\" });\n } catch {}\n await detachClient();\n setState({\n appState: null,\n automationArtefactDraftMarkdown: \"\",\n automationArtefactDraftSummary: \"\",\n automationArtefactDraftTitle: \"\",\n automationArtefactError: \"\",\n automationArtefacts: [],\n automationRules: [],\n automationRuns: [],\n detailError: \"\",\n folderError: \"\",\n folders: [],\n harnessDirty: false,\n harnessError: \"\",\n harnessExplainEventKind: null,\n harnessExplanations: [],\n harnessTestResult: null,\n harnesses: [],\n listError: \"\",\n meetings: [],\n processingIssueError: \"\",\n processingIssues: [],\n reviewNote: \"\",\n selectedAutomationArtefactId: null,\n selectedFolderId: null,\n selectedHarnessId: null,\n selectedMeeting: null,\n selectedMeetingBundle: null,\n selectedMeetingId: null,\n controlPanelTab: \"overview\",\n serverLocked: true,\n serverPassword: \"\"\n });\n setStatus(\"Server locked\", \"error\");\n };\n createEffect(() => {\n const nextPath = buildBrowserUrlPath(window.location.href, {\n selectedFolderId: state.selectedFolderId,\n selectedMeetingId: state.selectedMeetingId,\n workspaceTab: state.workspaceTab\n });\n if (nextPath !== `${window.location.pathname}${window.location.search}${window.location.hash}`) history.replaceState(null, \"\", nextPath);\n });\n createEffect(() => {\n if (!state.appState?.automation.loaded || !client) {\n automationPanelsHydrated = false;\n return;\n }\n if (automationPanelsHydrated) return;\n automationPanelsHydrated = true;\n loadAutomationRuns();\n loadAutomationArtefacts();\n loadProcessingIssues();\n });\n createEffect(() => {\n const current = reviewInboxItems().find((item) => item.key === state.selectedReviewInboxKey) ?? reviewInboxItems()[0] ?? null;\n const nextKey = current?.key ?? null;\n if (nextKey !== state.selectedReviewInboxKey) setState(\"selectedReviewInboxKey\", nextKey);\n if (current?.kind === \"artefact\" && current.artefact.id !== state.selectedAutomationArtefactId) applySelectedArtefactDrafts(current.artefact);\n });\n onMount(() => {\n const onKeyDown = (event) => {\n const target = event.target;\n if (target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement) return;\n const nextTab = nextWorkspaceTab(state.workspaceTab, event.key);\n if (nextTab) setState(\"workspaceTab\", nextTab);\n };\n document.addEventListener(\"keydown\", onKeyDown);\n onCleanup(() => {\n document.removeEventListener(\"keydown\", onKeyDown);\n });\n if (!state.serverLocked) connectAndRefresh();\n });\n onCleanup(() => {\n detachClient();\n });\n const onboardingState = () => deriveOnboardingState({\n appState: state.appState,\n automationRuleCount: state.automationRules.length,\n harnesses: state.harnesses,\n meetingsLoadedCount: state.meetings.length\n });\n const showOnboarding = () => !state.serverLocked && !onboardingState().complete;\n const controlTabs = [\n {\n id: \"overview\",\n label: \"Overview\"\n },\n {\n id: \"auth\",\n label: \"Auth\"\n },\n {\n id: \"pipelines\",\n label: \"Pipelines\"\n },\n {\n id: \"review\",\n label: \"Inbox\"\n }\n ];\n return createComponent(Show, {\n get when() {\n return memo(() => !!!state.serverLocked)() && !showOnboarding();\n },\n get fallback() {\n return (() => {\n var _el$15 = _tmpl$3(), _el$16 = _el$15.firstChild;\n insert(_el$16, createComponent(AppStatePanel, {\n get appState() {\n return state.appState;\n },\n get statusLabel() {\n return state.statusLabel;\n },\n get statusTone() {\n return state.statusTone;\n }\n }), null);\n insert(_el$16, createComponent(SecurityPanel, {\n onLock: () => {\n lockServer();\n },\n onPasswordChange: (value) => {\n setState(\"serverPassword\", value);\n },\n onUnlock: () => {\n unlockServer();\n },\n get password() {\n return state.serverPassword;\n },\n get visible() {\n return state.serverLocked;\n }\n }), null);\n insert(_el$16, (() => {\n var _c$ = memo(() => !!!state.serverLocked);\n return () => _c$() ? createComponent(OnboardingPanel, {\n get apiKeyDraft() {\n return state.apiKeyDraft;\n },\n get auth() {\n return state.appState?.auth;\n },\n get folders() {\n return state.folders;\n },\n get meetingsLoadedCount() {\n return state.meetings.length;\n },\n onApiKeyDraftChange: (value) => {\n setState(\"apiKeyDraft\", value);\n },\n onCreateStarterPipeline: () => {\n createStarterPipeline();\n },\n onImportDesktopSession: () => {\n importDesktopSession();\n },\n onRunSync: () => {\n connectAndRefresh(true);\n },\n onSaveApiKey: () => {\n saveApiKey();\n },\n onSelectProvider: (provider) => {\n setState(\"preferredProvider\", provider);\n },\n get preferredProvider() {\n return state.preferredProvider;\n },\n get state() {\n return onboardingState();\n }\n }) : null;\n })(), null);\n insert(_el$16, (() => {\n var _c$2 = memo(() => !!(!state.serverLocked && state.detailError));\n return () => _c$2() ? (() => {\n var _el$17 = _tmpl$4(), _el$19 = _el$17.firstChild.firstChild;\n insert(_el$19, () => state.detailError);\n return _el$17;\n })() : null;\n })(), null);\n return _el$15;\n })();\n },\n get children() {\n var _el$ = _tmpl$2(), _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, _el$9 = _el$8.nextSibling, _el$0 = _el$4.nextSibling, _el$1 = _el$0.firstChild, _el$10 = _el$1.nextSibling;\n insert(_el$2, createComponent(ToolbarFilters, {\n onQuickOpen: () => {\n quickOpenMeeting();\n },\n onQuickOpenInput: (value) => {\n setState(\"quickOpen\", value);\n },\n onSearchInput: (value) => {\n setState(\"search\", value.trim());\n loadMeetings();\n },\n onSortChange: (value) => {\n setState(\"sort\", value);\n loadMeetings();\n },\n onUpdatedFromChange: (value) => {\n setState(\"updatedFrom\", value);\n loadMeetings();\n },\n onUpdatedToChange: (value) => {\n setState(\"updatedTo\", value);\n loadMeetings();\n },\n get quickOpen() {\n return state.quickOpen;\n },\n get search() {\n return state.search;\n },\n get sort() {\n return state.sort;\n },\n get updatedFrom() {\n return state.updatedFrom;\n },\n get updatedTo() {\n return state.updatedTo;\n }\n }), null);\n insert(_el$2, createComponent(SavedFiltersPanel, {\n get folders() {\n return state.folders;\n },\n onApply: (preset) => {\n applySavedFilterPreset(preset.id);\n },\n onRemove: removeSavedFilterPreset,\n onSaveCurrent: saveCurrentFilter,\n get savedFilters() {\n return state.savedFilters;\n },\n get search() {\n return state.search;\n },\n get selectedFolderId() {\n return state.selectedFolderId;\n },\n get sort() {\n return state.sort;\n },\n get updatedFrom() {\n return state.updatedFrom;\n },\n get updatedTo() {\n return state.updatedTo;\n }\n }), null);\n insert(_el$2, createComponent(RecentMeetingsPanel, {\n onOpen: (meeting) => {\n openRecentMeeting(meeting.id, meeting.folderId);\n },\n get recentMeetings() {\n return state.recentMeetings;\n }\n }), null);\n insert(_el$2, createComponent(FolderList, {\n get error() {\n return state.folderError;\n },\n get folders() {\n return state.folders;\n },\n onSelect: (folderId) => {\n setState(\"selectedFolderId\", folderId);\n setState(\"selectedMeetingId\", null);\n setState(\"selectedMeeting\", null);\n setState(\"selectedMeetingBundle\", null);\n loadMeetings();\n },\n get selectedFolderId() {\n return state.selectedFolderId;\n }\n }), null);\n insert(_el$2, createComponent(MeetingList, {\n get error() {\n return state.listError;\n },\n get emptyHint() {\n return meetingEmptyHint();\n },\n get folders() {\n return state.folders;\n },\n get meetings() {\n return state.meetings;\n },\n onSelect: (meetingId) => {\n loadMeeting(meetingId);\n },\n get search() {\n return state.search;\n },\n get selectedFolderId() {\n return state.selectedFolderId;\n },\n get selectedMeetingId() {\n return state.selectedMeetingId;\n },\n get updatedFrom() {\n return state.updatedFrom;\n },\n get updatedTo() {\n return state.updatedTo;\n }\n }), null);\n insert(_el$3, createComponent(AppStatePanel, {\n get appState() {\n return state.appState;\n },\n get statusLabel() {\n return state.statusLabel;\n },\n get statusTone() {\n return state.statusTone;\n }\n }), _el$4);\n _el$6.$$click = () => {\n connectAndRefresh(true);\n };\n _el$7.$$click = () => {\n clearFilters();\n };\n _el$8.$$click = () => {\n exportNotes();\n };\n _el$9.$$click = () => {\n exportTranscripts();\n };\n insert(_el$3, createComponent(Workspace, {\n get bundle() {\n return state.selectedMeetingBundle;\n },\n get detailError() {\n return state.detailError;\n },\n onSelectTab: (tab) => {\n setState(\"workspaceTab\", tab);\n },\n get selectedMeeting() {\n return state.selectedMeeting;\n },\n get tab() {\n return state.workspaceTab;\n }\n }), _el$0);\n insert(_el$1, () => controlTabs.map((tab) => (() => {\n var _el$20 = _tmpl$5();\n _el$20.$$click = () => {\n setState(\"controlPanelTab\", tab.id);\n };\n insert(_el$20, () => tab.label);\n createRenderEffect(() => setAttribute(_el$20, \"data-selected\", state.controlPanelTab === tab.id));\n return _el$20;\n })()));\n insert(_el$10, createComponent(Show, {\n get when() {\n return state.controlPanelTab === \"overview\";\n },\n get children() {\n var _el$11 = _tmpl$();\n insert(_el$11, createComponent(ExportJobsPanel, {\n get jobs() {\n return state.appState?.exports.jobs || [];\n },\n onRerun: (jobId) => {\n rerunJob(jobId);\n }\n }));\n return _el$11;\n }\n }), null);\n insert(_el$10, createComponent(Show, {\n get when() {\n return state.controlPanelTab === \"auth\";\n },\n get children() {\n var _el$12 = _tmpl$();\n insert(_el$12, createComponent(SecurityPanel, {\n onLock: () => {\n lockServer();\n },\n onPasswordChange: (value) => {\n setState(\"serverPassword\", value);\n },\n onUnlock: () => {\n unlockServer();\n },\n get password() {\n return state.serverPassword;\n },\n get visible() {\n return state.serverLocked;\n }\n }), null);\n insert(_el$12, createComponent(AuthPanel, {\n get apiKeyDraft() {\n return state.apiKeyDraft;\n },\n get auth() {\n return state.appState?.auth;\n },\n onApiKeyDraftChange: (value) => {\n setState(\"apiKeyDraft\", value);\n },\n onImportDesktopSession: () => {\n importDesktopSession();\n },\n onLogout: () => {\n logout();\n },\n onRefresh: () => {\n refreshAuth();\n },\n onSaveApiKey: () => {\n saveApiKey();\n },\n onSwitchMode: (mode) => {\n switchAuthMode(mode);\n }\n }), null);\n return _el$12;\n }\n }), null);\n insert(_el$10, createComponent(Show, {\n get when() {\n return state.controlPanelTab === \"pipelines\";\n },\n get children() {\n var _el$13 = _tmpl$();\n insert(_el$13, createComponent(HarnessEditorPanel, {\n get dirty() {\n return state.harnessDirty;\n },\n get error() {\n return state.harnessError;\n },\n get explanations() {\n return state.harnessExplanations;\n },\n get explanationEventKind() {\n return state.harnessExplainEventKind;\n },\n get harnesses() {\n return state.harnesses;\n },\n onChange: updateHarness,\n onDuplicate: duplicateHarness,\n onNew: createHarness,\n onReload: () => {\n reloadHarnesses();\n },\n onRemove: removeHarness,\n onSave: () => {\n saveHarnesses();\n },\n onSelect: (id) => {\n setState(\"selectedHarnessId\", id);\n setState(\"harnessTestResult\", null);\n },\n onTest: () => {\n testHarness();\n },\n onTestKindChange: (kind) => {\n setState(\"harnessTestKind\", kind);\n },\n get selectedHarness() {\n return selectedHarness();\n },\n get selectedHarnessId() {\n return state.selectedHarnessId;\n },\n get selectedMeeting() {\n return state.selectedMeeting;\n },\n get testKind() {\n return state.harnessTestKind;\n },\n get testResult() {\n return state.harnessTestResult;\n }\n }));\n return _el$13;\n }\n }), null);\n insert(_el$10, createComponent(Show, {\n get when() {\n return state.controlPanelTab === \"review\";\n },\n get children() {\n var _el$14 = _tmpl$();\n insert(_el$14, createComponent(ReviewInboxPanel, {\n get items() {\n return reviewInboxItems();\n },\n onSelect: (key) => {\n selectReviewInboxItem(key);\n },\n get selectedKey() {\n return state.selectedReviewInboxKey;\n },\n get summary() {\n return reviewInboxSummary();\n }\n }), null);\n insert(_el$14, createComponent(Show, {\n get when() {\n return selectedReviewInboxItem()?.kind === \"issue\";\n },\n get children() {\n return createComponent(IssueReviewPanel, {\n get issue() {\n return selectedReviewIssue();\n },\n onOpenMeeting: (meetingId) => {\n loadMeeting(meetingId);\n },\n onRecover: (issueId) => {\n recoverProcessingIssue(issueId);\n }\n });\n }\n }), null);\n insert(_el$14, createComponent(Show, {\n get when() {\n return selectedReviewInboxItem()?.kind === \"run\";\n },\n get children() {\n return createComponent(RunReviewPanel, {\n onApprove: (runId) => {\n resolveAutomationRun(runId, \"approve\");\n },\n onOpenMeeting: (meetingId) => {\n loadMeeting(meetingId);\n },\n onReject: (runId) => {\n resolveAutomationRun(runId, \"reject\");\n },\n get run() {\n return selectedReviewRun();\n }\n });\n }\n }), null);\n insert(_el$14, createComponent(Show, {\n get when() {\n return selectedReviewInboxItem()?.kind === \"artefact\";\n },\n get children() {\n return createComponent(ArtefactReviewPanel, {\n get artefact() {\n return selectedReviewArtefact();\n },\n get bundle() {\n return state.selectedMeetingBundle;\n },\n get draftMarkdown() {\n return state.automationArtefactDraftMarkdown;\n },\n get draftSummary() {\n return state.automationArtefactDraftSummary;\n },\n get draftTitle() {\n return state.automationArtefactDraftTitle;\n },\n get error() {\n return state.automationArtefactError;\n },\n onApprove: () => {\n resolveAutomationArtefact(\"approve\");\n },\n onDraftMarkdownChange: (value) => {\n setState(\"automationArtefactDraftMarkdown\", value);\n },\n onDraftSummaryChange: (value) => {\n setState(\"automationArtefactDraftSummary\", value);\n },\n onDraftTitleChange: (value) => {\n setState(\"automationArtefactDraftTitle\", value);\n },\n onReject: () => {\n resolveAutomationArtefact(\"reject\");\n },\n onRerun: () => {\n rerunAutomationArtefact();\n },\n onReviewNoteChange: (value) => {\n setState(\"reviewNote\", value);\n },\n onSave: () => {\n saveAutomationArtefact();\n },\n get reviewNote() {\n return state.reviewNote;\n }\n });\n }\n }), null);\n return _el$14;\n }\n }), null);\n return _el$;\n }\n });\n}\ndelegateEvents([\"click\"]);\n//#endregion\n//#region src/web-app/main.tsx\n/** @jsxImportSource solid-js */\nvar root = document.getElementById(\"granola-web-root\");\nif (!root) throw new Error(\"Granola web root element not found\");\nrender(() => createComponent(App, {}), root);\n//#endregion\n";
|
|
9860
9925
|
//#endregion
|
|
9861
9926
|
//#region src/web/assets.ts
|
|
9862
9927
|
const granolaWebAssetPaths = {
|