granola-toolkit 0.43.0 → 0.45.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.
Files changed (3) hide show
  1. package/README.md +4 -0
  2. package/dist/cli.js +2 -3229
  3. package/package.json +9 -1
package/dist/cli.js CHANGED
@@ -6359,3235 +6359,8 @@ async function openExternalUrl(url, options = {}) {
6359
6359
  }
6360
6360
  //#endregion
6361
6361
  //#region src/web/generated.ts
6362
- const granolaWebClientCss = String.raw`:root {
6363
- --bg: #f2ede2;
6364
- --panel: rgba(255, 252, 247, 0.86);
6365
- --panel-strong: #fffaf2;
6366
- --line: rgba(36, 39, 44, 0.12);
6367
- --ink: #1d242c;
6368
- --muted: #5d6b77;
6369
- --accent: #0d6a6d;
6370
- --accent-soft: rgba(13, 106, 109, 0.12);
6371
- --warm: #a34f2f;
6372
- --ok: #246b4f;
6373
- --error: #9d2c2c;
6374
- --shadow: 0 24px 80px rgba(40, 32, 16, 0.12);
6375
- --radius: 24px;
6376
- --mono: "SF Mono", "IBM Plex Mono", "Cascadia Code", monospace;
6377
- --serif: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia, serif;
6378
- --sans: "Avenir Next", "Segoe UI", sans-serif;
6379
- }
6380
-
6381
- * {
6382
- box-sizing: border-box;
6383
- }
6384
-
6385
- body {
6386
- margin: 0;
6387
- min-height: 100vh;
6388
- font-family: var(--sans);
6389
- color: var(--ink);
6390
- background:
6391
- radial-gradient(circle at top left, rgba(163, 79, 47, 0.18), transparent 32%),
6392
- radial-gradient(circle at right 12%, rgba(13, 106, 109, 0.16), transparent 28%),
6393
- linear-gradient(180deg, #f8f2e8 0%, var(--bg) 100%);
6394
- }
6395
-
6396
- button,
6397
- input,
6398
- select {
6399
- font: inherit;
6400
- }
6401
-
6402
- #granola-web-root {
6403
- min-height: 100vh;
6404
- }
6405
-
6406
- .shell {
6407
- display: grid;
6408
- grid-template-columns: 320px minmax(0, 1fr);
6409
- gap: 18px;
6410
- min-height: 100vh;
6411
- padding: 24px;
6412
- }
6413
-
6414
- .pane {
6415
- background: var(--panel);
6416
- backdrop-filter: blur(18px);
6417
- border: 1px solid var(--line);
6418
- border-radius: var(--radius);
6419
- box-shadow: var(--shadow);
6420
- }
6421
-
6422
- .sidebar {
6423
- display: grid;
6424
- grid-template-rows: auto auto auto 1fr;
6425
- overflow: hidden;
6426
- }
6427
-
6428
- .hero,
6429
- .toolbar,
6430
- .detail-head,
6431
- .folder-panel {
6432
- padding: 22px 24px;
6433
- border-bottom: 1px solid var(--line);
6434
- }
6435
-
6436
- .hero h1 {
6437
- margin: 0;
6438
- font-family: var(--serif);
6439
- font-size: clamp(2rem, 3vw, 2.8rem);
6440
- font-weight: 600;
6441
- letter-spacing: -0.04em;
6442
- }
6443
-
6444
- .hero p,
6445
- .toolbar p {
6446
- margin: 8px 0 0;
6447
- color: var(--muted);
6448
- line-height: 1.5;
6449
- }
6450
-
6451
- .search,
6452
- .select,
6453
- .field-input,
6454
- .input {
6455
- width: 100%;
6456
- margin-top: 16px;
6457
- padding: 12px 14px;
6458
- border: 1px solid var(--line);
6459
- border-radius: 999px;
6460
- background: rgba(255, 255, 255, 0.7);
6461
- color: var(--ink);
6462
- }
6463
-
6464
- .field-row {
6465
- display: grid;
6466
- gap: 10px;
6467
- margin-top: 12px;
6468
- }
6469
-
6470
- .field-row--inline {
6471
- grid-template-columns: repeat(2, minmax(0, 1fr));
6472
- }
6473
-
6474
- .field-label {
6475
- display: block;
6476
- margin-bottom: 6px;
6477
- color: var(--muted);
6478
- font-size: 0.78rem;
6479
- font-weight: 700;
6480
- letter-spacing: 0.08em;
6481
- text-transform: uppercase;
6482
- }
6483
-
6484
- .folder-panel {
6485
- display: grid;
6486
- gap: 14px;
6487
- }
6488
-
6489
- .folder-panel__head h2 {
6490
- margin: 0;
6491
- font-size: 0.92rem;
6492
- letter-spacing: 0.08em;
6493
- text-transform: uppercase;
6494
- }
6495
-
6496
- .folder-panel__head p {
6497
- margin: 6px 0 0;
6498
- color: var(--muted);
6499
- font-size: 0.9rem;
6500
- }
6501
-
6502
- .folder-list,
6503
- .jobs-list {
6504
- display: grid;
6505
- gap: 10px;
6506
- }
6507
-
6508
- .folder-row,
6509
- .meeting-row {
6510
- width: 100%;
6511
- display: grid;
6512
- gap: 4px;
6513
- text-align: left;
6514
- padding: 12px 14px;
6515
- border: 1px solid transparent;
6516
- border-radius: 16px;
6517
- background: rgba(255, 255, 255, 0.72);
6518
- color: inherit;
6519
- cursor: pointer;
6520
- transition:
6521
- transform 140ms ease,
6522
- border-color 140ms ease,
6523
- background 140ms ease;
6524
- }
6525
-
6526
- .meeting-row {
6527
- margin: 0 0 10px;
6528
- padding: 14px 16px;
6529
- border-radius: 18px;
6530
- }
6531
-
6532
- .folder-row:hover,
6533
- .folder-row[data-selected="true"] {
6534
- transform: translateY(-1px);
6535
- border-color: rgba(163, 79, 47, 0.26);
6536
- background: var(--panel-strong);
6537
- }
6538
-
6539
- .meeting-row:hover,
6540
- .meeting-row[data-selected="true"] {
6541
- transform: translateY(-1px);
6542
- border-color: rgba(13, 106, 109, 0.25);
6543
- background: var(--panel-strong);
6544
- }
6545
-
6546
- .folder-row__title,
6547
- .job-card__title {
6548
- font-weight: 700;
6549
- }
6550
-
6551
- .meeting-row__title {
6552
- font-weight: 600;
6553
- }
6554
-
6555
- .folder-row__meta,
6556
- .meeting-row__meta,
6557
- .auth-card__meta,
6558
- .job-card__meta,
6559
- .folder-empty,
6560
- .job-empty,
6561
- .meeting-empty {
6562
- color: var(--muted);
6563
- font-size: 0.9rem;
6564
- }
6565
-
6566
- .folder-empty--error,
6567
- .meeting-empty--error,
6568
- .auth-card__error {
6569
- color: var(--error);
6570
- }
6571
-
6572
- .meeting-list {
6573
- padding: 14px;
6574
- overflow: auto;
6575
- }
6576
-
6577
- .detail {
6578
- display: grid;
6579
- grid-template-rows: auto auto 1fr;
6580
- min-width: 0;
6581
- }
6582
-
6583
- .detail-head {
6584
- display: flex;
6585
- align-items: center;
6586
- justify-content: space-between;
6587
- gap: 18px;
6588
- }
6589
-
6590
- .detail-head h2 {
6591
- margin: 0;
6592
- font-family: var(--serif);
6593
- font-size: clamp(1.8rem, 2.4vw, 2.4rem);
6594
- font-weight: 600;
6595
- }
6596
-
6597
- .state-badge {
6598
- padding: 10px 14px;
6599
- border-radius: 999px;
6600
- background: var(--accent-soft);
6601
- color: var(--accent);
6602
- font-size: 0.92rem;
6603
- font-weight: 700;
6604
- }
6605
-
6606
- .state-badge[data-tone="busy"] {
6607
- background: rgba(163, 79, 47, 0.12);
6608
- color: var(--warm);
6609
- }
6610
-
6611
- .state-badge[data-tone="error"] {
6612
- background: rgba(157, 44, 44, 0.12);
6613
- color: var(--error);
6614
- }
6615
-
6616
- .state-badge[data-tone="ok"] {
6617
- background: rgba(36, 107, 79, 0.12);
6618
- color: var(--ok);
6619
- }
6620
-
6621
- .toolbar {
6622
- display: flex;
6623
- flex-wrap: wrap;
6624
- align-items: center;
6625
- justify-content: space-between;
6626
- gap: 14px;
6627
- }
6628
-
6629
- .toolbar-actions,
6630
- .auth-card__actions,
6631
- .job-card__actions {
6632
- display: flex;
6633
- flex-wrap: wrap;
6634
- gap: 10px;
6635
- }
6636
-
6637
- .toolbar-form {
6638
- display: grid;
6639
- grid-template-columns: minmax(0, 1fr) auto;
6640
- gap: 10px;
6641
- width: min(440px, 100%);
6642
- }
6643
-
6644
- .security-panel,
6645
- .auth-panel,
6646
- .jobs-panel {
6647
- padding: 0 24px 18px;
6648
- }
6649
-
6650
- .security-panel__head h3,
6651
- .auth-panel__head h3,
6652
- .jobs-panel__head h3 {
6653
- margin: 0;
6654
- font-size: 0.92rem;
6655
- letter-spacing: 0.08em;
6656
- text-transform: uppercase;
6657
- }
6658
-
6659
- .security-panel__head p,
6660
- .auth-panel__head p,
6661
- .jobs-panel__head p {
6662
- margin: 6px 0 0;
6663
- color: var(--muted);
6664
- font-size: 0.9rem;
6665
- }
6666
-
6667
- .security-panel__body,
6668
- .auth-panel__body {
6669
- display: grid;
6670
- gap: 12px;
6671
- margin-top: 14px;
6672
- }
6673
-
6674
- .auth-card,
6675
- .job-card {
6676
- display: grid;
6677
- gap: 12px;
6678
- padding: 14px 16px;
6679
- border: 1px solid var(--line);
6680
- border-radius: 18px;
6681
- background: rgba(255, 255, 255, 0.72);
6682
- }
6683
-
6684
- .job-card__head {
6685
- display: flex;
6686
- flex-wrap: wrap;
6687
- align-items: center;
6688
- justify-content: space-between;
6689
- gap: 10px;
6690
- }
6691
-
6692
- .job-card__status {
6693
- padding: 6px 10px;
6694
- border-radius: 999px;
6695
- background: var(--accent-soft);
6696
- color: var(--accent);
6697
- font-size: 0.82rem;
6698
- font-weight: 700;
6699
- }
6700
-
6701
- .job-card__status[data-status="running"] {
6702
- background: rgba(163, 79, 47, 0.12);
6703
- color: var(--warm);
6704
- }
6705
-
6706
- .job-card__status[data-status="failed"] {
6707
- background: rgba(157, 44, 44, 0.12);
6708
- color: var(--error);
6709
- }
6710
-
6711
- .job-card__status[data-status="completed"] {
6712
- background: rgba(36, 107, 79, 0.12);
6713
- color: var(--ok);
6714
- }
6715
-
6716
- .workspace-tabs {
6717
- display: flex;
6718
- flex-wrap: wrap;
6719
- align-items: center;
6720
- gap: 10px;
6721
- padding: 0 24px 18px;
6722
- }
6723
-
6724
- .workspace-tab,
6725
- .button {
6726
- border: 1px solid var(--line);
6727
- border-radius: 999px;
6728
- padding: 10px 14px;
6729
- background: rgba(255, 255, 255, 0.72);
6730
- color: var(--ink);
6731
- cursor: pointer;
6732
- font-weight: 700;
6733
- }
6734
-
6735
- .button {
6736
- padding: 12px 16px;
6737
- }
6738
-
6739
- .workspace-tab[data-selected="true"],
6740
- .button--primary {
6741
- background: var(--ink);
6742
- color: #fff;
6743
- border-color: var(--ink);
6744
- }
6745
-
6746
- .button--secondary {
6747
- background: rgba(255, 255, 255, 0.72);
6748
- }
6749
-
6750
- .button:disabled {
6751
- cursor: not-allowed;
6752
- opacity: 0.56;
6753
- }
6754
-
6755
- .workspace-hint {
6756
- margin-left: auto;
6757
- color: var(--muted);
6758
- font-size: 0.86rem;
6759
- }
6760
-
6761
- .status-grid {
6762
- display: grid;
6763
- grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
6764
- gap: 14px;
6765
- }
6766
-
6767
- .status-label {
6768
- display: block;
6769
- margin-bottom: 6px;
6770
- color: var(--muted);
6771
- font-size: 0.78rem;
6772
- letter-spacing: 0.08em;
6773
- text-transform: uppercase;
6774
- }
6775
-
6776
- .detail-meta {
6777
- display: flex;
6778
- flex-wrap: wrap;
6779
- gap: 10px;
6780
- padding: 0 24px 18px;
6781
- }
6782
-
6783
- .detail-chip {
6784
- padding: 10px 12px;
6785
- border-radius: 999px;
6786
- background: rgba(255, 255, 255, 0.72);
6787
- border: 1px solid var(--line);
6788
- color: var(--muted);
6789
- font-size: 0.88rem;
6790
- }
6791
-
6792
- .detail-body {
6793
- padding: 0 24px 24px;
6794
- overflow: auto;
6795
- }
6796
-
6797
- .workspace-grid {
6798
- display: grid;
6799
- grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
6800
- gap: 18px;
6801
- }
6802
-
6803
- .detail-section {
6804
- margin-bottom: 20px;
6805
- padding: 20px;
6806
- background: rgba(255, 255, 255, 0.72);
6807
- border: 1px solid var(--line);
6808
- border-radius: 20px;
6809
- }
6810
-
6811
- .detail-section h2 {
6812
- margin: 0 0 14px;
6813
- font-size: 1rem;
6814
- letter-spacing: 0.08em;
6815
- text-transform: uppercase;
6816
- }
6817
-
6818
- .detail-pre {
6819
- margin: 0;
6820
- white-space: pre-wrap;
6821
- word-break: break-word;
6822
- font-family: var(--mono);
6823
- line-height: 1.55;
6824
- }
6825
-
6826
- .empty {
6827
- margin: 24px;
6828
- padding: 24px;
6829
- border-radius: 20px;
6830
- background: rgba(255, 255, 255, 0.72);
6831
- color: var(--muted);
6832
- }
6833
-
6834
- @media (max-width: 1024px) {
6835
- .shell,
6836
- .workspace-grid {
6837
- grid-template-columns: 1fr;
6838
- }
6839
- }
6840
- /*$vite$:1*/`;
6841
- const granolaWebClientJs = String.raw`//#region node_modules/solid-js/dist/solid.js
6842
- var sharedConfig = {
6843
- context: void 0,
6844
- registry: void 0,
6845
- effects: void 0,
6846
- done: false,
6847
- getContextId() {
6848
- return getContextId(this.context.count);
6849
- },
6850
- getNextContextId() {
6851
- return getContextId(this.context.count++);
6852
- }
6853
- };
6854
- function getContextId(count) {
6855
- const num = String(count), len = num.length - 1;
6856
- return sharedConfig.context.id + (len ? String.fromCharCode(96 + len) : "") + num;
6857
- }
6858
- function setHydrateContext(context) {
6859
- sharedConfig.context = context;
6860
- }
6861
- function nextHydrateContext() {
6862
- return {
6863
- ...sharedConfig.context,
6864
- id: sharedConfig.getNextContextId(),
6865
- count: 0
6866
- };
6867
- }
6868
- var equalFn = (a, b) => a === b;
6869
- var $PROXY = Symbol("solid-proxy");
6870
- var $TRACK = Symbol("solid-track");
6871
- var signalOptions = { equals: equalFn };
6872
- var ERROR = null;
6873
- var runEffects = runQueue;
6874
- var STALE = 1;
6875
- var PENDING = 2;
6876
- var UNOWNED = {
6877
- owned: null,
6878
- cleanups: null,
6879
- context: null,
6880
- owner: null
6881
- };
6882
- var Owner = null;
6883
- var Transition = null;
6884
- var Scheduler = null;
6885
- var ExternalSourceConfig = null;
6886
- var Listener = null;
6887
- var Updates = null;
6888
- var Effects = null;
6889
- var ExecCount = 0;
6890
- function createRoot(fn, detachedOwner) {
6891
- const listener = Listener, owner = Owner, unowned = fn.length === 0, current = detachedOwner === void 0 ? owner : detachedOwner, root = unowned ? UNOWNED : {
6892
- owned: null,
6893
- cleanups: null,
6894
- context: current ? current.context : null,
6895
- owner: current
6896
- }, updateFn = unowned ? fn : () => fn(() => untrack(() => cleanNode(root)));
6897
- Owner = root;
6898
- Listener = null;
6899
- try {
6900
- return runUpdates(updateFn, true);
6901
- } finally {
6902
- Listener = listener;
6903
- Owner = owner;
6904
- }
6905
- }
6906
- function createSignal(value, options) {
6907
- options = options ? Object.assign({}, signalOptions, options) : signalOptions;
6908
- const s = {
6909
- value,
6910
- observers: null,
6911
- observerSlots: null,
6912
- comparator: options.equals || void 0
6913
- };
6914
- const setter = (value) => {
6915
- if (typeof value === "function") if (Transition && Transition.running && Transition.sources.has(s)) value = value(s.tValue);
6916
- else value = value(s.value);
6917
- return writeSignal(s, value);
6918
- };
6919
- return [readSignal.bind(s), setter];
6920
- }
6921
- function createRenderEffect(fn, value, options) {
6922
- const c = createComputation(fn, value, false, STALE);
6923
- if (Scheduler && Transition && Transition.running) Updates.push(c);
6924
- else updateComputation(c);
6925
- }
6926
- function createEffect(fn, value, options) {
6927
- runEffects = runUserEffects;
6928
- const c = createComputation(fn, value, false, STALE), s = SuspenseContext && useContext(SuspenseContext);
6929
- if (s) c.suspense = s;
6930
- if (!options || !options.render) c.user = true;
6931
- Effects ? Effects.push(c) : updateComputation(c);
6932
- }
6933
- function createMemo(fn, value, options) {
6934
- options = options ? Object.assign({}, signalOptions, options) : signalOptions;
6935
- const c = createComputation(fn, value, true, 0);
6936
- c.observers = null;
6937
- c.observerSlots = null;
6938
- c.comparator = options.equals || void 0;
6939
- if (Scheduler && Transition && Transition.running) {
6940
- c.tState = STALE;
6941
- Updates.push(c);
6942
- } else updateComputation(c);
6943
- return readSignal.bind(c);
6944
- }
6945
- function batch(fn) {
6946
- return runUpdates(fn, false);
6947
- }
6948
- function untrack(fn) {
6949
- if (!ExternalSourceConfig && Listener === null) return fn();
6950
- const listener = Listener;
6951
- Listener = null;
6952
- try {
6953
- if (ExternalSourceConfig) return ExternalSourceConfig.untrack(fn);
6954
- return fn();
6955
- } finally {
6956
- Listener = listener;
6957
- }
6958
- }
6959
- function onMount(fn) {
6960
- createEffect(() => untrack(fn));
6961
- }
6962
- function onCleanup(fn) {
6963
- if (Owner === null);
6964
- else if (Owner.cleanups === null) Owner.cleanups = [fn];
6965
- else Owner.cleanups.push(fn);
6966
- return fn;
6967
- }
6968
- function getListener() {
6969
- return Listener;
6970
- }
6971
- function startTransition(fn) {
6972
- if (Transition && Transition.running) {
6973
- fn();
6974
- return Transition.done;
6975
- }
6976
- const l = Listener;
6977
- const o = Owner;
6978
- return Promise.resolve().then(() => {
6979
- Listener = l;
6980
- Owner = o;
6981
- let t;
6982
- if (Scheduler || SuspenseContext) {
6983
- t = Transition || (Transition = {
6984
- sources: /* @__PURE__ */ new Set(),
6985
- effects: [],
6986
- promises: /* @__PURE__ */ new Set(),
6987
- disposed: /* @__PURE__ */ new Set(),
6988
- queue: /* @__PURE__ */ new Set(),
6989
- running: true
6990
- });
6991
- t.done || (t.done = new Promise((res) => t.resolve = res));
6992
- t.running = true;
6993
- }
6994
- runUpdates(fn, false);
6995
- Listener = Owner = null;
6996
- return t ? t.done : void 0;
6997
- });
6998
- }
6999
- var [transPending, setTransPending] = /* @__PURE__ */ createSignal(false);
7000
- function useContext(context) {
7001
- let value;
7002
- return Owner && Owner.context && (value = Owner.context[context.id]) !== void 0 ? value : context.defaultValue;
7003
- }
7004
- var SuspenseContext;
7005
- function readSignal() {
7006
- const runningTransition = Transition && Transition.running;
7007
- if (this.sources && (runningTransition ? this.tState : this.state)) if ((runningTransition ? this.tState : this.state) === STALE) updateComputation(this);
7008
- else {
7009
- const updates = Updates;
7010
- Updates = null;
7011
- runUpdates(() => lookUpstream(this), false);
7012
- Updates = updates;
7013
- }
7014
- if (Listener) {
7015
- const sSlot = this.observers ? this.observers.length : 0;
7016
- if (!Listener.sources) {
7017
- Listener.sources = [this];
7018
- Listener.sourceSlots = [sSlot];
7019
- } else {
7020
- Listener.sources.push(this);
7021
- Listener.sourceSlots.push(sSlot);
7022
- }
7023
- if (!this.observers) {
7024
- this.observers = [Listener];
7025
- this.observerSlots = [Listener.sources.length - 1];
7026
- } else {
7027
- this.observers.push(Listener);
7028
- this.observerSlots.push(Listener.sources.length - 1);
7029
- }
7030
- }
7031
- if (runningTransition && Transition.sources.has(this)) return this.tValue;
7032
- return this.value;
7033
- }
7034
- function writeSignal(node, value, isComp) {
7035
- let current = Transition && Transition.running && Transition.sources.has(node) ? node.tValue : node.value;
7036
- if (!node.comparator || !node.comparator(current, value)) {
7037
- if (Transition) {
7038
- const TransitionRunning = Transition.running;
7039
- if (TransitionRunning || !isComp && Transition.sources.has(node)) {
7040
- Transition.sources.add(node);
7041
- node.tValue = value;
7042
- }
7043
- if (!TransitionRunning) node.value = value;
7044
- } else node.value = value;
7045
- if (node.observers && node.observers.length) runUpdates(() => {
7046
- for (let i = 0; i < node.observers.length; i += 1) {
7047
- const o = node.observers[i];
7048
- const TransitionRunning = Transition && Transition.running;
7049
- if (TransitionRunning && Transition.disposed.has(o)) continue;
7050
- if (TransitionRunning ? !o.tState : !o.state) {
7051
- if (o.pure) Updates.push(o);
7052
- else Effects.push(o);
7053
- if (o.observers) markDownstream(o);
7054
- }
7055
- if (!TransitionRunning) o.state = STALE;
7056
- else o.tState = STALE;
7057
- }
7058
- if (Updates.length > 1e6) {
7059
- Updates = [];
7060
- throw new Error();
7061
- }
7062
- }, false);
7063
- }
7064
- return value;
7065
- }
7066
- function updateComputation(node) {
7067
- if (!node.fn) return;
7068
- cleanNode(node);
7069
- const time = ExecCount;
7070
- runComputation(node, Transition && Transition.running && Transition.sources.has(node) ? node.tValue : node.value, time);
7071
- if (Transition && !Transition.running && Transition.sources.has(node)) queueMicrotask(() => {
7072
- runUpdates(() => {
7073
- Transition && (Transition.running = true);
7074
- Listener = Owner = node;
7075
- runComputation(node, node.tValue, time);
7076
- Listener = Owner = null;
7077
- }, false);
7078
- });
7079
- }
7080
- function runComputation(node, value, time) {
7081
- let nextValue;
7082
- const owner = Owner, listener = Listener;
7083
- Listener = Owner = node;
7084
- try {
7085
- nextValue = node.fn(value);
7086
- } catch (err) {
7087
- if (node.pure) if (Transition && Transition.running) {
7088
- node.tState = STALE;
7089
- node.tOwned && node.tOwned.forEach(cleanNode);
7090
- node.tOwned = void 0;
7091
- } else {
7092
- node.state = STALE;
7093
- node.owned && node.owned.forEach(cleanNode);
7094
- node.owned = null;
7095
- }
7096
- node.updatedAt = time + 1;
7097
- return handleError(err);
7098
- } finally {
7099
- Listener = listener;
7100
- Owner = owner;
7101
- }
7102
- if (!node.updatedAt || node.updatedAt <= time) {
7103
- if (node.updatedAt != null && "observers" in node) writeSignal(node, nextValue, true);
7104
- else if (Transition && Transition.running && node.pure) {
7105
- if (!Transition.sources.has(node)) node.value = nextValue;
7106
- Transition.sources.add(node);
7107
- node.tValue = nextValue;
7108
- } else node.value = nextValue;
7109
- node.updatedAt = time;
7110
- }
7111
- }
7112
- function createComputation(fn, init, pure, state = STALE, options) {
7113
- const c = {
7114
- fn,
7115
- state,
7116
- updatedAt: null,
7117
- owned: null,
7118
- sources: null,
7119
- sourceSlots: null,
7120
- cleanups: null,
7121
- value: init,
7122
- owner: Owner,
7123
- context: Owner ? Owner.context : null,
7124
- pure
7125
- };
7126
- if (Transition && Transition.running) {
7127
- c.state = 0;
7128
- c.tState = state;
7129
- }
7130
- if (Owner === null);
7131
- else if (Owner !== UNOWNED) if (Transition && Transition.running && Owner.pure) if (!Owner.tOwned) Owner.tOwned = [c];
7132
- else Owner.tOwned.push(c);
7133
- else if (!Owner.owned) Owner.owned = [c];
7134
- else Owner.owned.push(c);
7135
- if (ExternalSourceConfig && c.fn) {
7136
- const sourceFn = c.fn;
7137
- const [track, trigger] = createSignal(void 0, { equals: false });
7138
- const ordinary = ExternalSourceConfig.factory(sourceFn, trigger);
7139
- onCleanup(() => ordinary.dispose());
7140
- let inTransition;
7141
- const triggerInTransition = () => startTransition(trigger).then(() => {
7142
- if (inTransition) {
7143
- inTransition.dispose();
7144
- inTransition = void 0;
7145
- }
7146
- });
7147
- c.fn = (x) => {
7148
- track();
7149
- if (Transition && Transition.running) {
7150
- if (!inTransition) inTransition = ExternalSourceConfig.factory(sourceFn, triggerInTransition);
7151
- return inTransition.track(x);
7152
- }
7153
- return ordinary.track(x);
7154
- };
7155
- }
7156
- return c;
7157
- }
7158
- function runTop(node) {
7159
- const runningTransition = Transition && Transition.running;
7160
- if ((runningTransition ? node.tState : node.state) === 0) return;
7161
- if ((runningTransition ? node.tState : node.state) === PENDING) return lookUpstream(node);
7162
- if (node.suspense && untrack(node.suspense.inFallback)) return node.suspense.effects.push(node);
7163
- const ancestors = [node];
7164
- while ((node = node.owner) && (!node.updatedAt || node.updatedAt < ExecCount)) {
7165
- if (runningTransition && Transition.disposed.has(node)) return;
7166
- if (runningTransition ? node.tState : node.state) ancestors.push(node);
7167
- }
7168
- for (let i = ancestors.length - 1; i >= 0; i--) {
7169
- node = ancestors[i];
7170
- if (runningTransition) {
7171
- let top = node, prev = ancestors[i + 1];
7172
- while ((top = top.owner) && top !== prev) if (Transition.disposed.has(top)) return;
7173
- }
7174
- if ((runningTransition ? node.tState : node.state) === STALE) updateComputation(node);
7175
- else if ((runningTransition ? node.tState : node.state) === PENDING) {
7176
- const updates = Updates;
7177
- Updates = null;
7178
- runUpdates(() => lookUpstream(node, ancestors[0]), false);
7179
- Updates = updates;
7180
- }
7181
- }
7182
- }
7183
- function runUpdates(fn, init) {
7184
- if (Updates) return fn();
7185
- let wait = false;
7186
- if (!init) Updates = [];
7187
- if (Effects) wait = true;
7188
- else Effects = [];
7189
- ExecCount++;
7190
- try {
7191
- const res = fn();
7192
- completeUpdates(wait);
7193
- return res;
7194
- } catch (err) {
7195
- if (!wait) Effects = null;
7196
- Updates = null;
7197
- handleError(err);
7198
- }
7199
- }
7200
- function completeUpdates(wait) {
7201
- if (Updates) {
7202
- if (Scheduler && Transition && Transition.running) scheduleQueue(Updates);
7203
- else runQueue(Updates);
7204
- Updates = null;
7205
- }
7206
- if (wait) return;
7207
- let res;
7208
- if (Transition) {
7209
- if (!Transition.promises.size && !Transition.queue.size) {
7210
- const sources = Transition.sources;
7211
- const disposed = Transition.disposed;
7212
- Effects.push.apply(Effects, Transition.effects);
7213
- res = Transition.resolve;
7214
- for (const e of Effects) {
7215
- "tState" in e && (e.state = e.tState);
7216
- delete e.tState;
7217
- }
7218
- Transition = null;
7219
- runUpdates(() => {
7220
- for (const d of disposed) cleanNode(d);
7221
- for (const v of sources) {
7222
- v.value = v.tValue;
7223
- if (v.owned) for (let i = 0, len = v.owned.length; i < len; i++) cleanNode(v.owned[i]);
7224
- if (v.tOwned) v.owned = v.tOwned;
7225
- delete v.tValue;
7226
- delete v.tOwned;
7227
- v.tState = 0;
7228
- }
7229
- setTransPending(false);
7230
- }, false);
7231
- } else if (Transition.running) {
7232
- Transition.running = false;
7233
- Transition.effects.push.apply(Transition.effects, Effects);
7234
- Effects = null;
7235
- setTransPending(true);
7236
- return;
7237
- }
7238
- }
7239
- const e = Effects;
7240
- Effects = null;
7241
- if (e.length) runUpdates(() => runEffects(e), false);
7242
- if (res) res();
7243
- }
7244
- function runQueue(queue) {
7245
- for (let i = 0; i < queue.length; i++) runTop(queue[i]);
7246
- }
7247
- function scheduleQueue(queue) {
7248
- for (let i = 0; i < queue.length; i++) {
7249
- const item = queue[i];
7250
- const tasks = Transition.queue;
7251
- if (!tasks.has(item)) {
7252
- tasks.add(item);
7253
- Scheduler(() => {
7254
- tasks.delete(item);
7255
- runUpdates(() => {
7256
- Transition.running = true;
7257
- runTop(item);
7258
- }, false);
7259
- Transition && (Transition.running = false);
7260
- });
7261
- }
7262
- }
7263
- }
7264
- function runUserEffects(queue) {
7265
- let i, userLength = 0;
7266
- for (i = 0; i < queue.length; i++) {
7267
- const e = queue[i];
7268
- if (!e.user) runTop(e);
7269
- else queue[userLength++] = e;
7270
- }
7271
- if (sharedConfig.context) {
7272
- if (sharedConfig.count) {
7273
- sharedConfig.effects || (sharedConfig.effects = []);
7274
- sharedConfig.effects.push(...queue.slice(0, userLength));
7275
- return;
7276
- }
7277
- setHydrateContext();
7278
- }
7279
- if (sharedConfig.effects && (sharedConfig.done || !sharedConfig.count)) {
7280
- queue = [...sharedConfig.effects, ...queue];
7281
- userLength += sharedConfig.effects.length;
7282
- delete sharedConfig.effects;
7283
- }
7284
- for (i = 0; i < userLength; i++) runTop(queue[i]);
7285
- }
7286
- function lookUpstream(node, ignore) {
7287
- const runningTransition = Transition && Transition.running;
7288
- if (runningTransition) node.tState = 0;
7289
- else node.state = 0;
7290
- for (let i = 0; i < node.sources.length; i += 1) {
7291
- const source = node.sources[i];
7292
- if (source.sources) {
7293
- const state = runningTransition ? source.tState : source.state;
7294
- if (state === STALE) {
7295
- if (source !== ignore && (!source.updatedAt || source.updatedAt < ExecCount)) runTop(source);
7296
- } else if (state === PENDING) lookUpstream(source, ignore);
7297
- }
7298
- }
7299
- }
7300
- function markDownstream(node) {
7301
- const runningTransition = Transition && Transition.running;
7302
- for (let i = 0; i < node.observers.length; i += 1) {
7303
- const o = node.observers[i];
7304
- if (runningTransition ? !o.tState : !o.state) {
7305
- if (runningTransition) o.tState = PENDING;
7306
- else o.state = PENDING;
7307
- if (o.pure) Updates.push(o);
7308
- else Effects.push(o);
7309
- o.observers && markDownstream(o);
7310
- }
7311
- }
7312
- }
7313
- function cleanNode(node) {
7314
- let i;
7315
- if (node.sources) while (node.sources.length) {
7316
- const source = node.sources.pop(), index = node.sourceSlots.pop(), obs = source.observers;
7317
- if (obs && obs.length) {
7318
- const n = obs.pop(), s = source.observerSlots.pop();
7319
- if (index < obs.length) {
7320
- n.sourceSlots[s] = index;
7321
- obs[index] = n;
7322
- source.observerSlots[index] = s;
7323
- }
7324
- }
7325
- }
7326
- if (node.tOwned) {
7327
- for (i = node.tOwned.length - 1; i >= 0; i--) cleanNode(node.tOwned[i]);
7328
- delete node.tOwned;
7329
- }
7330
- if (Transition && Transition.running && node.pure) reset(node, true);
7331
- else if (node.owned) {
7332
- for (i = node.owned.length - 1; i >= 0; i--) cleanNode(node.owned[i]);
7333
- node.owned = null;
7334
- }
7335
- if (node.cleanups) {
7336
- for (i = node.cleanups.length - 1; i >= 0; i--) node.cleanups[i]();
7337
- node.cleanups = null;
7338
- }
7339
- if (Transition && Transition.running) node.tState = 0;
7340
- else node.state = 0;
7341
- }
7342
- function reset(node, top) {
7343
- if (!top) {
7344
- node.tState = 0;
7345
- Transition.disposed.add(node);
7346
- }
7347
- if (node.owned) for (let i = 0; i < node.owned.length; i++) reset(node.owned[i]);
7348
- }
7349
- function castError(err) {
7350
- if (err instanceof Error) return err;
7351
- return new Error(typeof err === "string" ? err : "Unknown error", { cause: err });
7352
- }
7353
- function runErrors(err, fns, owner) {
7354
- try {
7355
- for (const f of fns) f(err);
7356
- } catch (e) {
7357
- handleError(e, owner && owner.owner || null);
7358
- }
7359
- }
7360
- function handleError(err, owner = Owner) {
7361
- const fns = ERROR && owner && owner.context && owner.context[ERROR];
7362
- const error = castError(err);
7363
- if (!fns) throw error;
7364
- if (Effects) Effects.push({
7365
- fn() {
7366
- runErrors(error, fns, owner);
7367
- },
7368
- state: STALE
7369
- });
7370
- else runErrors(error, fns, owner);
7371
- }
7372
- var FALLBACK = Symbol("fallback");
7373
- function dispose(d) {
7374
- for (let i = 0; i < d.length; i++) d[i]();
7375
- }
7376
- function mapArray(list, mapFn, options = {}) {
7377
- let items = [], mapped = [], disposers = [], len = 0, indexes = mapFn.length > 1 ? [] : null;
7378
- onCleanup(() => dispose(disposers));
7379
- return () => {
7380
- let newItems = list() || [], newLen = newItems.length, i, j;
7381
- newItems[$TRACK];
7382
- return untrack(() => {
7383
- let newIndices, newIndicesNext, temp, tempdisposers, tempIndexes, start, end, newEnd, item;
7384
- if (newLen === 0) {
7385
- if (len !== 0) {
7386
- dispose(disposers);
7387
- disposers = [];
7388
- items = [];
7389
- mapped = [];
7390
- len = 0;
7391
- indexes && (indexes = []);
7392
- }
7393
- if (options.fallback) {
7394
- items = [FALLBACK];
7395
- mapped[0] = createRoot((disposer) => {
7396
- disposers[0] = disposer;
7397
- return options.fallback();
7398
- });
7399
- len = 1;
7400
- }
7401
- } else if (len === 0) {
7402
- mapped = new Array(newLen);
7403
- for (j = 0; j < newLen; j++) {
7404
- items[j] = newItems[j];
7405
- mapped[j] = createRoot(mapper);
7406
- }
7407
- len = newLen;
7408
- } else {
7409
- temp = new Array(newLen);
7410
- tempdisposers = new Array(newLen);
7411
- indexes && (tempIndexes = new Array(newLen));
7412
- for (start = 0, end = Math.min(len, newLen); start < end && items[start] === newItems[start]; start++);
7413
- for (end = len - 1, newEnd = newLen - 1; end >= start && newEnd >= start && items[end] === newItems[newEnd]; end--, newEnd--) {
7414
- temp[newEnd] = mapped[end];
7415
- tempdisposers[newEnd] = disposers[end];
7416
- indexes && (tempIndexes[newEnd] = indexes[end]);
7417
- }
7418
- newIndices = /* @__PURE__ */ new Map();
7419
- newIndicesNext = new Array(newEnd + 1);
7420
- for (j = newEnd; j >= start; j--) {
7421
- item = newItems[j];
7422
- i = newIndices.get(item);
7423
- newIndicesNext[j] = i === void 0 ? -1 : i;
7424
- newIndices.set(item, j);
7425
- }
7426
- for (i = start; i <= end; i++) {
7427
- item = items[i];
7428
- j = newIndices.get(item);
7429
- if (j !== void 0 && j !== -1) {
7430
- temp[j] = mapped[i];
7431
- tempdisposers[j] = disposers[i];
7432
- indexes && (tempIndexes[j] = indexes[i]);
7433
- j = newIndicesNext[j];
7434
- newIndices.set(item, j);
7435
- } else disposers[i]();
7436
- }
7437
- for (j = start; j < newLen; j++) if (j in temp) {
7438
- mapped[j] = temp[j];
7439
- disposers[j] = tempdisposers[j];
7440
- if (indexes) {
7441
- indexes[j] = tempIndexes[j];
7442
- indexes[j](j);
7443
- }
7444
- } else mapped[j] = createRoot(mapper);
7445
- mapped = mapped.slice(0, len = newLen);
7446
- items = newItems.slice(0);
7447
- }
7448
- return mapped;
7449
- });
7450
- function mapper(disposer) {
7451
- disposers[j] = disposer;
7452
- if (indexes) {
7453
- const [s, set] = createSignal(j);
7454
- indexes[j] = set;
7455
- return mapFn(newItems[j], s);
7456
- }
7457
- return mapFn(newItems[j]);
7458
- }
7459
- };
7460
- }
7461
- var hydrationEnabled = false;
7462
- function createComponent(Comp, props) {
7463
- if (hydrationEnabled) {
7464
- if (sharedConfig.context) {
7465
- const c = sharedConfig.context;
7466
- setHydrateContext(nextHydrateContext());
7467
- const r = untrack(() => Comp(props || {}));
7468
- setHydrateContext(c);
7469
- return r;
7470
- }
7471
- }
7472
- return untrack(() => Comp(props || {}));
7473
- }
7474
- var narrowedError = (name) => \`Stale read from <\${name}>.\`;
7475
- function For(props) {
7476
- const fallback = "fallback" in props && { fallback: () => props.fallback };
7477
- return createMemo(mapArray(() => props.each, props.children, fallback || void 0));
7478
- }
7479
- function Show(props) {
7480
- const keyed = props.keyed;
7481
- const conditionValue = createMemo(() => props.when, void 0, void 0);
7482
- const condition = keyed ? conditionValue : createMemo(conditionValue, void 0, { equals: (a, b) => !a === !b });
7483
- return createMemo(() => {
7484
- const c = condition();
7485
- if (c) {
7486
- const child = props.children;
7487
- return typeof child === "function" && child.length > 0 ? untrack(() => child(keyed ? c : () => {
7488
- if (!untrack(condition)) throw narrowedError("Show");
7489
- return conditionValue();
7490
- })) : child;
7491
- }
7492
- return props.fallback;
7493
- }, void 0, void 0);
7494
- }
7495
- //#endregion
7496
- //#region node_modules/solid-js/web/dist/web.js
7497
- var memo = (fn) => createMemo(() => fn());
7498
- function reconcileArrays(parentNode, a, b) {
7499
- let bLength = b.length, aEnd = a.length, bEnd = bLength, aStart = 0, bStart = 0, after = a[aEnd - 1].nextSibling, map = null;
7500
- while (aStart < aEnd || bStart < bEnd) {
7501
- if (a[aStart] === b[bStart]) {
7502
- aStart++;
7503
- bStart++;
7504
- continue;
7505
- }
7506
- while (a[aEnd - 1] === b[bEnd - 1]) {
7507
- aEnd--;
7508
- bEnd--;
7509
- }
7510
- if (aEnd === aStart) {
7511
- const node = bEnd < bLength ? bStart ? b[bStart - 1].nextSibling : b[bEnd - bStart] : after;
7512
- while (bStart < bEnd) parentNode.insertBefore(b[bStart++], node);
7513
- } else if (bEnd === bStart) while (aStart < aEnd) {
7514
- if (!map || !map.has(a[aStart])) a[aStart].remove();
7515
- aStart++;
7516
- }
7517
- else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {
7518
- const node = a[--aEnd].nextSibling;
7519
- parentNode.insertBefore(b[bStart++], a[aStart++].nextSibling);
7520
- parentNode.insertBefore(b[--bEnd], node);
7521
- a[aEnd] = b[bEnd];
7522
- } else {
7523
- if (!map) {
7524
- map = /* @__PURE__ */ new Map();
7525
- let i = bStart;
7526
- while (i < bEnd) map.set(b[i], i++);
7527
- }
7528
- const index = map.get(a[aStart]);
7529
- if (index != null) if (bStart < index && index < bEnd) {
7530
- let i = aStart, sequence = 1, t;
7531
- while (++i < aEnd && i < bEnd) {
7532
- if ((t = map.get(a[i])) == null || t !== index + sequence) break;
7533
- sequence++;
7534
- }
7535
- if (sequence > index - bStart) {
7536
- const node = a[aStart];
7537
- while (bStart < index) parentNode.insertBefore(b[bStart++], node);
7538
- } else parentNode.replaceChild(b[bStart++], a[aStart++]);
7539
- } else aStart++;
7540
- else a[aStart++].remove();
7541
- }
7542
- }
7543
- }
7544
- var $$EVENTS = "_$DX_DELEGATE";
7545
- function render(code, element, init, options = {}) {
7546
- let disposer;
7547
- createRoot((dispose) => {
7548
- disposer = dispose;
7549
- element === document ? code() : insert(element, code(), element.firstChild ? null : void 0, init);
7550
- }, options.owner);
7551
- return () => {
7552
- disposer();
7553
- element.textContent = "";
7554
- };
7555
- }
7556
- function template(html, isImportNode, isSVG, isMathML) {
7557
- let node;
7558
- const create = () => {
7559
- const t = isMathML ? document.createElementNS("http://www.w3.org/1998/Math/MathML", "template") : document.createElement("template");
7560
- t.innerHTML = html;
7561
- return isSVG ? t.content.firstChild.firstChild : isMathML ? t.firstChild : t.content.firstChild;
7562
- };
7563
- const fn = isImportNode ? () => untrack(() => document.importNode(node || (node = create()), true)) : () => (node || (node = create())).cloneNode(true);
7564
- fn.cloneNode = fn;
7565
- return fn;
7566
- }
7567
- function delegateEvents(eventNames, document = window.document) {
7568
- const e = document[$$EVENTS] || (document[$$EVENTS] = /* @__PURE__ */ new Set());
7569
- for (let i = 0, l = eventNames.length; i < l; i++) {
7570
- const name = eventNames[i];
7571
- if (!e.has(name)) {
7572
- e.add(name);
7573
- document.addEventListener(name, eventHandler);
7574
- }
7575
- }
7576
- }
7577
- function setAttribute(node, name, value) {
7578
- if (isHydrating(node)) return;
7579
- if (value == null) node.removeAttribute(name);
7580
- else node.setAttribute(name, value);
7581
- }
7582
- function addEventListener(node, name, handler, delegate) {
7583
- if (delegate) if (Array.isArray(handler)) {
7584
- node[\`$$\${name}\`] = handler[0];
7585
- node[\`$$\${name}Data\`] = handler[1];
7586
- } else node[\`$$\${name}\`] = handler;
7587
- else if (Array.isArray(handler)) {
7588
- const handlerFn = handler[0];
7589
- node.addEventListener(name, handler[0] = (e) => handlerFn.call(node, handler[1], e));
7590
- } else node.addEventListener(name, handler, typeof handler !== "function" && handler);
7591
- }
7592
- function insert(parent, accessor, marker, initial) {
7593
- if (marker !== void 0 && !initial) initial = [];
7594
- if (typeof accessor !== "function") return insertExpression(parent, accessor, initial, marker);
7595
- createRenderEffect((current) => insertExpression(parent, accessor(), current, marker), initial);
7596
- }
7597
- function isHydrating(node) {
7598
- return !!sharedConfig.context && !sharedConfig.done && (!node || node.isConnected);
7599
- }
7600
- function eventHandler(e) {
7601
- if (sharedConfig.registry && sharedConfig.events) {
7602
- if (sharedConfig.events.find(([el, ev]) => ev === e)) return;
7603
- }
7604
- let node = e.target;
7605
- const key = \`$$\${e.type}\`;
7606
- const oriTarget = e.target;
7607
- const oriCurrentTarget = e.currentTarget;
7608
- const retarget = (value) => Object.defineProperty(e, "target", {
7609
- configurable: true,
7610
- value
7611
- });
7612
- const handleNode = () => {
7613
- const handler = node[key];
7614
- if (handler && !node.disabled) {
7615
- const data = node[\`\${key}Data\`];
7616
- data !== void 0 ? handler.call(node, data, e) : handler.call(node, e);
7617
- if (e.cancelBubble) return;
7618
- }
7619
- node.host && typeof node.host !== "string" && !node.host._$host && node.contains(e.target) && retarget(node.host);
7620
- return true;
7621
- };
7622
- const walkUpTree = () => {
7623
- while (handleNode() && (node = node._$host || node.parentNode || node.host));
7624
- };
7625
- Object.defineProperty(e, "currentTarget", {
7626
- configurable: true,
7627
- get() {
7628
- return node || document;
7629
- }
7630
- });
7631
- if (sharedConfig.registry && !sharedConfig.done) sharedConfig.done = _$HY.done = true;
7632
- if (e.composedPath) {
7633
- const path = e.composedPath();
7634
- retarget(path[0]);
7635
- for (let i = 0; i < path.length - 2; i++) {
7636
- node = path[i];
7637
- if (!handleNode()) break;
7638
- if (node._$host) {
7639
- node = node._$host;
7640
- walkUpTree();
7641
- break;
7642
- }
7643
- if (node.parentNode === oriCurrentTarget) break;
7644
- }
7645
- } else walkUpTree();
7646
- retarget(oriTarget);
7647
- }
7648
- function insertExpression(parent, value, current, marker, unwrapArray) {
7649
- const hydrating = isHydrating(parent);
7650
- if (hydrating) {
7651
- !current && (current = [...parent.childNodes]);
7652
- let cleaned = [];
7653
- for (let i = 0; i < current.length; i++) {
7654
- const node = current[i];
7655
- if (node.nodeType === 8 && node.data.slice(0, 2) === "!$") node.remove();
7656
- else cleaned.push(node);
7657
- }
7658
- current = cleaned;
7659
- }
7660
- while (typeof current === "function") current = current();
7661
- if (value === current) return current;
7662
- const t = typeof value, multi = marker !== void 0;
7663
- parent = multi && current[0] && current[0].parentNode || parent;
7664
- if (t === "string" || t === "number") {
7665
- if (hydrating) return current;
7666
- if (t === "number") {
7667
- value = value.toString();
7668
- if (value === current) return current;
7669
- }
7670
- if (multi) {
7671
- let node = current[0];
7672
- if (node && node.nodeType === 3) node.data !== value && (node.data = value);
7673
- else node = document.createTextNode(value);
7674
- current = cleanChildren(parent, current, marker, node);
7675
- } else if (current !== "" && typeof current === "string") current = parent.firstChild.data = value;
7676
- else current = parent.textContent = value;
7677
- } else if (value == null || t === "boolean") {
7678
- if (hydrating) return current;
7679
- current = cleanChildren(parent, current, marker);
7680
- } else if (t === "function") {
7681
- createRenderEffect(() => {
7682
- let v = value();
7683
- while (typeof v === "function") v = v();
7684
- current = insertExpression(parent, v, current, marker);
7685
- });
7686
- return () => current;
7687
- } else if (Array.isArray(value)) {
7688
- const array = [];
7689
- const currentArray = current && Array.isArray(current);
7690
- if (normalizeIncomingArray(array, value, current, unwrapArray)) {
7691
- createRenderEffect(() => current = insertExpression(parent, array, current, marker, true));
7692
- return () => current;
7693
- }
7694
- if (hydrating) {
7695
- if (!array.length) return current;
7696
- if (marker === void 0) return current = [...parent.childNodes];
7697
- let node = array[0];
7698
- if (node.parentNode !== parent) return current;
7699
- const nodes = [node];
7700
- while ((node = node.nextSibling) !== marker) nodes.push(node);
7701
- return current = nodes;
7702
- }
7703
- if (array.length === 0) {
7704
- current = cleanChildren(parent, current, marker);
7705
- if (multi) return current;
7706
- } else if (currentArray) if (current.length === 0) appendNodes(parent, array, marker);
7707
- else reconcileArrays(parent, current, array);
7708
- else {
7709
- current && cleanChildren(parent);
7710
- appendNodes(parent, array);
7711
- }
7712
- current = array;
7713
- } else if (value.nodeType) {
7714
- if (hydrating && value.parentNode) return current = multi ? [value] : value;
7715
- if (Array.isArray(current)) {
7716
- if (multi) return current = cleanChildren(parent, current, marker, value);
7717
- cleanChildren(parent, current, null, value);
7718
- } else if (current == null || current === "" || !parent.firstChild) parent.appendChild(value);
7719
- else parent.replaceChild(value, parent.firstChild);
7720
- current = value;
7721
- }
7722
- return current;
7723
- }
7724
- function normalizeIncomingArray(normalized, array, current, unwrap) {
7725
- let dynamic = false;
7726
- for (let i = 0, len = array.length; i < len; i++) {
7727
- let item = array[i], prev = current && current[normalized.length], t;
7728
- if (item == null || item === true || item === false);
7729
- else if ((t = typeof item) === "object" && item.nodeType) normalized.push(item);
7730
- else if (Array.isArray(item)) dynamic = normalizeIncomingArray(normalized, item, prev) || dynamic;
7731
- else if (t === "function") if (unwrap) {
7732
- while (typeof item === "function") item = item();
7733
- dynamic = normalizeIncomingArray(normalized, Array.isArray(item) ? item : [item], Array.isArray(prev) ? prev : [prev]) || dynamic;
7734
- } else {
7735
- normalized.push(item);
7736
- dynamic = true;
7737
- }
7738
- else {
7739
- const value = String(item);
7740
- if (prev && prev.nodeType === 3 && prev.data === value) normalized.push(prev);
7741
- else normalized.push(document.createTextNode(value));
7742
- }
7743
- }
7744
- return dynamic;
7745
- }
7746
- function appendNodes(parent, array, marker = null) {
7747
- for (let i = 0, len = array.length; i < len; i++) parent.insertBefore(array[i], marker);
7748
- }
7749
- function cleanChildren(parent, current, marker, replacement) {
7750
- if (marker === void 0) return parent.textContent = "";
7751
- const node = replacement || document.createTextNode("");
7752
- if (current.length) {
7753
- let inserted = false;
7754
- for (let i = current.length - 1; i >= 0; i--) {
7755
- const el = current[i];
7756
- if (node !== el) {
7757
- const isParent = el.parentNode === parent;
7758
- if (!inserted && !i) isParent ? parent.replaceChild(node, el) : parent.insertBefore(node, marker);
7759
- else isParent && el.remove();
7760
- } else inserted = true;
7761
- }
7762
- } else parent.insertBefore(node, marker);
7763
- return [node];
7764
- }
7765
- //#endregion
7766
- //#region node_modules/solid-js/store/dist/store.js
7767
- var $RAW = Symbol("store-raw"), $NODE = Symbol("store-node"), $HAS = Symbol("store-has"), $SELF = Symbol("store-self");
7768
- function wrap$1(value) {
7769
- let p = value[$PROXY];
7770
- if (!p) {
7771
- Object.defineProperty(value, $PROXY, { value: p = new Proxy(value, proxyTraps$1) });
7772
- if (!Array.isArray(value)) {
7773
- const keys = Object.keys(value), desc = Object.getOwnPropertyDescriptors(value);
7774
- for (let i = 0, l = keys.length; i < l; i++) {
7775
- const prop = keys[i];
7776
- if (desc[prop].get) Object.defineProperty(value, prop, {
7777
- enumerable: desc[prop].enumerable,
7778
- get: desc[prop].get.bind(p)
7779
- });
7780
- }
7781
- }
7782
- }
7783
- return p;
7784
- }
7785
- function isWrappable(obj) {
7786
- let proto;
7787
- return obj != null && typeof obj === "object" && (obj[$PROXY] || !(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype || Array.isArray(obj));
7788
- }
7789
- function unwrap(item, set = /* @__PURE__ */ new Set()) {
7790
- let result, unwrapped, v, prop;
7791
- if (result = item != null && item[$RAW]) return result;
7792
- if (!isWrappable(item) || set.has(item)) return item;
7793
- if (Array.isArray(item)) {
7794
- if (Object.isFrozen(item)) item = item.slice(0);
7795
- else set.add(item);
7796
- for (let i = 0, l = item.length; i < l; i++) {
7797
- v = item[i];
7798
- if ((unwrapped = unwrap(v, set)) !== v) item[i] = unwrapped;
7799
- }
7800
- } else {
7801
- if (Object.isFrozen(item)) item = Object.assign({}, item);
7802
- else set.add(item);
7803
- const keys = Object.keys(item), desc = Object.getOwnPropertyDescriptors(item);
7804
- for (let i = 0, l = keys.length; i < l; i++) {
7805
- prop = keys[i];
7806
- if (desc[prop].get) continue;
7807
- v = item[prop];
7808
- if ((unwrapped = unwrap(v, set)) !== v) item[prop] = unwrapped;
7809
- }
7810
- }
7811
- return item;
7812
- }
7813
- function getNodes(target, symbol) {
7814
- let nodes = target[symbol];
7815
- if (!nodes) Object.defineProperty(target, symbol, { value: nodes = Object.create(null) });
7816
- return nodes;
7817
- }
7818
- function getNode(nodes, property, value) {
7819
- if (nodes[property]) return nodes[property];
7820
- const [s, set] = createSignal(value, {
7821
- equals: false,
7822
- internal: true
7823
- });
7824
- s.$ = set;
7825
- return nodes[property] = s;
7826
- }
7827
- function proxyDescriptor$1(target, property) {
7828
- const desc = Reflect.getOwnPropertyDescriptor(target, property);
7829
- if (!desc || desc.get || !desc.configurable || property === $PROXY || property === $NODE) return desc;
7830
- delete desc.value;
7831
- delete desc.writable;
7832
- desc.get = () => target[$PROXY][property];
7833
- return desc;
7834
- }
7835
- function trackSelf(target) {
7836
- getListener() && getNode(getNodes(target, $NODE), $SELF)();
7837
- }
7838
- function ownKeys(target) {
7839
- trackSelf(target);
7840
- return Reflect.ownKeys(target);
7841
- }
7842
- var proxyTraps$1 = {
7843
- get(target, property, receiver) {
7844
- if (property === $RAW) return target;
7845
- if (property === $PROXY) return receiver;
7846
- if (property === $TRACK) {
7847
- trackSelf(target);
7848
- return receiver;
7849
- }
7850
- const nodes = getNodes(target, $NODE);
7851
- const tracked = nodes[property];
7852
- let value = tracked ? tracked() : target[property];
7853
- if (property === $NODE || property === $HAS || property === "__proto__") return value;
7854
- if (!tracked) {
7855
- const desc = Object.getOwnPropertyDescriptor(target, property);
7856
- if (getListener() && (typeof value !== "function" || target.hasOwnProperty(property)) && !(desc && desc.get)) value = getNode(nodes, property, value)();
7857
- }
7858
- return isWrappable(value) ? wrap$1(value) : value;
7859
- },
7860
- has(target, property) {
7861
- if (property === $RAW || property === $PROXY || property === $TRACK || property === $NODE || property === $HAS || property === "__proto__") return true;
7862
- getListener() && getNode(getNodes(target, $HAS), property)();
7863
- return property in target;
7864
- },
7865
- set() {
7866
- return true;
7867
- },
7868
- deleteProperty() {
7869
- return true;
7870
- },
7871
- ownKeys,
7872
- getOwnPropertyDescriptor: proxyDescriptor$1
7873
- };
7874
- function setProperty(state, property, value, deleting = false) {
7875
- if (!deleting && state[property] === value) return;
7876
- const prev = state[property], len = state.length;
7877
- if (value === void 0) {
7878
- delete state[property];
7879
- if (state[$HAS] && state[$HAS][property] && prev !== void 0) state[$HAS][property].$();
7880
- } else {
7881
- state[property] = value;
7882
- if (state[$HAS] && state[$HAS][property] && prev === void 0) state[$HAS][property].$();
7883
- }
7884
- let nodes = getNodes(state, $NODE), node;
7885
- if (node = getNode(nodes, property, prev)) node.$(() => value);
7886
- if (Array.isArray(state) && state.length !== len) {
7887
- for (let i = state.length; i < len; i++) (node = nodes[i]) && node.$();
7888
- (node = getNode(nodes, "length", len)) && node.$(state.length);
7889
- }
7890
- (node = nodes[$SELF]) && node.$();
7891
- }
7892
- function mergeStoreNode(state, value) {
7893
- const keys = Object.keys(value);
7894
- for (let i = 0; i < keys.length; i += 1) {
7895
- const key = keys[i];
7896
- setProperty(state, key, value[key]);
7897
- }
7898
- }
7899
- function updateArray(current, next) {
7900
- if (typeof next === "function") next = next(current);
7901
- next = unwrap(next);
7902
- if (Array.isArray(next)) {
7903
- if (current === next) return;
7904
- let i = 0, len = next.length;
7905
- for (; i < len; i++) {
7906
- const value = next[i];
7907
- if (current[i] !== value) setProperty(current, i, value);
7908
- }
7909
- setProperty(current, "length", len);
7910
- } else mergeStoreNode(current, next);
7911
- }
7912
- function updatePath(current, path, traversed = []) {
7913
- let part, prev = current;
7914
- if (path.length > 1) {
7915
- part = path.shift();
7916
- const partType = typeof part, isArray = Array.isArray(current);
7917
- if (Array.isArray(part)) {
7918
- for (let i = 0; i < part.length; i++) updatePath(current, [part[i]].concat(path), traversed);
7919
- return;
7920
- } else if (isArray && partType === "function") {
7921
- for (let i = 0; i < current.length; i++) if (part(current[i], i)) updatePath(current, [i].concat(path), traversed);
7922
- return;
7923
- } else if (isArray && partType === "object") {
7924
- const { from = 0, to = current.length - 1, by = 1 } = part;
7925
- for (let i = from; i <= to; i += by) updatePath(current, [i].concat(path), traversed);
7926
- return;
7927
- } else if (path.length > 1) {
7928
- updatePath(current[part], path, [part].concat(traversed));
7929
- return;
7930
- }
7931
- prev = current[part];
7932
- traversed = [part].concat(traversed);
7933
- }
7934
- let value = path[0];
7935
- if (typeof value === "function") {
7936
- value = value(prev, traversed);
7937
- if (value === prev) return;
7938
- }
7939
- if (part === void 0 && value == void 0) return;
7940
- value = unwrap(value);
7941
- if (part === void 0 || isWrappable(prev) && isWrappable(value) && !Array.isArray(value)) mergeStoreNode(prev, value);
7942
- else setProperty(current, part, value);
7943
- }
7944
- function createStore(...[store, options]) {
7945
- const unwrappedStore = unwrap(store || {});
7946
- const isArray = Array.isArray(unwrappedStore);
7947
- const wrappedStore = wrap$1(unwrappedStore);
7948
- function setStore(...args) {
7949
- batch(() => {
7950
- isArray && args.length === 1 ? updateArray(unwrappedStore, args[0]) : updatePath(unwrappedStore, args);
7951
- });
7952
- }
7953
- return [wrappedStore, setStore];
7954
- }
7955
- //#endregion
7956
- //#region src/transport.ts
7957
- var granolaTransportPaths = {
7958
- authLock: "/auth/lock",
7959
- authLogin: "/auth/login",
7960
- authLogout: "/auth/logout",
7961
- authMode: "/auth/mode",
7962
- authRefresh: "/auth/refresh",
7963
- authStatus: "/auth/status",
7964
- authUnlock: "/auth/unlock",
7965
- automationMatches: "/automation/matches",
7966
- automationRules: "/automation/rules",
7967
- automationRuns: "/automation/runs",
7968
- events: "/events",
7969
- exportJobs: "/exports/jobs",
7970
- exportNotes: "/exports/notes",
7971
- exportTranscripts: "/exports/transcripts",
7972
- folderResolve: "/folders/resolve",
7973
- folders: "/folders",
7974
- health: "/health",
7975
- meetingResolve: "/meetings/resolve",
7976
- meetings: "/meetings",
7977
- root: "/",
7978
- serverInfo: "/server/info",
7979
- syncRun: "/sync",
7980
- syncEvents: "/sync/events",
7981
- state: "/state"
7982
- };
7983
- function appendSearchParams(path, params) {
7984
- const url = new URL(path, "http://localhost");
7985
- for (const [key, value] of Object.entries(params)) {
7986
- if (value === void 0 || value === false || value === "") continue;
7987
- url.searchParams.set(key, String(value));
7988
- }
7989
- return \`\${url.pathname}\${url.search}\`;
7990
- }
7991
- function granolaMeetingPath(id) {
7992
- return \`\${granolaTransportPaths.meetings}/\${encodeURIComponent(id)}\`;
7993
- }
7994
- function granolaMeetingResolvePath(query, options = {}) {
7995
- return appendSearchParams(granolaTransportPaths.meetingResolve, {
7996
- includeTranscript: options.includeTranscript ? "true" : void 0,
7997
- q: query
7998
- });
7999
- }
8000
- function granolaMeetingsPath(options = {}) {
8001
- return appendSearchParams(granolaTransportPaths.meetings, {
8002
- folderId: options.folderId,
8003
- limit: options.limit,
8004
- refresh: options.forceRefresh ? "true" : void 0,
8005
- search: options.search,
8006
- sort: options.sort,
8007
- updatedFrom: options.updatedFrom,
8008
- updatedTo: options.updatedTo
8009
- });
8010
- }
8011
- function granolaFolderPath(id) {
8012
- return \`\${granolaTransportPaths.folders}/\${encodeURIComponent(id)}\`;
8013
- }
8014
- function granolaFolderResolvePath(query) {
8015
- return appendSearchParams(granolaTransportPaths.folderResolve, { q: query });
8016
- }
8017
- function granolaFoldersPath(options = {}) {
8018
- return appendSearchParams(granolaTransportPaths.folders, {
8019
- limit: options.limit,
8020
- refresh: options.forceRefresh ? "true" : void 0,
8021
- search: options.search
8022
- });
8023
- }
8024
- function granolaExportJobsPath(options = {}) {
8025
- return appendSearchParams(granolaTransportPaths.exportJobs, { limit: options.limit });
8026
- }
8027
- function granolaAutomationRunsPath(options = {}) {
8028
- return appendSearchParams(granolaTransportPaths.automationRuns, {
8029
- limit: options.limit,
8030
- status: options.status
8031
- });
8032
- }
8033
- function granolaAutomationRunDecisionPath(id, decision) {
8034
- return \`\${granolaTransportPaths.automationRuns}/\${encodeURIComponent(id)}/\${decision}\`;
8035
- }
8036
- function granolaExportJobRerunPath(id) {
8037
- return \`\${granolaTransportPaths.exportJobs}/\${encodeURIComponent(id)}/rerun\`;
8038
- }
8039
- //#endregion
8040
- //#region \0@oxc-project+runtime@0.122.0/helpers/checkPrivateRedeclaration.js
8041
- function _checkPrivateRedeclaration(e, t) {
8042
- if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object");
8043
- }
8044
- //#endregion
8045
- //#region \0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldInitSpec.js
8046
- function _classPrivateFieldInitSpec(e, t, a) {
8047
- _checkPrivateRedeclaration(e, t), t.set(e, a);
8048
- }
8049
- //#endregion
8050
- //#region \0@oxc-project+runtime@0.122.0/helpers/typeof.js
8051
- function _typeof(o) {
8052
- "@babel/helpers - typeof";
8053
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
8054
- return typeof o;
8055
- } : function(o) {
8056
- return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
8057
- }, _typeof(o);
8058
- }
8059
- //#endregion
8060
- //#region \0@oxc-project+runtime@0.122.0/helpers/toPrimitive.js
8061
- function toPrimitive(t, r) {
8062
- if ("object" != _typeof(t) || !t) return t;
8063
- var e = t[Symbol.toPrimitive];
8064
- if (void 0 !== e) {
8065
- var i = e.call(t, r || "default");
8066
- if ("object" != _typeof(i)) return i;
8067
- throw new TypeError("@@toPrimitive must return a primitive value.");
8068
- }
8069
- return ("string" === r ? String : Number)(t);
8070
- }
8071
- //#endregion
8072
- //#region \0@oxc-project+runtime@0.122.0/helpers/toPropertyKey.js
8073
- function toPropertyKey(t) {
8074
- var i = toPrimitive(t, "string");
8075
- return "symbol" == _typeof(i) ? i : i + "";
8076
- }
8077
- //#endregion
8078
- //#region \0@oxc-project+runtime@0.122.0/helpers/defineProperty.js
8079
- function _defineProperty(e, r, t) {
8080
- return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
8081
- value: t,
8082
- enumerable: !0,
8083
- configurable: !0,
8084
- writable: !0
8085
- }) : e[r] = t, e;
8086
- }
8087
- //#endregion
8088
- //#region \0@oxc-project+runtime@0.122.0/helpers/assertClassBrand.js
8089
- function _assertClassBrand(e, t, n) {
8090
- if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
8091
- throw new TypeError("Private element is not present on this object");
8092
- }
8093
- //#endregion
8094
- //#region \0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldSet2.js
8095
- function _classPrivateFieldSet2(s, a, r) {
8096
- return s.set(_assertClassBrand(s, a), r), r;
8097
- }
8098
- //#endregion
8099
- //#region \0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldGet2.js
8100
- function _classPrivateFieldGet2(s, a) {
8101
- return s.get(_assertClassBrand(s, a));
8102
- }
8103
- //#endregion
8104
- //#region src/server/client.ts
8105
- function cloneValue(value) {
8106
- return structuredClone(value);
8107
- }
8108
- function normaliseServerUrl(serverUrl) {
8109
- const raw = serverUrl instanceof URL ? serverUrl.href : serverUrl.trim();
8110
- if (!raw) throw new Error("server URL is required");
8111
- const withProtocol = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw) ? raw : \`http://\${raw}\`;
8112
- const parsed = new URL(withProtocol);
8113
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("server URL must use http or https");
8114
- parsed.pathname = "/";
8115
- parsed.search = "";
8116
- parsed.hash = "";
8117
- return parsed;
8118
- }
8119
- function mergeHeaders(...values) {
8120
- const headers = new Headers();
8121
- for (const value of values) {
8122
- if (!value) continue;
8123
- new Headers(value).forEach((headerValue, headerName) => {
8124
- headers.set(headerName, headerValue);
8125
- });
8126
- }
8127
- return headers;
8128
- }
8129
- async function responseError(response) {
8130
- let message = \`\${response.status} \${response.statusText}\`.trim();
8131
- try {
8132
- const payload = await response.json();
8133
- if (typeof payload.error === "string" && payload.error.trim()) message = payload.error;
8134
- else if (typeof payload.message === "string" && payload.message.trim()) message = payload.message;
8135
- } catch {
8136
- const text = (await response.text()).trim();
8137
- if (text) message = text;
8138
- }
8139
- return new Error(message);
8140
- }
8141
- function parseSseEvent(payload) {
8142
- const data = payload.replaceAll("\r\n", "\n").split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
8143
- if (!data) return;
8144
- return JSON.parse(data);
8145
- }
8146
- var _closed = /* @__PURE__ */ new WeakMap();
8147
- var _eventLoop = /* @__PURE__ */ new WeakMap();
8148
- var _listeners = /* @__PURE__ */ new WeakMap();
8149
- var _fetchImpl = /* @__PURE__ */ new WeakMap();
8150
- var _password = /* @__PURE__ */ new WeakMap();
8151
- var _reconnectDelayMs = /* @__PURE__ */ new WeakMap();
8152
- var _streamAbortController = /* @__PURE__ */ new WeakMap();
8153
- var _state = /* @__PURE__ */ new WeakMap();
8154
- var GranolaServerClient = class GranolaServerClient {
8155
- constructor(info, url, initialState, options = {}) {
8156
- _classPrivateFieldInitSpec(this, _closed, false);
8157
- _classPrivateFieldInitSpec(this, _eventLoop, void 0);
8158
- _classPrivateFieldInitSpec(this, _listeners, /* @__PURE__ */ new Set());
8159
- _classPrivateFieldInitSpec(this, _fetchImpl, void 0);
8160
- _classPrivateFieldInitSpec(this, _password, void 0);
8161
- _classPrivateFieldInitSpec(this, _reconnectDelayMs, void 0);
8162
- _defineProperty(this, "info", void 0);
8163
- _classPrivateFieldInitSpec(this, _streamAbortController, void 0);
8164
- _classPrivateFieldInitSpec(this, _state, void 0);
8165
- this.url = url;
8166
- _classPrivateFieldSet2(_fetchImpl, this, options.fetchImpl ?? fetch);
8167
- this.info = cloneValue(info);
8168
- _classPrivateFieldSet2(_password, this, options.password?.trim() || void 0);
8169
- _classPrivateFieldSet2(_reconnectDelayMs, this, options.reconnectDelayMs ?? 1e3);
8170
- _classPrivateFieldSet2(_state, this, cloneValue(initialState));
8171
- }
8172
- static async connect(serverUrl, options = {}) {
8173
- const url = normaliseServerUrl(serverUrl);
8174
- const fetchImpl = options.fetchImpl ?? fetch;
8175
- const infoResponse = await fetchImpl(new URL(granolaTransportPaths.serverInfo, url), { headers: mergeHeaders({
8176
- ...options.password?.trim() ? { "x-granola-password": options.password.trim() } : {},
8177
- accept: "application/json"
8178
- }) });
8179
- if (!infoResponse.ok) throw await responseError(infoResponse);
8180
- const info = await infoResponse.json();
8181
- if (info.protocolVersion !== 2) throw new Error(\`unsupported Granola transport protocol: expected 2, got \${info.protocolVersion}\`);
8182
- const response = await fetchImpl(new URL(granolaTransportPaths.state, url), { headers: mergeHeaders({
8183
- ...options.password?.trim() ? { "x-granola-password": options.password.trim() } : {},
8184
- accept: "application/json"
8185
- }) });
8186
- if (!response.ok) throw await responseError(response);
8187
- const client = new GranolaServerClient(info, url, await response.json(), options);
8188
- client.startEvents();
8189
- return client;
8190
- }
8191
- async close() {
8192
- _classPrivateFieldSet2(_closed, this, true);
8193
- _classPrivateFieldGet2(_streamAbortController, this)?.abort();
8194
- try {
8195
- await _classPrivateFieldGet2(_eventLoop, this);
8196
- } catch {}
8197
- }
8198
- getState() {
8199
- return cloneValue(_classPrivateFieldGet2(_state, this));
8200
- }
8201
- subscribe(listener) {
8202
- _classPrivateFieldGet2(_listeners, this).add(listener);
8203
- return () => {
8204
- _classPrivateFieldGet2(_listeners, this).delete(listener);
8205
- };
8206
- }
8207
- async inspectAuth() {
8208
- return await this.requestJson(granolaTransportPaths.authStatus);
8209
- }
8210
- async listAutomationRules() {
8211
- return await this.requestJson(granolaTransportPaths.automationRules);
8212
- }
8213
- async listAutomationMatches(options = {}) {
8214
- const path = options.limit ? \`\${granolaTransportPaths.automationMatches}?limit=\${encodeURIComponent(String(options.limit))}\` : granolaTransportPaths.automationMatches;
8215
- return await this.requestJson(path);
8216
- }
8217
- async listAutomationRuns(options = {}) {
8218
- return await this.requestJson(granolaAutomationRunsPath(options));
8219
- }
8220
- async resolveAutomationRun(id, decision, options = {}) {
8221
- return await this.requestJson(granolaAutomationRunDecisionPath(id, decision), {
8222
- body: JSON.stringify(options),
8223
- headers: { "content-type": "application/json" },
8224
- method: "POST"
8225
- });
8226
- }
8227
- async inspectSync() {
8228
- return cloneValue(_classPrivateFieldGet2(_state, this).sync);
8229
- }
8230
- async listSyncEvents(options = {}) {
8231
- const path = options.limit ? \`\${granolaTransportPaths.syncEvents}?limit=\${encodeURIComponent(String(options.limit))}\` : granolaTransportPaths.syncEvents;
8232
- return await this.requestJson(path);
8233
- }
8234
- async loginAuth(options = {}) {
8235
- return await this.requestJson(granolaTransportPaths.authLogin, {
8236
- body: JSON.stringify(options),
8237
- headers: { "content-type": "application/json" },
8238
- method: "POST"
8239
- });
8240
- }
8241
- async logoutAuth() {
8242
- return await this.requestJson(granolaTransportPaths.authLogout, { method: "POST" });
8243
- }
8244
- async refreshAuth() {
8245
- return await this.requestJson(granolaTransportPaths.authRefresh, { method: "POST" });
8246
- }
8247
- async switchAuthMode(mode) {
8248
- return await this.requestJson(granolaTransportPaths.authMode, {
8249
- body: JSON.stringify({ mode }),
8250
- headers: { "content-type": "application/json" },
8251
- method: "POST"
8252
- });
8253
- }
8254
- async sync(options = {}) {
8255
- return await this.requestJson(granolaTransportPaths.syncRun, {
8256
- body: JSON.stringify(options),
8257
- headers: { "content-type": "application/json" },
8258
- method: "POST"
8259
- });
8260
- }
8261
- async listFolders(options = {}) {
8262
- return await this.requestJson(granolaFoldersPath(options));
8263
- }
8264
- async getFolder(id) {
8265
- return await this.requestJson(granolaFolderPath(id));
8266
- }
8267
- async findFolder(query) {
8268
- return await this.requestJson(granolaFolderResolvePath(query));
8269
- }
8270
- async listMeetings(options = {}) {
8271
- return await this.requestJson(granolaMeetingsPath(options));
8272
- }
8273
- async getMeeting(id, options = {}) {
8274
- return await this.requestJson(\`\${granolaMeetingPath(id)}\${options.requireCache ? "?includeTranscript=true" : ""}\`);
8275
- }
8276
- async findMeeting(query, options = {}) {
8277
- return await this.requestJson(granolaMeetingResolvePath(query, { includeTranscript: options.requireCache }));
8278
- }
8279
- async listExportJobs(options = {}) {
8280
- return await this.requestJson(granolaExportJobsPath(options));
8281
- }
8282
- async exportNotes(format = "markdown", options = {}) {
8283
- return await this.requestJson(granolaTransportPaths.exportNotes, {
8284
- body: JSON.stringify({
8285
- folderId: options.folderId,
8286
- format
8287
- }),
8288
- headers: { "content-type": "application/json" },
8289
- method: "POST"
8290
- });
8291
- }
8292
- async exportTranscripts(format = "text", options = {}) {
8293
- return await this.requestJson(granolaTransportPaths.exportTranscripts, {
8294
- body: JSON.stringify({
8295
- folderId: options.folderId,
8296
- format
8297
- }),
8298
- headers: { "content-type": "application/json" },
8299
- method: "POST"
8300
- });
8301
- }
8302
- async rerunExportJob(id) {
8303
- return await this.requestJson(granolaExportJobRerunPath(id), { method: "POST" });
8304
- }
8305
- async request(path, init = {}) {
8306
- const response = await _classPrivateFieldGet2(_fetchImpl, this).call(this, new URL(path, this.url), {
8307
- ...init,
8308
- headers: mergeHeaders({
8309
- ..._classPrivateFieldGet2(_password, this) ? { "x-granola-password": _classPrivateFieldGet2(_password, this) } : {},
8310
- accept: "application/json"
8311
- }, init.headers)
8312
- });
8313
- if (!response.ok) throw await responseError(response);
8314
- return response;
8315
- }
8316
- async requestJson(path, init = {}) {
8317
- return cloneValue(await (await this.request(path, init)).json());
8318
- }
8319
- emit(event) {
8320
- _classPrivateFieldSet2(_state, this, cloneValue(event.state));
8321
- const nextEvent = cloneValue(event);
8322
- for (const listener of _classPrivateFieldGet2(_listeners, this)) listener(nextEvent);
8323
- }
8324
- startEvents() {
8325
- if (_classPrivateFieldGet2(_eventLoop, this)) return;
8326
- _classPrivateFieldSet2(_eventLoop, this, this.runEventsLoop());
8327
- }
8328
- async runEventsLoop() {
8329
- while (!_classPrivateFieldGet2(_closed, this)) {
8330
- const controller = new AbortController();
8331
- _classPrivateFieldSet2(_streamAbortController, this, controller);
8332
- try {
8333
- const response = await this.request(granolaTransportPaths.events, {
8334
- headers: { accept: "text/event-stream" },
8335
- signal: controller.signal
8336
- });
8337
- await this.consumeEventStream(response);
8338
- } catch {
8339
- if (_classPrivateFieldGet2(_closed, this) || controller.signal.aborted) break;
8340
- await new Promise((resolve) => {
8341
- setTimeout(resolve, _classPrivateFieldGet2(_reconnectDelayMs, this));
8342
- });
8343
- }
8344
- }
8345
- }
8346
- async consumeEventStream(response) {
8347
- const reader = response.body?.getReader();
8348
- if (!reader) throw new Error("server did not provide an event stream");
8349
- const decoder = new TextDecoder();
8350
- let buffer = "";
8351
- while (!_classPrivateFieldGet2(_closed, this)) {
8352
- const { done, value } = await reader.read();
8353
- if (done) return;
8354
- buffer += decoder.decode(value, { stream: true });
8355
- buffer = buffer.replaceAll("\r\n", "\n");
8356
- while (true) {
8357
- const boundary = buffer.indexOf("\n\n");
8358
- if (boundary < 0) break;
8359
- const chunk = buffer.slice(0, boundary);
8360
- buffer = buffer.slice(boundary + 2);
8361
- const event = parseSseEvent(chunk);
8362
- if (event) this.emit(event);
8363
- }
8364
- }
8365
- }
8366
- };
8367
- async function createGranolaServerClient(serverUrl, options = {}) {
8368
- return await GranolaServerClient.connect(serverUrl, options);
8369
- }
8370
- //#endregion
8371
- //#region src/web/client-state.ts
8372
- function parseWorkspaceTab(value) {
8373
- switch (value) {
8374
- case "metadata":
8375
- case "raw":
8376
- case "transcript": return value;
8377
- default: return "notes";
8378
- }
8379
- }
8380
- function startupSelectionFromSearch(search) {
8381
- const params = new URLSearchParams(search);
8382
- return {
8383
- folderId: params.get("folder")?.trim() || "",
8384
- meetingId: params.get("meeting")?.trim() || "",
8385
- workspaceTab: parseWorkspaceTab(params.get("tab"))
8386
- };
8387
- }
8388
- function buildBrowserUrlPath(currentHref, selection) {
8389
- const url = new URL(currentHref);
8390
- if (selection.selectedFolderId) url.searchParams.set("folder", selection.selectedFolderId);
8391
- else url.searchParams.delete("folder");
8392
- if (selection.selectedMeetingId) url.searchParams.set("meeting", selection.selectedMeetingId);
8393
- else url.searchParams.delete("meeting");
8394
- if (parseWorkspaceTab(selection.workspaceTab) !== "notes") url.searchParams.set("tab", parseWorkspaceTab(selection.workspaceTab));
8395
- else url.searchParams.delete("tab");
8396
- return \`\${url.pathname}\${url.search}\${url.hash}\`;
8397
- }
8398
- function exportScopeLabel(scope) {
8399
- return scope && scope.mode === "folder" ? \`Folder: \${scope.folderName || scope.folderId}\` : "Scope: All meetings";
8400
- }
8401
- function currentFilterSummary(filters) {
8402
- const parts = [];
8403
- if (filters.selectedFolderId) {
8404
- const folder = filters.folders.find((candidate) => candidate.id === filters.selectedFolderId);
8405
- parts.push(\`folder "\${folder ? folder.name : filters.selectedFolderId}"\`);
8406
- }
8407
- if (filters.search) parts.push(\`search "\${filters.search}"\`);
8408
- if (filters.updatedFrom) parts.push(\`from \${filters.updatedFrom}\`);
8409
- if (filters.updatedTo) parts.push(\`to \${filters.updatedTo}\`);
8410
- return parts.join(", ");
8411
- }
8412
- function selectMeetingId(meetings, selectedMeetingId) {
8413
- if (selectedMeetingId && meetings.some((meeting) => meeting.id === selectedMeetingId)) return selectedMeetingId;
8414
- return meetings[0]?.id ?? null;
8415
- }
8416
- function nextWorkspaceTab(currentTab, key) {
8417
- const current = parseWorkspaceTab(currentTab);
8418
- switch (key) {
8419
- case "1": return "notes";
8420
- case "2": return "transcript";
8421
- case "3": return "metadata";
8422
- case "4": return "raw";
8423
- case "]":
8424
- switch (current) {
8425
- case "notes": return "transcript";
8426
- case "transcript": return "metadata";
8427
- case "metadata": return "raw";
8428
- case "raw": return "notes";
8429
- }
8430
- break;
8431
- case "[":
8432
- switch (current) {
8433
- case "notes": return "raw";
8434
- case "transcript": return "notes";
8435
- case "metadata": return "transcript";
8436
- case "raw": return "metadata";
8437
- }
8438
- break;
8439
- default: return;
8440
- }
8441
- }
8442
- //#endregion
8443
- //#region src/web-app/components.tsx
8444
- /** @jsxImportSource solid-js */
8445
- var _tmpl$$1 = /* @__PURE__ */ template(\`<section class=hero><h1>Granola Toolkit</h1><p>Browser workspace for folders, meetings, notes, transcripts, and export flows on top of one local server instance.</p><input class=search placeholder="Search meetings, ids, or tags"><div class="field-row field-row--inline"><label><span class=field-label>Sort</span><select class=select><option value=updated-desc>Newest first</option><option value=updated-asc>Oldest first</option><option value=title-asc>Title A-Z</option><option value=title-desc>Title Z-A</option></select></label><label><span class=field-label>Updated From</span><input class=field-input type=date></label></div><label class=field-row><span class=field-label>Updated To</span><input class=field-input type=date>\`), _tmpl$2 = /* @__PURE__ */ template(\`<section class=toolbar><div><p>Meetings are loaded from the shared server state so this view can stay aligned with the terminal UI and sync loop.</p></div><div class=toolbar-form><input class=field-input placeholder="Quick open by id or title"><button class="button button--secondary"type=button>Open\`), _tmpl$3 = /* @__PURE__ */ template(\`<div class="folder-empty folder-empty--error">\`), _tmpl$4 = /* @__PURE__ */ template(\`<section class=folder-panel><div class=folder-panel__head><h2>Folders</h2><p>Pick a folder to scope the meeting browser, or stay on All meetings.</p></div><div class=folder-list>\`), _tmpl$5 = /* @__PURE__ */ template(\`<button class=folder-row type=button><span class=folder-row__title>All meetings</span><span class=folder-row__meta>Browse the full meeting list.\`), _tmpl$6 = /* @__PURE__ */ template(\`<div class=folder-empty>No folders found.\`), _tmpl$7 = /* @__PURE__ */ template(\`<button class=folder-row type=button><span class=folder-row__title></span><span class=folder-row__meta>\`), _tmpl$8 = /* @__PURE__ */ template(\`<div class="meeting-empty meeting-empty--error">\`), _tmpl$9 = /* @__PURE__ */ template(\`<section class=meeting-list>\`), _tmpl$0 = /* @__PURE__ */ template(\`<div class=meeting-empty>\`), _tmpl$1 = /* @__PURE__ */ template(\`<button class=meeting-row type=button><span class=meeting-row__title></span><span class=meeting-row__meta></span><span class=meeting-row__meta>\`), _tmpl$10 = /* @__PURE__ */ template(\`<section class=detail-head><div><h2>Meeting Workspace</h2></div><div class=state-badge>\`), _tmpl$11 = /* @__PURE__ */ template(\`<p>Waiting for server state…\`), _tmpl$12 = /* @__PURE__ */ template(\`<div class=status-grid><div><span class=status-label>Surface</span><strong></strong></div><div><span class=status-label>View</span><strong></strong></div><div><span class=status-label>Auth</span><strong></strong></div><div><span class=status-label>Sync</span><strong></strong></div><div><span class=status-label>Documents</span><strong></strong></div><div><span class=status-label>Folders</span><strong></strong></div><div><span class=status-label>Cache</span><strong></strong></div><div><span class=status-label>Index</span><strong></strong></div><div><span class=status-label>Automation</span><strong>\`), _tmpl$13 = /* @__PURE__ */ template(\`<section class=security-panel><div class=security-panel__head><h3>Server Access</h3><p>This server is locked with a password. Unlock it to load meetings and live state.</p></div><div class=security-panel__body><input class=field-input placeholder="Server password"type=password><div class=toolbar-actions><button class="button button--primary"type=button>Unlock</button><button class="button button--secondary"type=button>Lock\`), _tmpl$14 = /* @__PURE__ */ template(\`<section class=auth-panel><div class=auth-panel__head><h3>Auth Session</h3><p>Inspect, refresh, and switch between API key, stored session, and <code>supabase.json</code>.</p></div><div class=auth-panel__body>\`), _tmpl$15 = /* @__PURE__ */ template(\`<div class=auth-card><div class=auth-card__meta>Auth state unavailable.\`), _tmpl$16 = /* @__PURE__ */ template(\`<div class=auth-card__meta>Client ID: \`), _tmpl$17 = /* @__PURE__ */ template(\`<div class=auth-card__meta>Sign-in method: \`), _tmpl$18 = /* @__PURE__ */ template(\`<div class=auth-card__meta>supabase path: \`), _tmpl$19 = /* @__PURE__ */ template(\`<div class="auth-card__meta auth-card__error">\`), _tmpl$20 = /* @__PURE__ */ template(\`<div class=auth-card><div class=status-grid><div><span class=status-label>Active</span><strong></strong></div><div><span class=status-label>API key</span><strong></strong></div><div><span class=status-label>Stored</span><strong></strong></div><div><span class=status-label>supabase.json</span><strong></strong></div><div><span class=status-label>Refresh</span><strong></strong></div></div><div class=auth-card__meta>Store a Granola Personal API key here or use <code>granola auth login --api-key &lt;token&gt;</code>.</div><div class=auth-card__actions><input class=input placeholder=grn_... type=password><button class="button button--secondary"type=button>Save API key</button><button class="button button--secondary"type=button>Import desktop session</button><button class="button button--secondary"type=button>Refresh stored session</button><button class="button button--secondary"type=button>Use API key</button><button class="button button--secondary"type=button>Use stored session</button><button class="button button--secondary"type=button>Use supabase.json</button><button class="button button--secondary"type=button>Sign out\`), _tmpl$21 = /* @__PURE__ */ template(\`<section class=jobs-panel><div class=jobs-panel__head><h3>Recent Export Jobs</h3><p>Tracked across CLI and web runs.</p></div><div class=jobs-list>\`), _tmpl$22 = /* @__PURE__ */ template(\`<div class=job-empty>No export jobs yet.\`), _tmpl$23 = /* @__PURE__ */ template(\`<div class=job-card__meta>\`), _tmpl$24 = /* @__PURE__ */ template(\`<button class="button button--secondary"type=button>Rerun\`), _tmpl$25 = /* @__PURE__ */ template(\`<article class=job-card><div class=job-card__head><div><div class=job-card__title> export</div><div class=job-card__meta></div></div><div class=job-card__status></div></div><div class=job-card__meta></div><div class=job-card__meta>Started: </div><div class=job-card__meta>Output: </div><div class=job-card__actions>\`), _tmpl$26 = /* @__PURE__ */ template(\`<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$27 = /* @__PURE__ */ template(\`<div class=job-empty>No automation runs yet.\`), _tmpl$28 = /* @__PURE__ */ template(\`<button class="button button--secondary"type=button>Approve\`), _tmpl$29 = /* @__PURE__ */ template(\`<button class="button button--secondary"type=button>Reject\`), _tmpl$30 = /* @__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$31 = /* @__PURE__ */ template(\`<nav class=workspace-tabs><span class=workspace-hint>1-4 switch tabs, [ and ] cycle\`), _tmpl$32 = /* @__PURE__ */ template(\`<button class=workspace-tab type=button>\`), _tmpl$33 = /* @__PURE__ */ template(\`<div class=empty>\`), _tmpl$34 = /* @__PURE__ */ template(\`<div class=detail-meta><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip>\`), _tmpl$35 = /* @__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>\`);
8446
- function authModeLabel(mode) {
8447
- switch (mode) {
8448
- case "api-key": return "API key";
8449
- case "stored-session": return "Stored session";
8450
- default: return "supabase.json";
8451
- }
8452
- }
8453
- function metadataLines(record) {
8454
- return [
8455
- \`Title: \${record.meeting.title || record.meeting.id}\`,
8456
- \`Created: \${record.meeting.createdAt}\`,
8457
- \`Updated: \${record.meeting.updatedAt}\`,
8458
- \`Folders: \${record.meeting.folders.length ? record.meeting.folders.map((folder) => folder.name).join(", ") : "none"}\`,
8459
- \`Tags: \${record.meeting.tags.length ? record.meeting.tags.join(", ") : "none"}\`,
8460
- \`Transcript loaded: \${record.meeting.transcriptLoaded ? "yes" : "no"}\`
8461
- ].join("\n");
8462
- }
8463
- function workspaceBody(bundle, record, tab) {
8464
- switch (tab) {
8465
- case "transcript": return {
8466
- body: record.transcriptText || "(Transcript unavailable)",
8467
- title: "Transcript"
8468
- };
8469
- case "metadata": return {
8470
- body: metadataLines(record),
8471
- title: "Metadata"
8472
- };
8473
- case "raw": return {
8474
- body: JSON.stringify(bundle || record, null, 2),
8475
- title: "Raw Bundle"
8476
- };
8477
- default: return {
8478
- body: record.noteMarkdown || "(No notes available)",
8479
- title: "Notes"
8480
- };
8481
- }
8482
- }
8483
- function scopeLabel(scope) {
8484
- return exportScopeLabel(scope);
8485
- }
8486
- function ToolbarFilters(props) {
8487
- return [(() => {
8488
- var _el$ = _tmpl$$1(), _el$4 = _el$.firstChild.nextSibling.nextSibling, _el$5 = _el$4.nextSibling, _el$6 = _el$5.firstChild, _el$8 = _el$6.firstChild.nextSibling, _el$1 = _el$6.nextSibling.firstChild.nextSibling, _el$12 = _el$5.nextSibling.firstChild.nextSibling;
8489
- _el$4.$$input = (event) => {
8490
- props.onSearchInput(event.currentTarget.value);
8491
- };
8492
- _el$8.addEventListener("change", (event) => {
8493
- props.onSortChange(event.currentTarget.value);
8494
- });
8495
- _el$1.addEventListener("change", (event) => {
8496
- props.onUpdatedFromChange(event.currentTarget.value);
8497
- });
8498
- _el$12.addEventListener("change", (event) => {
8499
- props.onUpdatedToChange(event.currentTarget.value);
8500
- });
8501
- createRenderEffect(() => _el$4.value = props.search);
8502
- createRenderEffect(() => _el$8.value = props.sort);
8503
- createRenderEffect(() => _el$1.value = props.updatedFrom);
8504
- createRenderEffect(() => _el$12.value = props.updatedTo);
8505
- return _el$;
8506
- })(), (() => {
8507
- var _el$13 = _tmpl$2(), _el$16 = _el$13.firstChild.nextSibling.firstChild, _el$17 = _el$16.nextSibling;
8508
- _el$16.$$keydown = (event) => {
8509
- if (event.key === "Enter") {
8510
- event.preventDefault();
8511
- props.onQuickOpen();
8512
- }
8513
- };
8514
- _el$16.$$input = (event) => {
8515
- props.onQuickOpenInput(event.currentTarget.value);
8516
- };
8517
- addEventListener(_el$17, "click", props.onQuickOpen, true);
8518
- createRenderEffect(() => _el$16.value = props.quickOpen);
8519
- return _el$13;
8520
- })()];
8521
- }
8522
- function FolderList(props) {
8523
- return (() => {
8524
- var _el$18 = _tmpl$4(), _el$20 = _el$18.firstChild.nextSibling;
8525
- insert(_el$20, createComponent(Show, {
8526
- get fallback() {
8527
- return [
8528
- (() => {
8529
- var _el$22 = _tmpl$5();
8530
- _el$22.$$click = () => {
8531
- props.onSelect(null);
8532
- };
8533
- createRenderEffect(() => setAttribute(_el$22, "data-selected", !props.selectedFolderId ? "true" : void 0));
8534
- return _el$22;
8535
- })(),
8536
- createComponent(For, {
8537
- get each() {
8538
- return props.folders;
8539
- },
8540
- children: (folder) => (() => {
8541
- var _el$24 = _tmpl$7(), _el$25 = _el$24.firstChild, _el$26 = _el$25.nextSibling;
8542
- _el$24.$$click = () => {
8543
- props.onSelect(folder.id);
8544
- };
8545
- insert(_el$25, () => (folder.isFavourite ? "★ " : "") + (folder.name || folder.id));
8546
- insert(_el$26, () => \`\${folder.documentCount} meetings\`);
8547
- createRenderEffect(() => setAttribute(_el$24, "data-selected", folder.id === props.selectedFolderId ? "true" : void 0));
8548
- return _el$24;
8549
- })()
8550
- }),
8551
- createComponent(Show, {
8552
- get when() {
8553
- return props.folders.length === 0;
8554
- },
8555
- get children() {
8556
- return _tmpl$6();
8557
- }
8558
- })
8559
- ];
8560
- },
8561
- get when() {
8562
- return !props.error;
8563
- },
8564
- get children() {
8565
- var _el$21 = _tmpl$3();
8566
- insert(_el$21, () => props.error);
8567
- return _el$21;
8568
- }
8569
- }));
8570
- return _el$18;
8571
- })();
8572
- }
8573
- function MeetingList(props) {
8574
- const summary = () => currentFilterSummary({
8575
- folders: props.folders,
8576
- search: props.search,
8577
- selectedFolderId: props.selectedFolderId,
8578
- updatedFrom: props.updatedFrom,
8579
- updatedTo: props.updatedTo
8580
- });
8581
- return (() => {
8582
- var _el$27 = _tmpl$9();
8583
- insert(_el$27, createComponent(Show, {
8584
- get fallback() {
8585
- return createComponent(Show, {
8586
- get fallback() {
8587
- return (() => {
8588
- var _el$29 = _tmpl$0();
8589
- insert(_el$29, (() => {
8590
- var _c$ = memo(() => !!summary());
8591
- return () => _c$() ? \`No meetings match \${summary()}.\` : "No meetings yet. Try Sync now.";
8592
- })());
8593
- return _el$29;
8594
- })();
8595
- },
8596
- get when() {
8597
- return props.meetings.length > 0;
8598
- },
8599
- get children() {
8600
- return createComponent(For, {
8601
- get each() {
8602
- return props.meetings;
8603
- },
8604
- children: (meeting) => (() => {
8605
- var _el$30 = _tmpl$1(), _el$31 = _el$30.firstChild, _el$32 = _el$31.nextSibling, _el$33 = _el$32.nextSibling;
8606
- _el$30.$$click = () => {
8607
- props.onSelect(meeting.id);
8608
- };
8609
- insert(_el$31, () => meeting.title || meeting.id);
8610
- insert(_el$32, (() => {
8611
- var _c$2 = memo(() => !!meeting.tags.length);
8612
- return () => _c$2() ? meeting.tags.map((tag) => \`#\${tag}\`).join(" ") : "untagged";
8613
- })());
8614
- insert(_el$33, (() => {
8615
- var _c$3 = memo(() => !!meeting.updatedAt);
8616
- return () => _c$3() ? meeting.updatedAt.slice(0, 10) : "unknown";
8617
- })());
8618
- createRenderEffect(() => setAttribute(_el$30, "data-selected", meeting.id === props.selectedMeetingId ? "true" : void 0));
8619
- return _el$30;
8620
- })()
8621
- });
8622
- }
8623
- });
8624
- },
8625
- get when() {
8626
- return props.error;
8627
- },
8628
- get children() {
8629
- var _el$28 = _tmpl$8();
8630
- insert(_el$28, () => props.error);
8631
- return _el$28;
8632
- }
8633
- }));
8634
- return _el$27;
8635
- })();
8636
- }
8637
- function AppStatePanel(props) {
8638
- const syncStatus = () => {
8639
- const sync = props.appState?.sync;
8640
- if (!sync) return "idle";
8641
- if (sync.running) return "running";
8642
- if (sync.lastError) return "error";
8643
- if (sync.lastCompletedAt) return \`last \${sync.lastCompletedAt.slice(11, 19)}\`;
8644
- return "idle";
8645
- };
8646
- const authStatus = () => {
8647
- if (!props.appState) return "Waiting for auth state…";
8648
- return authModeLabel(props.appState.auth.mode);
8649
- };
8650
- return (() => {
8651
- var _el$34 = _tmpl$10(), _el$35 = _el$34.firstChild;
8652
- _el$35.firstChild;
8653
- var _el$37 = _el$35.nextSibling;
8654
- insert(_el$35, createComponent(Show, {
8655
- get fallback() {
8656
- return _tmpl$11();
8657
- },
8658
- get when() {
8659
- return props.appState;
8660
- },
8661
- children: (appState) => (() => {
8662
- var _el$39 = _tmpl$12(), _el$40 = _el$39.firstChild, _el$42 = _el$40.firstChild.nextSibling, _el$43 = _el$40.nextSibling, _el$45 = _el$43.firstChild.nextSibling, _el$46 = _el$43.nextSibling, _el$48 = _el$46.firstChild.nextSibling, _el$49 = _el$46.nextSibling, _el$51 = _el$49.firstChild.nextSibling, _el$52 = _el$49.nextSibling, _el$54 = _el$52.firstChild.nextSibling, _el$55 = _el$52.nextSibling, _el$57 = _el$55.firstChild.nextSibling, _el$58 = _el$55.nextSibling, _el$60 = _el$58.firstChild.nextSibling, _el$61 = _el$58.nextSibling, _el$63 = _el$61.firstChild.nextSibling, _el$66 = _el$61.nextSibling.firstChild.nextSibling;
8663
- insert(_el$42, () => appState().ui.surface);
8664
- insert(_el$45, () => appState().ui.view);
8665
- insert(_el$48, authStatus);
8666
- insert(_el$51, syncStatus);
8667
- insert(_el$54, (() => {
8668
- var _c$4 = memo(() => !!appState().documents.loaded);
8669
- return () => _c$4() ? String(appState().documents.count) : "not loaded";
8670
- })());
8671
- insert(_el$57, (() => {
8672
- var _c$5 = memo(() => !!appState().folders.loaded);
8673
- return () => _c$5() ? String(appState().folders.count) : "not loaded";
8674
- })());
8675
- insert(_el$60, (() => {
8676
- var _c$6 = memo(() => !!appState().cache.loaded);
8677
- return () => _c$6() ? \`\${appState().cache.transcriptCount} transcript sets\` : appState().cache.configured ? "configured" : "not configured";
8678
- })());
8679
- insert(_el$63, (() => {
8680
- var _c$7 = memo(() => !!appState().index.loaded);
8681
- return () => _c$7() ? \`\${appState().index.meetingCount} meetings\` : appState().index.available ? "available" : "not built";
8682
- })());
8683
- insert(_el$66, () => \`\${appState().automation.runCount} runs / \${appState().automation.pendingRunCount} pending\`);
8684
- return _el$39;
8685
- })()
8686
- }), null);
8687
- insert(_el$37, () => props.statusLabel);
8688
- createRenderEffect(() => setAttribute(_el$37, "data-tone", props.statusTone));
8689
- return _el$34;
8690
- })();
8691
- }
8692
- function SecurityPanel(props) {
8693
- return createComponent(Show, {
8694
- get when() {
8695
- return props.visible;
8696
- },
8697
- get children() {
8698
- var _el$67 = _tmpl$13(), _el$70 = _el$67.firstChild.nextSibling.firstChild, _el$72 = _el$70.nextSibling.firstChild, _el$73 = _el$72.nextSibling;
8699
- _el$70.$$keydown = (event) => {
8700
- if (event.key === "Enter") {
8701
- event.preventDefault();
8702
- props.onUnlock();
8703
- }
8704
- };
8705
- _el$70.$$input = (event) => {
8706
- props.onPasswordChange(event.currentTarget.value);
8707
- };
8708
- addEventListener(_el$72, "click", props.onUnlock, true);
8709
- addEventListener(_el$73, "click", props.onLock, true);
8710
- createRenderEffect(() => _el$70.value = props.password);
8711
- return _el$67;
8712
- }
8713
- });
8714
- }
8715
- function AuthPanel(props) {
8716
- return (() => {
8717
- var _el$74 = _tmpl$14(), _el$76 = _el$74.firstChild.nextSibling;
8718
- insert(_el$76, createComponent(Show, {
8719
- get fallback() {
8720
- return _tmpl$15();
8721
- },
8722
- get when() {
8723
- return props.auth;
8724
- },
8725
- children: (auth) => (() => {
8726
- var _el$78 = _tmpl$20(), _el$79 = _el$78.firstChild, _el$80 = _el$79.firstChild, _el$82 = _el$80.firstChild.nextSibling, _el$83 = _el$80.nextSibling, _el$85 = _el$83.firstChild.nextSibling, _el$86 = _el$83.nextSibling, _el$88 = _el$86.firstChild.nextSibling, _el$89 = _el$86.nextSibling, _el$91 = _el$89.firstChild.nextSibling, _el$94 = _el$89.nextSibling.firstChild.nextSibling, _el$102 = _el$79.nextSibling, _el$104 = _el$102.nextSibling.firstChild, _el$105 = _el$104.nextSibling, _el$106 = _el$105.nextSibling, _el$107 = _el$106.nextSibling, _el$108 = _el$107.nextSibling, _el$109 = _el$108.nextSibling, _el$110 = _el$109.nextSibling, _el$111 = _el$110.nextSibling;
8727
- insert(_el$82, () => authModeLabel(auth().mode));
8728
- insert(_el$85, () => auth().apiKeyAvailable ? "available" : "missing");
8729
- insert(_el$88, () => auth().storedSessionAvailable ? "available" : "missing");
8730
- insert(_el$91, () => auth().supabaseAvailable ? "available" : "missing");
8731
- insert(_el$94, () => auth().refreshAvailable ? "available" : "missing");
8732
- insert(_el$78, createComponent(Show, {
8733
- get when() {
8734
- return auth().clientId;
8735
- },
8736
- get children() {
8737
- var _el$95 = _tmpl$16();
8738
- _el$95.firstChild;
8739
- insert(_el$95, () => auth().clientId, null);
8740
- return _el$95;
8741
- }
8742
- }), _el$102);
8743
- insert(_el$78, createComponent(Show, {
8744
- get when() {
8745
- return auth().signInMethod;
8746
- },
8747
- get children() {
8748
- var _el$97 = _tmpl$17();
8749
- _el$97.firstChild;
8750
- insert(_el$97, () => auth().signInMethod, null);
8751
- return _el$97;
8752
- }
8753
- }), _el$102);
8754
- insert(_el$78, createComponent(Show, {
8755
- get when() {
8756
- return auth().supabasePath;
8757
- },
8758
- get children() {
8759
- var _el$99 = _tmpl$18();
8760
- _el$99.firstChild;
8761
- insert(_el$99, () => auth().supabasePath, null);
8762
- return _el$99;
8763
- }
8764
- }), _el$102);
8765
- insert(_el$78, createComponent(Show, {
8766
- get when() {
8767
- return auth().lastError;
8768
- },
8769
- get children() {
8770
- var _el$101 = _tmpl$19();
8771
- insert(_el$101, () => auth().lastError);
8772
- return _el$101;
8773
- }
8774
- }), _el$102);
8775
- _el$104.$$input = (event) => {
8776
- props.onApiKeyDraftChange(event.currentTarget.value);
8777
- };
8778
- addEventListener(_el$105, "click", props.onSaveApiKey, true);
8779
- addEventListener(_el$106, "click", props.onImportDesktopSession, true);
8780
- addEventListener(_el$107, "click", props.onRefresh, true);
8781
- _el$108.$$click = () => {
8782
- props.onSwitchMode("api-key");
8783
- };
8784
- _el$109.$$click = () => {
8785
- props.onSwitchMode("stored-session");
8786
- };
8787
- _el$110.$$click = () => {
8788
- props.onSwitchMode("supabase-file");
8789
- };
8790
- addEventListener(_el$111, "click", props.onLogout, true);
8791
- createRenderEffect((_p$) => {
8792
- var _v$ = !auth().supabaseAvailable, _v$2 = !auth().storedSessionAvailable || !auth().refreshAvailable, _v$3 = !auth().apiKeyAvailable || auth().mode === "api-key", _v$4 = !auth().storedSessionAvailable || auth().mode === "stored-session", _v$5 = !auth().supabaseAvailable || auth().mode === "supabase-file", _v$6 = !auth().apiKeyAvailable && !auth().storedSessionAvailable;
8793
- _v$ !== _p$.e && (_el$106.disabled = _p$.e = _v$);
8794
- _v$2 !== _p$.t && (_el$107.disabled = _p$.t = _v$2);
8795
- _v$3 !== _p$.a && (_el$108.disabled = _p$.a = _v$3);
8796
- _v$4 !== _p$.o && (_el$109.disabled = _p$.o = _v$4);
8797
- _v$5 !== _p$.i && (_el$110.disabled = _p$.i = _v$5);
8798
- _v$6 !== _p$.n && (_el$111.disabled = _p$.n = _v$6);
8799
- return _p$;
8800
- }, {
8801
- e: void 0,
8802
- t: void 0,
8803
- a: void 0,
8804
- o: void 0,
8805
- i: void 0,
8806
- n: void 0
8807
- });
8808
- createRenderEffect(() => _el$104.value = props.apiKeyDraft);
8809
- return _el$78;
8810
- })()
8811
- }));
8812
- return _el$74;
8813
- })();
8814
- }
8815
- function ExportJobsPanel(props) {
8816
- return (() => {
8817
- var _el$112 = _tmpl$21(), _el$114 = _el$112.firstChild.nextSibling;
8818
- insert(_el$114, createComponent(Show, {
8819
- get when() {
8820
- return props.jobs.length > 0;
8821
- },
8822
- get fallback() {
8823
- return _tmpl$22();
8824
- },
8825
- get children() {
8826
- return createComponent(For, {
8827
- get each() {
8828
- return props.jobs.slice(0, 6);
8829
- },
8830
- children: (job) => (() => {
8831
- var _el$116 = _tmpl$25(), _el$117 = _el$116.firstChild, _el$118 = _el$117.firstChild, _el$119 = _el$118.firstChild, _el$120 = _el$119.firstChild, _el$121 = _el$119.nextSibling, _el$122 = _el$118.nextSibling, _el$123 = _el$117.nextSibling, _el$124 = _el$123.nextSibling;
8832
- _el$124.firstChild;
8833
- var _el$126 = _el$124.nextSibling;
8834
- _el$126.firstChild;
8835
- var _el$129 = _el$126.nextSibling;
8836
- insert(_el$119, () => job.kind, _el$120);
8837
- insert(_el$121, () => job.id);
8838
- insert(_el$122, () => job.status);
8839
- insert(_el$123, () => \`Format: \${job.format} • \${scopeLabel(job.scope)} • \${job.itemCount > 0 ? \`\${job.completedCount}/\${job.itemCount} items\` : "0 items"} • Written: \${job.written}\`);
8840
- insert(_el$124, () => job.startedAt.slice(0, 19), null);
8841
- insert(_el$126, () => job.outputDir, null);
8842
- insert(_el$116, createComponent(Show, {
8843
- get when() {
8844
- return job.error;
8845
- },
8846
- get children() {
8847
- var _el$128 = _tmpl$23();
8848
- insert(_el$128, () => job.error);
8849
- return _el$128;
8850
- }
8851
- }), _el$129);
8852
- insert(_el$129, createComponent(Show, {
8853
- get when() {
8854
- return job.status !== "running";
8855
- },
8856
- get children() {
8857
- var _el$130 = _tmpl$24();
8858
- _el$130.$$click = () => {
8859
- props.onRerun(job.id);
8860
- };
8861
- return _el$130;
8862
- }
8863
- }));
8864
- createRenderEffect(() => setAttribute(_el$122, "data-status", job.status));
8865
- return _el$116;
8866
- })()
8867
- });
8868
- }
8869
- }));
8870
- return _el$112;
8871
- })();
8872
- }
8873
- function AutomationRunsPanel(props) {
8874
- return (() => {
8875
- var _el$131 = _tmpl$26(), _el$133 = _el$131.firstChild.nextSibling;
8876
- insert(_el$133, createComponent(Show, {
8877
- get when() {
8878
- return props.runs.length > 0;
8879
- },
8880
- get fallback() {
8881
- return _tmpl$27();
8882
- },
8883
- get children() {
8884
- return createComponent(For, {
8885
- get each() {
8886
- return props.runs.slice(0, 6);
8887
- },
8888
- children: (run) => (() => {
8889
- var _el$135 = _tmpl$30(), _el$136 = _el$135.firstChild, _el$137 = _el$136.firstChild, _el$138 = _el$137.firstChild, _el$139 = _el$138.nextSibling, _el$140 = _el$137.nextSibling, _el$141 = _el$136.nextSibling, _el$142 = _el$141.nextSibling, _el$146 = _el$142.nextSibling;
8890
- insert(_el$138, () => run.actionName);
8891
- insert(_el$139, () => \`\${run.ruleName} • \${run.id}\`);
8892
- insert(_el$140, () => run.status);
8893
- insert(_el$141, () => \`\${run.title} • \${run.eventKind}\`);
8894
- insert(_el$142, () => \`Started: \${run.startedAt.slice(0, 19)}\`);
8895
- insert(_el$135, createComponent(Show, {
8896
- get when() {
8897
- return run.prompt;
8898
- },
8899
- get children() {
8900
- var _el$143 = _tmpl$23();
8901
- insert(_el$143, () => run.prompt);
8902
- return _el$143;
8903
- }
8904
- }), _el$146);
8905
- insert(_el$135, createComponent(Show, {
8906
- get when() {
8907
- return run.result;
8908
- },
8909
- get children() {
8910
- var _el$144 = _tmpl$23();
8911
- insert(_el$144, () => run.result);
8912
- return _el$144;
8913
- }
8914
- }), _el$146);
8915
- insert(_el$135, createComponent(Show, {
8916
- get when() {
8917
- return run.error;
8918
- },
8919
- get children() {
8920
- var _el$145 = _tmpl$23();
8921
- insert(_el$145, () => run.error);
8922
- return _el$145;
8923
- }
8924
- }), _el$146);
8925
- insert(_el$146, createComponent(Show, {
8926
- get when() {
8927
- return run.status === "pending";
8928
- },
8929
- get children() {
8930
- return [(() => {
8931
- var _el$147 = _tmpl$28();
8932
- _el$147.$$click = () => {
8933
- props.onApprove(run.id);
8934
- };
8935
- return _el$147;
8936
- })(), (() => {
8937
- var _el$148 = _tmpl$29();
8938
- _el$148.$$click = () => {
8939
- props.onReject(run.id);
8940
- };
8941
- return _el$148;
8942
- })()];
8943
- }
8944
- }));
8945
- createRenderEffect(() => setAttribute(_el$140, "data-status", run.status));
8946
- return _el$135;
8947
- })()
8948
- });
8949
- }
8950
- }));
8951
- return _el$131;
8952
- })();
8953
- }
8954
- function Workspace(props) {
8955
- const parsedTab = () => parseWorkspaceTab(props.tab);
8956
- const details = () => {
8957
- if (!props.selectedMeeting) return null;
8958
- return workspaceBody(props.bundle, props.selectedMeeting, parsedTab());
8959
- };
8960
- return [(() => {
8961
- var _el$149 = _tmpl$31(), _el$150 = _el$149.firstChild;
8962
- insert(_el$149, createComponent(For, {
8963
- each: [
8964
- "notes",
8965
- "transcript",
8966
- "metadata",
8967
- "raw"
8968
- ],
8969
- children: (tab) => (() => {
8970
- var _el$151 = _tmpl$32();
8971
- _el$151.$$click = () => {
8972
- props.onSelectTab(tab);
8973
- };
8974
- insert(_el$151, tab === "notes" ? "Notes" : tab === "transcript" ? "Transcript" : tab === "metadata" ? "Metadata" : "Raw");
8975
- createRenderEffect(() => setAttribute(_el$151, "data-selected", parsedTab() === tab ? "true" : void 0));
8976
- return _el$151;
8977
- })()
8978
- }), _el$150);
8979
- return _el$149;
8980
- })(), createComponent(Show, {
8981
- get when() {
8982
- return props.selectedMeeting;
8983
- },
8984
- get fallback() {
8985
- return (() => {
8986
- var _el$152 = _tmpl$33();
8987
- insert(_el$152, () => props.detailError || "Select a meeting to inspect its notes and transcript.");
8988
- return _el$152;
8989
- })();
8990
- },
8991
- children: (meeting) => [(() => {
8992
- var _el$153 = _tmpl$34(), _el$154 = _el$153.firstChild, _el$155 = _el$154.nextSibling, _el$156 = _el$155.nextSibling;
8993
- insert(_el$154, () => \`ID: \${meeting().meeting.id}\`);
8994
- insert(_el$155, () => \`Source: \${meeting().meeting.noteContentSource}\`);
8995
- insert(_el$156, () => \`Transcript: \${meeting().meeting.transcriptSegmentCount} segments\`);
8996
- return _el$153;
8997
- })(), createComponent(Show, {
8998
- get when() {
8999
- return !props.detailError;
9000
- },
9001
- get fallback() {
9002
- return (() => {
9003
- var _el$165 = _tmpl$33();
9004
- insert(_el$165, () => props.detailError);
9005
- return _el$165;
9006
- })();
9007
- },
9008
- get children() {
9009
- var _el$157 = _tmpl$35(), _el$159 = _el$157.firstChild.firstChild, _el$161 = _el$159.firstChild.nextSibling, _el$163 = _el$159.nextSibling.firstChild, _el$164 = _el$163.nextSibling;
9010
- insert(_el$161, () => metadataLines(meeting()));
9011
- insert(_el$163, () => details()?.title);
9012
- insert(_el$164, () => details()?.body);
9013
- return _el$157;
9014
- }
9015
- })]
9016
- })];
9017
- }
9018
- delegateEvents([
9019
- "input",
9020
- "keydown",
9021
- "click"
9022
- ]);
9023
- //#endregion
9024
- //#region src/web-app/App.tsx
9025
- /** @jsxImportSource solid-js */
9026
- var _tmpl$ = /* @__PURE__ */ template(\`<div class=shell><aside class="pane sidebar"></aside><main class="pane detail"><section class=toolbar><div class=toolbar-actions><button class="button button--primary"type=button>Sync now</button><button class="button button--secondary"type=button>Export Notes</button><button class="button button--secondary"type=button>Export Transcripts</button></div><p>Solid-powered web workspace on top of the same local server, sync loop, and shared app contracts.\`);
9027
- function browserConfig() {
9028
- return { passwordRequired: Boolean(window.__GRANOLA_SERVER__?.passwordRequired) };
9029
- }
9030
- async function requestJson(path, init) {
9031
- const response = await fetch(path, init);
9032
- const payload = await response.json().catch(() => ({}));
9033
- if (!response.ok) {
9034
- const error = typeof payload.error === "string" && payload.error.trim() ? payload.error : response.statusText || "Request failed";
9035
- throw new Error(error);
9036
- }
9037
- return payload;
9038
- }
9039
- function App() {
9040
- const startup = startupSelectionFromSearch(window.location.search);
9041
- const [state, setState] = createStore({
9042
- apiKeyDraft: "",
9043
- appState: null,
9044
- automationRuns: [],
9045
- detailError: "",
9046
- folderError: "",
9047
- folders: [],
9048
- listError: "",
9049
- meetingSource: "live",
9050
- meetings: [],
9051
- quickOpen: "",
9052
- search: "",
9053
- selectedFolderId: startup.folderId || null,
9054
- selectedMeetingBundle: null,
9055
- selectedMeetingId: startup.meetingId || null,
9056
- selectedMeeting: null,
9057
- serverLocked: browserConfig().passwordRequired,
9058
- serverPassword: "",
9059
- sort: "updated-desc",
9060
- statusLabel: browserConfig().passwordRequired ? "Server locked" : "Connecting…",
9061
- statusTone: browserConfig().passwordRequired ? "error" : "idle",
9062
- updatedFrom: "",
9063
- updatedTo: "",
9064
- workspaceTab: parseWorkspaceTab(startup.workspaceTab)
9065
- });
9066
- let client = null;
9067
- let unsubscribe;
9068
- const setStatus = (label, tone = "idle") => {
9069
- setState({
9070
- statusLabel: label,
9071
- statusTone: tone
9072
- });
9073
- };
9074
- const mergeAuthState = async (authState) => {
9075
- if (!client) return;
9076
- setState("appState", {
9077
- ...client.getState(),
9078
- auth: authState ?? await client.inspectAuth()
9079
- });
9080
- };
9081
- const detachClient = async () => {
9082
- unsubscribe?.();
9083
- unsubscribe = void 0;
9084
- if (client) {
9085
- await client.close().catch(() => void 0);
9086
- client = null;
9087
- }
9088
- };
9089
- const attachClient = async () => {
9090
- await detachClient();
9091
- client = await createGranolaServerClient(window.location.origin);
9092
- unsubscribe = client.subscribe((event) => {
9093
- setState("appState", event.state);
9094
- });
9095
- await mergeAuthState();
9096
- };
9097
- const loadFolders = async (refresh = false) => {
9098
- if (!client) return;
9099
- try {
9100
- setState("folderError", "");
9101
- const result = await client.listFolders({
9102
- forceRefresh: refresh,
9103
- limit: 100
9104
- });
9105
- setState("folders", result.folders);
9106
- if (state.selectedFolderId && !result.folders.some((folder) => folder.id === state.selectedFolderId)) setState("selectedFolderId", null);
9107
- } catch (error) {
9108
- setState("folderError", error instanceof Error ? error.message : String(error));
9109
- setState("folders", []);
9110
- setState("selectedFolderId", null);
9111
- }
9112
- };
9113
- const loadAutomationRuns = async () => {
9114
- if (!client) return;
9115
- try {
9116
- setState("automationRuns", (await client.listAutomationRuns({ limit: 20 })).runs);
9117
- } catch (error) {
9118
- setState("detailError", error instanceof Error ? error.message : String(error));
9119
- }
9120
- };
9121
- const loadMeeting = async (meetingId) => {
9122
- if (!client) return;
9123
- setState("selectedMeetingId", meetingId);
9124
- try {
9125
- setState("detailError", "");
9126
- const bundle = await client.getMeeting(meetingId);
9127
- setState("selectedMeetingBundle", bundle);
9128
- setState("selectedMeeting", bundle.meeting);
9129
- } catch (error) {
9130
- setState("selectedMeetingBundle", null);
9131
- setState("selectedMeeting", null);
9132
- setState("detailError", error instanceof Error ? error.message : String(error));
9133
- }
9134
- };
9135
- const loadMeetings = async (options = {}) => {
9136
- if (!client) return;
9137
- try {
9138
- setState("listError", "");
9139
- const result = await client.listMeetings({
9140
- folderId: state.selectedFolderId || void 0,
9141
- forceRefresh: options.refresh,
9142
- limit: 100,
9143
- search: state.search || void 0,
9144
- sort: state.sort,
9145
- updatedFrom: state.updatedFrom || void 0,
9146
- updatedTo: state.updatedTo || void 0
9147
- });
9148
- const preferredMeetingId = options.preferredMeetingId ?? state.selectedMeetingId;
9149
- const nextMeetingId = selectMeetingId(result.meetings, preferredMeetingId);
9150
- setState("meetings", result.meetings);
9151
- setState("meetingSource", result.source);
9152
- setState("selectedMeetingId", nextMeetingId);
9153
- if (nextMeetingId) await loadMeeting(nextMeetingId);
9154
- else {
9155
- setState("selectedMeeting", null);
9156
- setState("selectedMeetingBundle", null);
9157
- setState("detailError", "");
9158
- }
9159
- } catch (error) {
9160
- const message = error instanceof Error ? error.message : String(error);
9161
- setState("listError", message);
9162
- setState("selectedMeeting", null);
9163
- setState("selectedMeetingBundle", null);
9164
- setState("detailError", message);
9165
- }
9166
- };
9167
- const refreshAll = async (forceRefresh = false) => {
9168
- if (!client) await attachClient();
9169
- setStatus(forceRefresh ? "Syncing…" : "Refreshing…", "busy");
9170
- if (forceRefresh) await client?.sync({
9171
- forceRefresh: true,
9172
- foreground: true
9173
- });
9174
- await Promise.all([
9175
- loadFolders(forceRefresh),
9176
- loadAutomationRuns(),
9177
- mergeAuthState()
9178
- ]);
9179
- await loadMeetings({ refresh: forceRefresh });
9180
- setState("serverLocked", false);
9181
- setStatus(forceRefresh ? "Sync complete" : state.meetingSource === "index" ? "Loaded from index" : "Connected", "ok");
9182
- };
9183
- const connectAndRefresh = async (forceRefresh = false) => {
9184
- try {
9185
- await refreshAll(forceRefresh);
9186
- } catch (error) {
9187
- setStatus("Connection failed", "error");
9188
- setState("detailError", error instanceof Error ? error.message : String(error));
9189
- }
9190
- };
9191
- const quickOpenMeeting = async () => {
9192
- if (!client) return;
9193
- const query = state.quickOpen.trim();
9194
- if (!query) {
9195
- setStatus("Enter a title or id", "error");
9196
- return;
9197
- }
9198
- setStatus("Opening meeting…", "busy");
9199
- try {
9200
- const bundle = await client.findMeeting(query);
9201
- setState("selectedFolderId", bundle.meeting.meeting.folders[0]?.id || null);
9202
- setState("search", "");
9203
- setState("updatedFrom", "");
9204
- setState("updatedTo", "");
9205
- await loadMeetings({ preferredMeetingId: bundle.document.id });
9206
- setStatus("Connected", "ok");
9207
- } catch (error) {
9208
- setState("detailError", error instanceof Error ? error.message : String(error));
9209
- setStatus("Quick open failed", "error");
9210
- }
9211
- };
9212
- const saveApiKey = async () => {
9213
- if (!client) return;
9214
- if (!state.apiKeyDraft.trim()) {
9215
- setStatus("Enter a Granola API key", "error");
9216
- return;
9217
- }
9218
- setStatus("Saving API key…", "busy");
9219
- try {
9220
- const auth = await client.loginAuth({ apiKey: state.apiKeyDraft.trim() });
9221
- setState("apiKeyDraft", "");
9222
- await mergeAuthState(auth);
9223
- await refreshAll();
9224
- } catch (error) {
9225
- await mergeAuthState();
9226
- setState("detailError", error instanceof Error ? error.message : String(error));
9227
- setStatus("API key save failed", "error");
9228
- }
9229
- };
9230
- const importDesktopSession = async () => {
9231
- if (!client) return;
9232
- setStatus("Importing desktop session…", "busy");
9233
- try {
9234
- await mergeAuthState(await client.loginAuth());
9235
- await refreshAll();
9236
- } catch (error) {
9237
- await mergeAuthState();
9238
- setState("detailError", error instanceof Error ? error.message : String(error));
9239
- setStatus("Auth import failed", "error");
9240
- }
9241
- };
9242
- const refreshAuth = async () => {
9243
- if (!client) return;
9244
- setStatus("Refreshing session…", "busy");
9245
- try {
9246
- await mergeAuthState(await client.refreshAuth());
9247
- await refreshAll();
9248
- } catch (error) {
9249
- await mergeAuthState();
9250
- setState("detailError", error instanceof Error ? error.message : String(error));
9251
- setStatus("Refresh failed", "error");
9252
- }
9253
- };
9254
- const switchAuthMode = async (mode) => {
9255
- if (!client) return;
9256
- setStatus("Switching auth source…", "busy");
9257
- try {
9258
- await mergeAuthState(await client.switchAuthMode(mode));
9259
- await refreshAll();
9260
- } catch (error) {
9261
- await mergeAuthState();
9262
- setState("detailError", error instanceof Error ? error.message : String(error));
9263
- setStatus("Switch failed", "error");
9264
- }
9265
- };
9266
- const logout = async () => {
9267
- if (!client) return;
9268
- setStatus("Signing out…", "busy");
9269
- try {
9270
- await mergeAuthState(await client.logoutAuth());
9271
- await refreshAll();
9272
- } catch (error) {
9273
- await mergeAuthState();
9274
- setState("detailError", error instanceof Error ? error.message : String(error));
9275
- setStatus("Sign out failed", "error");
9276
- }
9277
- };
9278
- const exportNotes = async () => {
9279
- if (!client) return;
9280
- setStatus(state.selectedFolderId ? "Exporting folder notes…" : "Exporting notes…", "busy");
9281
- try {
9282
- await client.exportNotes("markdown", { folderId: state.selectedFolderId || void 0 });
9283
- await refreshAll();
9284
- } catch (error) {
9285
- setState("detailError", error instanceof Error ? error.message : String(error));
9286
- setStatus("Export failed", "error");
9287
- }
9288
- };
9289
- const exportTranscripts = async () => {
9290
- if (!client) return;
9291
- setStatus(state.selectedFolderId ? "Exporting folder transcripts…" : "Exporting transcripts…", "busy");
9292
- try {
9293
- await client.exportTranscripts("text", { folderId: state.selectedFolderId || void 0 });
9294
- await refreshAll();
9295
- } catch (error) {
9296
- setState("detailError", error instanceof Error ? error.message : String(error));
9297
- setStatus("Export failed", "error");
9298
- }
9299
- };
9300
- const rerunJob = async (jobId) => {
9301
- if (!client) return;
9302
- setStatus("Rerunning export…", "busy");
9303
- try {
9304
- await client.rerunExportJob(jobId);
9305
- await refreshAll();
9306
- } catch (error) {
9307
- setState("detailError", error instanceof Error ? error.message : String(error));
9308
- setStatus("Rerun failed", "error");
9309
- }
9310
- };
9311
- const resolveAutomationRun = async (id, decision) => {
9312
- if (!client) return;
9313
- setStatus(decision === "approve" ? "Approving automation…" : "Rejecting automation…", "busy");
9314
- try {
9315
- await client.resolveAutomationRun(id, decision);
9316
- await refreshAll();
9317
- } catch (error) {
9318
- setState("detailError", error instanceof Error ? error.message : String(error));
9319
- setStatus("Automation decision failed", "error");
9320
- }
9321
- };
9322
- const unlockServer = async () => {
9323
- if (!state.serverPassword.trim()) {
9324
- setStatus("Enter the server password", "error");
9325
- return;
9326
- }
9327
- setStatus("Unlocking server…", "busy");
9328
- try {
9329
- await requestJson("/auth/unlock", {
9330
- body: JSON.stringify({ password: state.serverPassword }),
9331
- headers: { "content-type": "application/json" },
9332
- method: "POST"
9333
- });
9334
- setState("serverPassword", "");
9335
- setState("serverLocked", false);
9336
- await connectAndRefresh(true);
9337
- } catch (error) {
9338
- setState("detailError", error instanceof Error ? error.message : String(error));
9339
- setStatus("Unlock failed", "error");
9340
- }
9341
- };
9342
- const lockServer = async () => {
9343
- try {
9344
- await requestJson("/auth/lock", { method: "POST" });
9345
- } catch {}
9346
- await detachClient();
9347
- setState({
9348
- appState: null,
9349
- automationRuns: [],
9350
- detailError: "",
9351
- folderError: "",
9352
- folders: [],
9353
- listError: "",
9354
- meetings: [],
9355
- selectedFolderId: null,
9356
- selectedMeeting: null,
9357
- selectedMeetingBundle: null,
9358
- selectedMeetingId: null,
9359
- serverLocked: true,
9360
- serverPassword: ""
9361
- });
9362
- setStatus("Server locked", "error");
9363
- };
9364
- createEffect(() => {
9365
- const nextPath = buildBrowserUrlPath(window.location.href, {
9366
- selectedFolderId: state.selectedFolderId,
9367
- selectedMeetingId: state.selectedMeetingId,
9368
- workspaceTab: state.workspaceTab
9369
- });
9370
- if (nextPath !== \`\${window.location.pathname}\${window.location.search}\${window.location.hash}\`) history.replaceState(null, "", nextPath);
9371
- });
9372
- createEffect(() => {
9373
- if (!state.appState?.automation.loaded || !client) return;
9374
- loadAutomationRuns();
9375
- });
9376
- onMount(() => {
9377
- const onKeyDown = (event) => {
9378
- const target = event.target;
9379
- if (target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement) return;
9380
- const nextTab = nextWorkspaceTab(state.workspaceTab, event.key);
9381
- if (nextTab) setState("workspaceTab", nextTab);
9382
- };
9383
- document.addEventListener("keydown", onKeyDown);
9384
- onCleanup(() => {
9385
- document.removeEventListener("keydown", onKeyDown);
9386
- });
9387
- if (!state.serverLocked) connectAndRefresh();
9388
- });
9389
- onCleanup(() => {
9390
- detachClient();
9391
- });
9392
- return (() => {
9393
- var _el$ = _tmpl$(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling, _el$4 = _el$3.firstChild, _el$6 = _el$4.firstChild.firstChild, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling;
9394
- insert(_el$2, createComponent(ToolbarFilters, {
9395
- onQuickOpen: () => {
9396
- quickOpenMeeting();
9397
- },
9398
- onQuickOpenInput: (value) => {
9399
- setState("quickOpen", value);
9400
- },
9401
- onSearchInput: (value) => {
9402
- setState("search", value.trim());
9403
- loadMeetings();
9404
- },
9405
- onSortChange: (value) => {
9406
- setState("sort", value);
9407
- loadMeetings();
9408
- },
9409
- onUpdatedFromChange: (value) => {
9410
- setState("updatedFrom", value);
9411
- loadMeetings();
9412
- },
9413
- onUpdatedToChange: (value) => {
9414
- setState("updatedTo", value);
9415
- loadMeetings();
9416
- },
9417
- get quickOpen() {
9418
- return state.quickOpen;
9419
- },
9420
- get search() {
9421
- return state.search;
9422
- },
9423
- get sort() {
9424
- return state.sort;
9425
- },
9426
- get updatedFrom() {
9427
- return state.updatedFrom;
9428
- },
9429
- get updatedTo() {
9430
- return state.updatedTo;
9431
- }
9432
- }), null);
9433
- insert(_el$2, createComponent(FolderList, {
9434
- get error() {
9435
- return state.folderError;
9436
- },
9437
- get folders() {
9438
- return state.folders;
9439
- },
9440
- onSelect: (folderId) => {
9441
- setState("selectedFolderId", folderId);
9442
- setState("selectedMeetingId", null);
9443
- setState("selectedMeeting", null);
9444
- setState("selectedMeetingBundle", null);
9445
- loadMeetings();
9446
- },
9447
- get selectedFolderId() {
9448
- return state.selectedFolderId;
9449
- }
9450
- }), null);
9451
- insert(_el$2, createComponent(MeetingList, {
9452
- get error() {
9453
- return state.listError;
9454
- },
9455
- get folders() {
9456
- return state.folders;
9457
- },
9458
- get meetings() {
9459
- return state.meetings;
9460
- },
9461
- onSelect: (meetingId) => {
9462
- loadMeeting(meetingId);
9463
- },
9464
- get search() {
9465
- return state.search;
9466
- },
9467
- get selectedFolderId() {
9468
- return state.selectedFolderId;
9469
- },
9470
- get selectedMeetingId() {
9471
- return state.selectedMeetingId;
9472
- },
9473
- get updatedFrom() {
9474
- return state.updatedFrom;
9475
- },
9476
- get updatedTo() {
9477
- return state.updatedTo;
9478
- }
9479
- }), null);
9480
- insert(_el$3, createComponent(AppStatePanel, {
9481
- get appState() {
9482
- return state.appState;
9483
- },
9484
- get statusLabel() {
9485
- return state.statusLabel;
9486
- },
9487
- get statusTone() {
9488
- return state.statusTone;
9489
- }
9490
- }), _el$4);
9491
- _el$6.$$click = () => {
9492
- connectAndRefresh(true);
9493
- };
9494
- _el$7.$$click = () => {
9495
- exportNotes();
9496
- };
9497
- _el$8.$$click = () => {
9498
- exportTranscripts();
9499
- };
9500
- insert(_el$3, createComponent(SecurityPanel, {
9501
- onLock: () => {
9502
- lockServer();
9503
- },
9504
- onPasswordChange: (value) => {
9505
- setState("serverPassword", value);
9506
- },
9507
- onUnlock: () => {
9508
- unlockServer();
9509
- },
9510
- get password() {
9511
- return state.serverPassword;
9512
- },
9513
- get visible() {
9514
- return state.serverLocked;
9515
- }
9516
- }), null);
9517
- insert(_el$3, createComponent(AuthPanel, {
9518
- get apiKeyDraft() {
9519
- return state.apiKeyDraft;
9520
- },
9521
- get auth() {
9522
- return state.appState?.auth;
9523
- },
9524
- onApiKeyDraftChange: (value) => {
9525
- setState("apiKeyDraft", value);
9526
- },
9527
- onImportDesktopSession: () => {
9528
- importDesktopSession();
9529
- },
9530
- onLogout: () => {
9531
- logout();
9532
- },
9533
- onRefresh: () => {
9534
- refreshAuth();
9535
- },
9536
- onSaveApiKey: () => {
9537
- saveApiKey();
9538
- },
9539
- onSwitchMode: (mode) => {
9540
- switchAuthMode(mode);
9541
- }
9542
- }), null);
9543
- insert(_el$3, createComponent(ExportJobsPanel, {
9544
- get jobs() {
9545
- return state.appState?.exports.jobs || [];
9546
- },
9547
- onRerun: (jobId) => {
9548
- rerunJob(jobId);
9549
- }
9550
- }), null);
9551
- insert(_el$3, createComponent(AutomationRunsPanel, {
9552
- onApprove: (runId) => {
9553
- resolveAutomationRun(runId, "approve");
9554
- },
9555
- onReject: (runId) => {
9556
- resolveAutomationRun(runId, "reject");
9557
- },
9558
- get runs() {
9559
- return state.automationRuns;
9560
- }
9561
- }), null);
9562
- insert(_el$3, createComponent(Workspace, {
9563
- get bundle() {
9564
- return state.selectedMeetingBundle;
9565
- },
9566
- get detailError() {
9567
- return state.detailError;
9568
- },
9569
- onSelectTab: (tab) => {
9570
- setState("workspaceTab", tab);
9571
- },
9572
- get selectedMeeting() {
9573
- return state.selectedMeeting;
9574
- },
9575
- get tab() {
9576
- return state.workspaceTab;
9577
- }
9578
- }), null);
9579
- return _el$;
9580
- })();
9581
- }
9582
- delegateEvents(["click"]);
9583
- //#endregion
9584
- //#region src/web-app/main.tsx
9585
- /** @jsxImportSource solid-js */
9586
- var root = document.getElementById("granola-web-root");
9587
- if (!root) throw new Error("Granola web root element not found");
9588
- render(() => createComponent(App, {}), root);
9589
- //#endregion
9590
- `;
6362
+ 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.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 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 display: grid;\n gap: 10px;\n}\n\n.folder-row,\n.meeting-row {\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.folder-row:hover,\n.folder-row[data-selected=\"true\"] {\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 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 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-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.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__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=\"completed\"] {\n background: rgba(36, 107, 79, 0.12);\n color: var(--ok);\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.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.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-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.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 .workspace-grid {\n grid-template-columns: 1fr;\n }\n}\n/*$vite$:1*/";
6363
+ 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 automationMatches: \"/automation/matches\",\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 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 granolaAutomationRunDecisionPath(id, decision) {\n return `${granolaTransportPaths.automationRuns}/${encodeURIComponent(id)}/${decision}`;\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 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, options.fetchImpl ?? fetch);\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 = options.fetchImpl ?? fetch;\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 listAutomationRules() {\n return await this.requestJson(granolaTransportPaths.automationRules);\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 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\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 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}\n//#endregion\n//#region src/web-app/components.tsx\n/** @jsxImportSource solid-js */\nvar _tmpl$$1 = /* @__PURE__ */ template(`<section class=hero><h1>Granola Toolkit</h1><p>Browser workspace for folders, meetings, notes, transcripts, and export flows on top of one local server instance.</p><input class=search placeholder=\"Search meetings, ids, or tags\"><div class=\"field-row field-row--inline\"><label><span class=field-label>Sort</span><select class=select><option value=updated-desc>Newest first</option><option value=updated-asc>Oldest first</option><option value=title-asc>Title A-Z</option><option value=title-desc>Title Z-A</option></select></label><label><span class=field-label>Updated From</span><input class=field-input type=date></label></div><label class=field-row><span class=field-label>Updated To</span><input class=field-input type=date>`), _tmpl$2 = /* @__PURE__ */ template(`<section class=toolbar><div><p>Meetings are loaded from the shared server state so this view can stay aligned with the terminal UI and sync loop.</p></div><div class=toolbar-form><input class=field-input placeholder=\"Quick open by id or title\"><button class=\"button button--secondary\"type=button>Open`), _tmpl$3 = /* @__PURE__ */ template(`<div class=\"folder-empty folder-empty--error\">`), _tmpl$4 = /* @__PURE__ */ template(`<section class=folder-panel><div class=folder-panel__head><h2>Folders</h2><p>Pick a folder to scope the meeting browser, or stay on All meetings.</p></div><div class=folder-list>`), _tmpl$5 = /* @__PURE__ */ template(`<button class=folder-row type=button><span class=folder-row__title>All meetings</span><span class=folder-row__meta>Browse the full meeting list.`), _tmpl$6 = /* @__PURE__ */ template(`<div class=folder-empty>No folders found.`), _tmpl$7 = /* @__PURE__ */ template(`<button class=folder-row type=button><span class=folder-row__title></span><span class=folder-row__meta>`), _tmpl$8 = /* @__PURE__ */ template(`<div class=\"meeting-empty meeting-empty--error\">`), _tmpl$9 = /* @__PURE__ */ template(`<section class=meeting-list>`), _tmpl$0 = /* @__PURE__ */ template(`<div class=meeting-empty>`), _tmpl$1 = /* @__PURE__ */ template(`<button class=meeting-row type=button><span class=meeting-row__title></span><span class=meeting-row__meta></span><span class=meeting-row__meta>`), _tmpl$10 = /* @__PURE__ */ template(`<section class=detail-head><div><h2>Meeting Workspace</h2></div><div class=state-badge>`), _tmpl$11 = /* @__PURE__ */ template(`<p>Waiting for server state…`), _tmpl$12 = /* @__PURE__ */ template(`<div class=status-grid><div><span class=status-label>Surface</span><strong></strong></div><div><span class=status-label>View</span><strong></strong></div><div><span class=status-label>Auth</span><strong></strong></div><div><span class=status-label>Sync</span><strong></strong></div><div><span class=status-label>Documents</span><strong></strong></div><div><span class=status-label>Folders</span><strong></strong></div><div><span class=status-label>Cache</span><strong></strong></div><div><span class=status-label>Index</span><strong></strong></div><div><span class=status-label>Automation</span><strong>`), _tmpl$13 = /* @__PURE__ */ template(`<section class=security-panel><div class=security-panel__head><h3>Server Access</h3><p>This server is locked with a password. Unlock it to load meetings and live state.</p></div><div class=security-panel__body><input class=field-input placeholder=\"Server password\"type=password><div class=toolbar-actions><button class=\"button button--primary\"type=button>Unlock</button><button class=\"button button--secondary\"type=button>Lock`), _tmpl$14 = /* @__PURE__ */ template(`<section class=auth-panel><div class=auth-panel__head><h3>Auth Session</h3><p>Inspect, refresh, and switch between API key, stored session, and <code>supabase.json</code>.</p></div><div class=auth-panel__body>`), _tmpl$15 = /* @__PURE__ */ template(`<div class=auth-card><div class=auth-card__meta>Auth state unavailable.`), _tmpl$16 = /* @__PURE__ */ template(`<div class=auth-card__meta>Client ID: `), _tmpl$17 = /* @__PURE__ */ template(`<div class=auth-card__meta>Sign-in method: `), _tmpl$18 = /* @__PURE__ */ template(`<div class=auth-card__meta>supabase path: `), _tmpl$19 = /* @__PURE__ */ template(`<div class=\"auth-card__meta auth-card__error\">`), _tmpl$20 = /* @__PURE__ */ template(`<div class=auth-card><div class=status-grid><div><span class=status-label>Active</span><strong></strong></div><div><span class=status-label>API key</span><strong></strong></div><div><span class=status-label>Stored</span><strong></strong></div><div><span class=status-label>supabase.json</span><strong></strong></div><div><span class=status-label>Refresh</span><strong></strong></div></div><div class=auth-card__meta>Store a Granola Personal API key here or use <code>granola auth login --api-key &lt;token&gt;</code>.</div><div class=auth-card__actions><input class=input placeholder=grn_... type=password><button class=\"button button--secondary\"type=button>Save API key</button><button class=\"button button--secondary\"type=button>Import desktop session</button><button class=\"button button--secondary\"type=button>Refresh stored session</button><button class=\"button button--secondary\"type=button>Use API key</button><button class=\"button button--secondary\"type=button>Use stored session</button><button class=\"button button--secondary\"type=button>Use supabase.json</button><button class=\"button button--secondary\"type=button>Sign out`), _tmpl$21 = /* @__PURE__ */ template(`<section class=jobs-panel><div class=jobs-panel__head><h3>Recent Export Jobs</h3><p>Tracked across CLI and web runs.</p></div><div class=jobs-list>`), _tmpl$22 = /* @__PURE__ */ template(`<div class=job-empty>No export jobs yet.`), _tmpl$23 = /* @__PURE__ */ template(`<div class=job-card__meta>`), _tmpl$24 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Rerun`), _tmpl$25 = /* @__PURE__ */ template(`<article class=job-card><div class=job-card__head><div><div class=job-card__title> export</div><div class=job-card__meta></div></div><div class=job-card__status></div></div><div class=job-card__meta></div><div class=job-card__meta>Started: </div><div class=job-card__meta>Output: </div><div class=job-card__actions>`), _tmpl$26 = /* @__PURE__ */ template(`<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$27 = /* @__PURE__ */ template(`<div class=job-empty>No automation runs yet.`), _tmpl$28 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Approve`), _tmpl$29 = /* @__PURE__ */ template(`<button class=\"button button--secondary\"type=button>Reject`), _tmpl$30 = /* @__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$31 = /* @__PURE__ */ template(`<nav class=workspace-tabs><span class=workspace-hint>1-4 switch tabs, [ and ] cycle`), _tmpl$32 = /* @__PURE__ */ template(`<button class=workspace-tab type=button>`), _tmpl$33 = /* @__PURE__ */ template(`<div class=empty>`), _tmpl$34 = /* @__PURE__ */ template(`<div class=detail-meta><div class=detail-chip></div><div class=detail-chip></div><div class=detail-chip>`), _tmpl$35 = /* @__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 authModeLabel(mode) {\n switch (mode) {\n case \"api-key\": return \"API key\";\n case \"stored-session\": return \"Stored session\";\n default: return \"supabase.json\";\n }\n}\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 ].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$$1(), _el$4 = _el$.firstChild.nextSibling.nextSibling, _el$5 = _el$4.nextSibling, _el$6 = _el$5.firstChild, _el$8 = _el$6.firstChild.nextSibling, _el$1 = _el$6.nextSibling.firstChild.nextSibling, _el$12 = _el$5.nextSibling.firstChild.nextSibling;\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(), _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(), _el$20 = _el$18.firstChild.nextSibling;\n insert(_el$20, createComponent(Show, {\n get fallback() {\n return [\n (() => {\n var _el$22 = _tmpl$5();\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(), _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();\n }\n })\n ];\n },\n get when() {\n return !props.error;\n },\n get children() {\n var _el$21 = _tmpl$3();\n insert(_el$21, () => props.error);\n return _el$21;\n }\n }));\n return _el$18;\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$27 = _tmpl$9();\n insert(_el$27, createComponent(Show, {\n get fallback() {\n return createComponent(Show, {\n get fallback() {\n return (() => {\n var _el$29 = _tmpl$0();\n insert(_el$29, (() => {\n var _c$ = memo(() => !!summary());\n return () => _c$() ? `No meetings match ${summary()}.` : \"No meetings yet. Try Sync now.\";\n })());\n return _el$29;\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$30 = _tmpl$1(), _el$31 = _el$30.firstChild, _el$32 = _el$31.nextSibling, _el$33 = _el$32.nextSibling;\n _el$30.$$click = () => {\n props.onSelect(meeting.id);\n };\n insert(_el$31, () => meeting.title || meeting.id);\n insert(_el$32, (() => {\n var _c$2 = memo(() => !!meeting.tags.length);\n return () => _c$2() ? meeting.tags.map((tag) => `#${tag}`).join(\" \") : \"untagged\";\n })());\n insert(_el$33, (() => {\n var _c$3 = memo(() => !!meeting.updatedAt);\n return () => _c$3() ? meeting.updatedAt.slice(0, 10) : \"unknown\";\n })());\n createRenderEffect(() => setAttribute(_el$30, \"data-selected\", meeting.id === props.selectedMeetingId ? \"true\" : void 0));\n return _el$30;\n })()\n });\n }\n });\n },\n get when() {\n return props.error;\n },\n get children() {\n var _el$28 = _tmpl$8();\n insert(_el$28, () => props.error);\n return _el$28;\n }\n }));\n return _el$27;\n })();\n}\nfunction AppStatePanel(props) {\n const syncStatus = () => {\n const sync = props.appState?.sync;\n if (!sync) return \"idle\";\n if (sync.running) return \"running\";\n if (sync.lastError) return \"error\";\n if (sync.lastCompletedAt) return `last ${sync.lastCompletedAt.slice(11, 19)}`;\n return \"idle\";\n };\n const authStatus = () => {\n if (!props.appState) return \"Waiting for auth state…\";\n return authModeLabel(props.appState.auth.mode);\n };\n return (() => {\n var _el$34 = _tmpl$10(), _el$35 = _el$34.firstChild;\n _el$35.firstChild;\n var _el$37 = _el$35.nextSibling;\n insert(_el$35, createComponent(Show, {\n get fallback() {\n return _tmpl$11();\n },\n get when() {\n return props.appState;\n },\n children: (appState) => (() => {\n var _el$39 = _tmpl$12(), _el$40 = _el$39.firstChild, _el$42 = _el$40.firstChild.nextSibling, _el$43 = _el$40.nextSibling, _el$45 = _el$43.firstChild.nextSibling, _el$46 = _el$43.nextSibling, _el$48 = _el$46.firstChild.nextSibling, _el$49 = _el$46.nextSibling, _el$51 = _el$49.firstChild.nextSibling, _el$52 = _el$49.nextSibling, _el$54 = _el$52.firstChild.nextSibling, _el$55 = _el$52.nextSibling, _el$57 = _el$55.firstChild.nextSibling, _el$58 = _el$55.nextSibling, _el$60 = _el$58.firstChild.nextSibling, _el$61 = _el$58.nextSibling, _el$63 = _el$61.firstChild.nextSibling, _el$66 = _el$61.nextSibling.firstChild.nextSibling;\n insert(_el$42, () => appState().ui.surface);\n insert(_el$45, () => appState().ui.view);\n insert(_el$48, authStatus);\n insert(_el$51, syncStatus);\n insert(_el$54, (() => {\n var _c$4 = memo(() => !!appState().documents.loaded);\n return () => _c$4() ? String(appState().documents.count) : \"not loaded\";\n })());\n insert(_el$57, (() => {\n var _c$5 = memo(() => !!appState().folders.loaded);\n return () => _c$5() ? String(appState().folders.count) : \"not loaded\";\n })());\n insert(_el$60, (() => {\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$63, (() => {\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$66, () => `${appState().automation.runCount} runs / ${appState().automation.pendingRunCount} pending`);\n return _el$39;\n })()\n }), null);\n insert(_el$37, () => props.statusLabel);\n createRenderEffect(() => setAttribute(_el$37, \"data-tone\", props.statusTone));\n return _el$34;\n })();\n}\nfunction SecurityPanel(props) {\n return createComponent(Show, {\n get when() {\n return props.visible;\n },\n get children() {\n var _el$67 = _tmpl$13(), _el$70 = _el$67.firstChild.nextSibling.firstChild, _el$72 = _el$70.nextSibling.firstChild, _el$73 = _el$72.nextSibling;\n _el$70.$$keydown = (event) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n props.onUnlock();\n }\n };\n _el$70.$$input = (event) => {\n props.onPasswordChange(event.currentTarget.value);\n };\n addEventListener(_el$72, \"click\", props.onUnlock, true);\n addEventListener(_el$73, \"click\", props.onLock, true);\n createRenderEffect(() => _el$70.value = props.password);\n return _el$67;\n }\n });\n}\nfunction AuthPanel(props) {\n return (() => {\n var _el$74 = _tmpl$14(), _el$76 = _el$74.firstChild.nextSibling;\n insert(_el$76, createComponent(Show, {\n get fallback() {\n return _tmpl$15();\n },\n get when() {\n return props.auth;\n },\n children: (auth) => (() => {\n var _el$78 = _tmpl$20(), _el$79 = _el$78.firstChild, _el$80 = _el$79.firstChild, _el$82 = _el$80.firstChild.nextSibling, _el$83 = _el$80.nextSibling, _el$85 = _el$83.firstChild.nextSibling, _el$86 = _el$83.nextSibling, _el$88 = _el$86.firstChild.nextSibling, _el$89 = _el$86.nextSibling, _el$91 = _el$89.firstChild.nextSibling, _el$94 = _el$89.nextSibling.firstChild.nextSibling, _el$102 = _el$79.nextSibling, _el$104 = _el$102.nextSibling.firstChild, _el$105 = _el$104.nextSibling, _el$106 = _el$105.nextSibling, _el$107 = _el$106.nextSibling, _el$108 = _el$107.nextSibling, _el$109 = _el$108.nextSibling, _el$110 = _el$109.nextSibling, _el$111 = _el$110.nextSibling;\n insert(_el$82, () => authModeLabel(auth().mode));\n insert(_el$85, () => auth().apiKeyAvailable ? \"available\" : \"missing\");\n insert(_el$88, () => auth().storedSessionAvailable ? \"available\" : \"missing\");\n insert(_el$91, () => auth().supabaseAvailable ? \"available\" : \"missing\");\n insert(_el$94, () => auth().refreshAvailable ? \"available\" : \"missing\");\n insert(_el$78, createComponent(Show, {\n get when() {\n return auth().clientId;\n },\n get children() {\n var _el$95 = _tmpl$16();\n _el$95.firstChild;\n insert(_el$95, () => auth().clientId, null);\n return _el$95;\n }\n }), _el$102);\n insert(_el$78, createComponent(Show, {\n get when() {\n return auth().signInMethod;\n },\n get children() {\n var _el$97 = _tmpl$17();\n _el$97.firstChild;\n insert(_el$97, () => auth().signInMethod, null);\n return _el$97;\n }\n }), _el$102);\n insert(_el$78, createComponent(Show, {\n get when() {\n return auth().supabasePath;\n },\n get children() {\n var _el$99 = _tmpl$18();\n _el$99.firstChild;\n insert(_el$99, () => auth().supabasePath, null);\n return _el$99;\n }\n }), _el$102);\n insert(_el$78, createComponent(Show, {\n get when() {\n return auth().lastError;\n },\n get children() {\n var _el$101 = _tmpl$19();\n insert(_el$101, () => auth().lastError);\n return _el$101;\n }\n }), _el$102);\n _el$104.$$input = (event) => {\n props.onApiKeyDraftChange(event.currentTarget.value);\n };\n addEventListener(_el$105, \"click\", props.onSaveApiKey, true);\n addEventListener(_el$106, \"click\", props.onImportDesktopSession, true);\n addEventListener(_el$107, \"click\", props.onRefresh, true);\n _el$108.$$click = () => {\n props.onSwitchMode(\"api-key\");\n };\n _el$109.$$click = () => {\n props.onSwitchMode(\"stored-session\");\n };\n _el$110.$$click = () => {\n props.onSwitchMode(\"supabase-file\");\n };\n addEventListener(_el$111, \"click\", props.onLogout, true);\n createRenderEffect((_p$) => {\n var _v$ = !auth().supabaseAvailable, _v$2 = !auth().storedSessionAvailable || !auth().refreshAvailable, _v$3 = !auth().apiKeyAvailable || auth().mode === \"api-key\", _v$4 = !auth().storedSessionAvailable || auth().mode === \"stored-session\", _v$5 = !auth().supabaseAvailable || auth().mode === \"supabase-file\", _v$6 = !auth().apiKeyAvailable && !auth().storedSessionAvailable;\n _v$ !== _p$.e && (_el$106.disabled = _p$.e = _v$);\n _v$2 !== _p$.t && (_el$107.disabled = _p$.t = _v$2);\n _v$3 !== _p$.a && (_el$108.disabled = _p$.a = _v$3);\n _v$4 !== _p$.o && (_el$109.disabled = _p$.o = _v$4);\n _v$5 !== _p$.i && (_el$110.disabled = _p$.i = _v$5);\n _v$6 !== _p$.n && (_el$111.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$104.value = props.apiKeyDraft);\n return _el$78;\n })()\n }));\n return _el$74;\n })();\n}\nfunction ExportJobsPanel(props) {\n return (() => {\n var _el$112 = _tmpl$21(), _el$114 = _el$112.firstChild.nextSibling;\n insert(_el$114, createComponent(Show, {\n get when() {\n return props.jobs.length > 0;\n },\n get fallback() {\n return _tmpl$22();\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$116 = _tmpl$25(), _el$117 = _el$116.firstChild, _el$118 = _el$117.firstChild, _el$119 = _el$118.firstChild, _el$120 = _el$119.firstChild, _el$121 = _el$119.nextSibling, _el$122 = _el$118.nextSibling, _el$123 = _el$117.nextSibling, _el$124 = _el$123.nextSibling;\n _el$124.firstChild;\n var _el$126 = _el$124.nextSibling;\n _el$126.firstChild;\n var _el$129 = _el$126.nextSibling;\n insert(_el$119, () => job.kind, _el$120);\n insert(_el$121, () => job.id);\n insert(_el$122, () => job.status);\n insert(_el$123, () => `Format: ${job.format} • ${scopeLabel(job.scope)} • ${job.itemCount > 0 ? `${job.completedCount}/${job.itemCount} items` : \"0 items\"} • Written: ${job.written}`);\n insert(_el$124, () => job.startedAt.slice(0, 19), null);\n insert(_el$126, () => job.outputDir, null);\n insert(_el$116, createComponent(Show, {\n get when() {\n return job.error;\n },\n get children() {\n var _el$128 = _tmpl$23();\n insert(_el$128, () => job.error);\n return _el$128;\n }\n }), _el$129);\n insert(_el$129, createComponent(Show, {\n get when() {\n return job.status !== \"running\";\n },\n get children() {\n var _el$130 = _tmpl$24();\n _el$130.$$click = () => {\n props.onRerun(job.id);\n };\n return _el$130;\n }\n }));\n createRenderEffect(() => setAttribute(_el$122, \"data-status\", job.status));\n return _el$116;\n })()\n });\n }\n }));\n return _el$112;\n })();\n}\nfunction AutomationRunsPanel(props) {\n return (() => {\n var _el$131 = _tmpl$26(), _el$133 = _el$131.firstChild.nextSibling;\n insert(_el$133, createComponent(Show, {\n get when() {\n return props.runs.length > 0;\n },\n get fallback() {\n return _tmpl$27();\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$135 = _tmpl$30(), _el$136 = _el$135.firstChild, _el$137 = _el$136.firstChild, _el$138 = _el$137.firstChild, _el$139 = _el$138.nextSibling, _el$140 = _el$137.nextSibling, _el$141 = _el$136.nextSibling, _el$142 = _el$141.nextSibling, _el$146 = _el$142.nextSibling;\n insert(_el$138, () => run.actionName);\n insert(_el$139, () => `${run.ruleName} • ${run.id}`);\n insert(_el$140, () => run.status);\n insert(_el$141, () => `${run.title} • ${run.eventKind}`);\n insert(_el$142, () => `Started: ${run.startedAt.slice(0, 19)}`);\n insert(_el$135, createComponent(Show, {\n get when() {\n return run.prompt;\n },\n get children() {\n var _el$143 = _tmpl$23();\n insert(_el$143, () => run.prompt);\n return _el$143;\n }\n }), _el$146);\n insert(_el$135, createComponent(Show, {\n get when() {\n return run.result;\n },\n get children() {\n var _el$144 = _tmpl$23();\n insert(_el$144, () => run.result);\n return _el$144;\n }\n }), _el$146);\n insert(_el$135, createComponent(Show, {\n get when() {\n return run.error;\n },\n get children() {\n var _el$145 = _tmpl$23();\n insert(_el$145, () => run.error);\n return _el$145;\n }\n }), _el$146);\n insert(_el$146, createComponent(Show, {\n get when() {\n return run.status === \"pending\";\n },\n get children() {\n return [(() => {\n var _el$147 = _tmpl$28();\n _el$147.$$click = () => {\n props.onApprove(run.id);\n };\n return _el$147;\n })(), (() => {\n var _el$148 = _tmpl$29();\n _el$148.$$click = () => {\n props.onReject(run.id);\n };\n return _el$148;\n })()];\n }\n }));\n createRenderEffect(() => setAttribute(_el$140, \"data-status\", run.status));\n return _el$135;\n })()\n });\n }\n }));\n return _el$131;\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$149 = _tmpl$31(), _el$150 = _el$149.firstChild;\n insert(_el$149, createComponent(For, {\n each: [\n \"notes\",\n \"transcript\",\n \"metadata\",\n \"raw\"\n ],\n children: (tab) => (() => {\n var _el$151 = _tmpl$32();\n _el$151.$$click = () => {\n props.onSelectTab(tab);\n };\n insert(_el$151, tab === \"notes\" ? \"Notes\" : tab === \"transcript\" ? \"Transcript\" : tab === \"metadata\" ? \"Metadata\" : \"Raw\");\n createRenderEffect(() => setAttribute(_el$151, \"data-selected\", parsedTab() === tab ? \"true\" : void 0));\n return _el$151;\n })()\n }), _el$150);\n return _el$149;\n })(), createComponent(Show, {\n get when() {\n return props.selectedMeeting;\n },\n get fallback() {\n return (() => {\n var _el$152 = _tmpl$33();\n insert(_el$152, () => props.detailError || \"Select a meeting to inspect its notes and transcript.\");\n return _el$152;\n })();\n },\n children: (meeting) => [(() => {\n var _el$153 = _tmpl$34(), _el$154 = _el$153.firstChild, _el$155 = _el$154.nextSibling, _el$156 = _el$155.nextSibling;\n insert(_el$154, () => `ID: ${meeting().meeting.id}`);\n insert(_el$155, () => `Source: ${meeting().meeting.noteContentSource}`);\n insert(_el$156, () => `Transcript: ${meeting().meeting.transcriptSegmentCount} segments`);\n return _el$153;\n })(), createComponent(Show, {\n get when() {\n return !props.detailError;\n },\n get fallback() {\n return (() => {\n var _el$165 = _tmpl$33();\n insert(_el$165, () => props.detailError);\n return _el$165;\n })();\n },\n get children() {\n var _el$157 = _tmpl$35(), _el$159 = _el$157.firstChild.firstChild, _el$161 = _el$159.firstChild.nextSibling, _el$163 = _el$159.nextSibling.firstChild, _el$164 = _el$163.nextSibling;\n insert(_el$161, () => metadataLines(meeting()));\n insert(_el$163, () => details()?.title);\n insert(_el$164, () => details()?.body);\n return _el$157;\n }\n })]\n })];\n}\ndelegateEvents([\n \"input\",\n \"keydown\",\n \"click\"\n]);\n//#endregion\n//#region src/web-app/App.tsx\n/** @jsxImportSource solid-js */\nvar _tmpl$ = /* @__PURE__ */ template(`<div class=shell><aside class=\"pane sidebar\"></aside><main class=\"pane detail\"><section class=toolbar><div class=toolbar-actions><button class=\"button button--primary\"type=button>Sync now</button><button class=\"button button--secondary\"type=button>Export Notes</button><button class=\"button button--secondary\"type=button>Export Transcripts</button></div><p>Solid-powered web workspace on top of the same local server, sync loop, and shared app contracts.`);\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 [state, setState] = createStore({\n apiKeyDraft: \"\",\n appState: null,\n automationRuns: [],\n detailError: \"\",\n folderError: \"\",\n folders: [],\n listError: \"\",\n meetingSource: \"live\",\n meetings: [],\n quickOpen: \"\",\n search: \"\",\n selectedFolderId: startup.folderId || null,\n selectedMeetingBundle: null,\n selectedMeetingId: startup.meetingId || null,\n selectedMeeting: null,\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 unsubscribe;\n const setStatus = (label, tone = \"idle\") => {\n setState({\n statusLabel: label,\n statusTone: tone\n });\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 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 } catch (error) {\n setState(\"selectedMeetingBundle\", null);\n setState(\"selectedMeeting\", null);\n setState(\"detailError\", error instanceof Error ? error.message : String(error));\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 }\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 }\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 loadAutomationRuns(),\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 (!client) return;\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 client.loginAuth({ apiKey: state.apiKeyDraft.trim() });\n setState(\"apiKeyDraft\", \"\");\n await mergeAuthState(auth);\n await refreshAll();\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 if (!client) return;\n setStatus(\"Importing desktop session…\", \"busy\");\n try {\n await mergeAuthState(await client.loginAuth());\n await refreshAll();\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 if (!client) return;\n setStatus(\"Refreshing session…\", \"busy\");\n try {\n await mergeAuthState(await client.refreshAuth());\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 if (!client) return;\n setStatus(\"Switching auth source…\", \"busy\");\n try {\n await mergeAuthState(await client.switchAuthMode(mode));\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 if (!client) return;\n setStatus(\"Signing out…\", \"busy\");\n try {\n await mergeAuthState(await client.logoutAuth());\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 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 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 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 automationRuns: [],\n detailError: \"\",\n folderError: \"\",\n folders: [],\n listError: \"\",\n meetings: [],\n selectedFolderId: null,\n selectedMeeting: null,\n selectedMeetingBundle: null,\n selectedMeetingId: null,\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) return;\n loadAutomationRuns();\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 return (() => {\n var _el$ = _tmpl$(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling, _el$4 = _el$3.firstChild, _el$6 = _el$4.firstChild.firstChild, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling;\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(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 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 exportNotes();\n };\n _el$8.$$click = () => {\n exportTranscripts();\n };\n insert(_el$3, 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$3, 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 insert(_el$3, createComponent(ExportJobsPanel, {\n get jobs() {\n return state.appState?.exports.jobs || [];\n },\n onRerun: (jobId) => {\n rerunJob(jobId);\n }\n }), null);\n insert(_el$3, 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 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 }), null);\n return _el$;\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";
9591
6364
  //#endregion
9592
6365
  //#region src/web/assets.ts
9593
6366
  const granolaWebAssetPaths = {