latticesql 4.3.0 → 4.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -23491,12 +23491,12 @@ var init_getNodeModulesParentDirs = __esm({
23491
23491
  "node_modules/@aws-sdk/core/dist-es/submodules/client/util-user-agent-node/getNodeModulesParentDirs.js"() {
23492
23492
  "use strict";
23493
23493
  import_node_path21 = require("path");
23494
- getNodeModulesParentDirs = (dirname18) => {
23494
+ getNodeModulesParentDirs = (dirname19) => {
23495
23495
  const cwd = process.cwd();
23496
- if (!dirname18) {
23496
+ if (!dirname19) {
23497
23497
  return [cwd];
23498
23498
  }
23499
- const normalizedPath = (0, import_node_path21.normalize)(dirname18);
23499
+ const normalizedPath = (0, import_node_path21.normalize)(dirname19);
23500
23500
  const parts = normalizedPath.split(import_node_path21.sep);
23501
23501
  const nodeModulesIndex = parts.indexOf("node_modules");
23502
23502
  const parentDir = nodeModulesIndex !== -1 ? parts.slice(0, nodeModulesIndex).join(import_node_path21.sep) : normalizedPath;
@@ -23574,8 +23574,8 @@ var init_getTypeScriptUserAgentPair = __esm({
23574
23574
  tscVersion = null;
23575
23575
  return void 0;
23576
23576
  }
23577
- const dirname18 = typeof __dirname !== "undefined" ? __dirname : void 0;
23578
- const nodeModulesParentDirs = getNodeModulesParentDirs(dirname18);
23577
+ const dirname19 = typeof __dirname !== "undefined" ? __dirname : void 0;
23578
+ const nodeModulesParentDirs = getNodeModulesParentDirs(dirname19);
23579
23579
  let versionFromApp;
23580
23580
  for (const nodeModulesParentDir of nodeModulesParentDirs) {
23581
23581
  try {
@@ -64302,12 +64302,16 @@ var tokensCss = `
64302
64302
  surface (a bare input on a modal, the Database wizard, User Config, etc.):
64303
64303
  dark text on a white field with a light border. Placeholders use the
64304
64304
  muted token. Affects every input/select/textarea across the GUI. */
64305
- input, select, textarea {
64305
+ /* Text-like fields only \u2014 radios/checkboxes keep their native rendering
64306
+ (the box/border treatment mangles them). The accent color tints the native
64307
+ controls so checked radios/checkboxes are on-brand blue. */
64308
+ input:not([type='radio']):not([type='checkbox']):not([type='range']), select, textarea {
64306
64309
  color: var(--text);
64307
64310
  background: var(--surface);
64308
64311
  border: 1px solid var(--border);
64309
64312
  border-radius: 6px;
64310
64313
  }
64314
+ input[type='radio'], input[type='checkbox'] { accent-color: var(--accent); }
64311
64315
  input::placeholder, textarea::placeholder {
64312
64316
  color: var(--text-muted);
64313
64317
  opacity: 1;
@@ -65292,7 +65296,7 @@ var teamsCss = ` /* \u2500\u2500 Teams (Project Config + User Config) \u2500\
65292
65296
  color: var(--text); font-weight: 500;
65293
65297
  text-transform: uppercase; letter-spacing: 0.04em;
65294
65298
  }
65295
- .modal .field input, .modal .field textarea {
65299
+ .modal .field input:not([type='radio']):not([type='checkbox']), .modal .field textarea {
65296
65300
  width: 100%; padding: 6px 8px;
65297
65301
  background: var(--surface); color: var(--text);
65298
65302
  border: 1px solid var(--border-strong);
@@ -65302,6 +65306,20 @@ var teamsCss = ` /* \u2500\u2500 Teams (Project Config + User Config) \u2500\
65302
65306
  color: var(--text-muted);
65303
65307
  }
65304
65308
  .modal .field textarea { min-height: 60px; font-family: ui-monospace, monospace; font-size: 12px; }
65309
+ /* New-workspace "Kind" selector \u2014 clean cards, blue-highlighted selection. */
65310
+ .wiz-kind-opts { display: flex; gap: 10px; margin-top: 6px; flex-wrap: wrap; }
65311
+ .wiz-kind-card {
65312
+ flex: 1 1 0; min-width: 132px; display: flex; align-items: center; gap: 9px;
65313
+ padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px;
65314
+ background: var(--surface); cursor: pointer;
65315
+ transition: border-color 0.12s ease, background 0.12s ease;
65316
+ }
65317
+ .wiz-kind-card:hover { border-color: var(--border-strong); }
65318
+ .wiz-kind-card:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
65319
+ .wiz-kind-card input { margin: 0; flex: 0 0 auto; }
65320
+ .wiz-kind-name { font-size: 13px; color: var(--text); text-transform: none; letter-spacing: 0; }
65321
+ .wiz-kind-sub { color: var(--text-muted); font-size: 11px; margin-left: 2px; }
65322
+ .wiz-kind-card:has(input:checked) .wiz-kind-name { color: var(--accent-deep); font-weight: 500; }
65305
65323
  .modal .copy-token {
65306
65324
  padding: 8px 10px; background: var(--surface-2); border: 1px solid var(--border);
65307
65325
  border-radius: 4px; font-family: ui-monospace, monospace; font-size: 12px;
@@ -66547,6 +66565,9 @@ var bootJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
66547
66565
  initRailDragDrop();
66548
66566
  renderComposer();
66549
66567
  initThreadControls();
66568
+ // Warm up on-device voice in the background shortly after boot so dictation
66569
+ // is ready on first use \u2014 no visible model-loading step, ever.
66570
+ if (typeof voicePreload === 'function') setTimeout(voicePreload, 1500);
66550
66571
  checkNativeSetup();
66551
66572
  // App is fully populated \u2014 reveal it (Feature C).
66552
66573
  hideAppLoading();
@@ -70310,9 +70331,14 @@ var dashboardJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
70310
70331
  }
70311
70332
  var d = displayFor(table);
70312
70333
  // The tab shows the open RECORD's name (e.g. an entity row), not the
70313
- // object/table name.
70334
+ // object/table name. Guard: only retitle a RECORD ('item:') tab \u2014 never the
70335
+ // shared Brain Graph ('graph') tab, even if a record render briefly runs
70336
+ // while the hash still resolves to the graph (which would rename \u{1F9E0}).
70314
70337
  if (typeof setTabTitle === 'function') {
70315
- setTabTitle(tabKeyForHash(location.hash), fsDisplayName(row) || d.label);
70338
+ var fsItemKey = tabKeyForHash(location.hash);
70339
+ if (fsItemKey && fsItemKey.indexOf('item:') === 0) {
70340
+ setTabTitle(fsItemKey, fsDisplayName(row) || d.label);
70341
+ }
70316
70342
  }
70317
70343
  // Files + artifacts get the two-view document layout (formatted display +
70318
70344
  // a View Source toggle) with a Version History + Delete dropdown \u2014 not the
@@ -70382,7 +70408,10 @@ var dashboardJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
70382
70408
  // The tab shows the FILE's name (e.g. "Properties Dashboard"), not the
70383
70409
  // object name ("Files").
70384
70410
  if (typeof setTabTitle === 'function') {
70385
- setTabTitle(tabKeyForHash(location.hash), fsDisplayName(row) || d.label);
70411
+ var fsDocKey = tabKeyForHash(location.hash);
70412
+ if (fsDocKey && fsDocKey.indexOf('item:') === 0) {
70413
+ setTabTitle(fsDocKey, fsDisplayName(row) || d.label);
70414
+ }
70386
70415
  }
70387
70416
  var mode = fileViewMode[id] || 'display';
70388
70417
  // Actions live in a dropdown menu next to the title; View source / Version
@@ -72589,15 +72618,18 @@ var dataModelJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
72589
72618
  return '' +
72590
72619
  nameField +
72591
72620
  '<div class="field"><label>Kind</label>' +
72592
- '<div style="display:flex;gap:16px;margin-top:4px;flex-wrap:wrap">' +
72593
- '<label style="display:flex;align-items:center;gap:6px;font-weight:400;text-transform:none;letter-spacing:0">' +
72594
- '<input type="radio" name="wiz-kind" value="local"' + (kind === 'local' ? ' checked' : '') + ' /> New local (SQLite)' +
72621
+ '<div class="wiz-kind-opts">' +
72622
+ '<label class="wiz-kind-card">' +
72623
+ '<input type="radio" name="wiz-kind" value="local"' + (kind === 'local' ? ' checked' : '') + ' />' +
72624
+ '<span class="wiz-kind-name">New local <span class="wiz-kind-sub">SQLite</span></span>' +
72595
72625
  '</label>' +
72596
- '<label style="display:flex;align-items:center;gap:6px;font-weight:400;text-transform:none;letter-spacing:0">' +
72597
- '<input type="radio" name="wiz-kind" value="cloud"' + (kind === 'cloud' ? ' checked' : '') + ' /> New cloud (Postgres)' +
72626
+ '<label class="wiz-kind-card">' +
72627
+ '<input type="radio" name="wiz-kind" value="cloud"' + (kind === 'cloud' ? ' checked' : '') + ' />' +
72628
+ '<span class="wiz-kind-name">New cloud <span class="wiz-kind-sub">Postgres</span></span>' +
72598
72629
  '</label>' +
72599
- '<label style="display:flex;align-items:center;gap:6px;font-weight:400;text-transform:none;letter-spacing:0">' +
72600
- '<input type="radio" name="wiz-kind" value="join"' + (kind === 'join' ? ' checked' : '') + ' /> Join a team (invite)' +
72630
+ '<label class="wiz-kind-card">' +
72631
+ '<input type="radio" name="wiz-kind" value="join"' + (kind === 'join' ? ' checked' : '') + ' />' +
72632
+ '<span class="wiz-kind-name">Join a team <span class="wiz-kind-sub">invite</span></span>' +
72601
72633
  '</label>' +
72602
72634
  '</div>' +
72603
72635
  '<p style="font-size:11px;color:var(--text-muted);margin:6px 0 0">' +
@@ -74742,6 +74774,14 @@ var voiceLocalJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
74742
74774
  return _voiceWorker;
74743
74775
  }
74744
74776
 
74777
+ // Warm up the voice worker + model in the BACKGROUND on launch, so dictation is
74778
+ // ready by the time the user records and any first-run weight fetch happens
74779
+ // silently up front (never mid-recording, never with a visible "downloading"
74780
+ // state). Idempotent + best-effort \u2014 a failure just leaves the mic to lazy-init.
74781
+ function voicePreload() {
74782
+ try { getVoiceWorker(); } catch (_) { /* best-effort */ }
74783
+ }
74784
+
74745
74785
  function voicePreferredDevice() {
74746
74786
  // WebGPU when the browser exposes it, else WASM. The worker downgrades to
74747
74787
  // wasm itself if the runtime can't honor webgpu, so this is just a hint.
@@ -74763,14 +74803,14 @@ var voiceLocalJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
74763
74803
  _voiceWorkerReady = false;
74764
74804
  }
74765
74805
 
74766
- // Forward model-download progress to the composer placeholder so the first
74767
- // use shows "Downloading voice model\u2026 N%" (it needs the network ONCE; after
74768
- // that it's cached and offline).
74769
- function onVoiceProgress(file, progress) {
74770
- var inp = document.getElementById('chat-input');
74771
- if (!inp) return;
74772
- var pct = (typeof progress === 'number' && progress > 0) ? (' ' + Math.round(progress) + '%') : '';
74773
- inp.setAttribute('placeholder', 'Downloading voice model\u2026' + pct);
74806
+ // Model loading is SILENT \u2014 the user never sees a "downloading voice model"
74807
+ // state. The worker + model are warmed up in the background on launch (see
74808
+ // voicePreload), and the ORT runtime ships bundled, so by the time anyone
74809
+ // records, dictation is ready. We intentionally surface no loading progress in
74810
+ // the composer; if a first-run weight fetch is still in flight, the existing
74811
+ // "Transcribing\u2026" placeholder covers it.
74812
+ function onVoiceProgress(_file, _progress) {
74813
+ /* intentionally silent \u2014 no visible voice-model loading UI */
74774
74814
  }
74775
74815
 
74776
74816
  // Decode a recorded audio blob to mono 16 kHz PCM (Float32Array), entirely in
@@ -75774,12 +75814,33 @@ var sourcesJs = `
75774
75814
  '<span class="src-name">' + escapeHtml(name) + '</span></div></li>';
75775
75815
  }).join('') + '</ul>'
75776
75816
  : '';
75817
+ // Preserve expanded folders across this re-render so an in-progress
75818
+ // ingest (which re-renders the sidebar) never snaps an open folder shut.
75819
+ // Capture each expanded folder's path + its lazily-loaded children markup,
75820
+ // then re-attach + re-open them after the rebuild (no re-fetch, no flicker).
75821
+ var openFolders = {};
75822
+ host.querySelectorAll('.src-folder[data-loaded="1"]').forEach(function (li) {
75823
+ var ul0 = li.querySelector(':scope > .src-children');
75824
+ if (ul0 && !ul0.hidden) openFolders[li.getAttribute('data-path')] = ul0.innerHTML;
75825
+ });
75777
75826
  if (!rootsHtml && !looseHtml) {
75778
75827
  host.innerHTML = '<div class="src-empty">No files yet.</div>';
75779
75828
  return;
75780
75829
  }
75781
75830
  host.innerHTML = rootsHtml + looseHtml;
75782
75831
  wireSourceTree(host);
75832
+ host.querySelectorAll('.src-folder').forEach(function (li) {
75833
+ var saved = openFolders[li.getAttribute('data-path')];
75834
+ if (saved == null) return;
75835
+ var ul1 = li.querySelector(':scope > .src-children');
75836
+ var caret = li.querySelector(':scope > .src-row > .src-caret');
75837
+ if (!ul1) return;
75838
+ ul1.innerHTML = saved;
75839
+ ul1.hidden = false;
75840
+ li.setAttribute('data-loaded', '1');
75841
+ if (caret) caret.textContent = '\u25BE';
75842
+ wireSourceTree(ul1);
75843
+ });
75783
75844
  })
75784
75845
  .catch(function () { host.innerHTML = ''; });
75785
75846
  }
@@ -80231,20 +80292,49 @@ var MAX_LIST_ENTRIES = 2e3;
80231
80292
  var MAX_INGEST_FILES = 500;
80232
80293
  var MAX_INGEST_DEPTH = 8;
80233
80294
  var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".DS_Store", "__pycache__", ".venv", "venv"]);
80234
- function rootsFile() {
80235
- return (0, import_node_path44.join)(configDir(), "sources.json");
80295
+ function rootsFile(configPath) {
80296
+ return (0, import_node_path44.join)((0, import_node_path44.dirname)(configPath), "sources.json");
80297
+ }
80298
+ function migrateGlobalRootsIfNeeded(configPath) {
80299
+ const wsFile = rootsFile(configPath);
80300
+ if ((0, import_node_fs41.existsSync)(wsFile)) return;
80301
+ const globalFile = (0, import_node_path44.join)(configDir(), "sources.json");
80302
+ if (globalFile === wsFile || !(0, import_node_fs41.existsSync)(globalFile)) return;
80303
+ let roots;
80304
+ try {
80305
+ const parsed = JSON.parse((0, import_node_fs41.readFileSync)(globalFile, "utf8"));
80306
+ if (!Array.isArray(parsed.roots) || parsed.roots.length === 0) return;
80307
+ roots = parsed.roots;
80308
+ } catch {
80309
+ return;
80310
+ }
80311
+ try {
80312
+ (0, import_node_fs41.mkdirSync)((0, import_node_path44.dirname)(wsFile), { recursive: true });
80313
+ (0, import_node_fs41.writeFileSync)(wsFile, JSON.stringify({ roots }, null, 2), "utf8");
80314
+ } catch {
80315
+ return;
80316
+ }
80317
+ try {
80318
+ (0, import_node_fs41.rmSync)(globalFile, { force: true });
80319
+ } catch {
80320
+ try {
80321
+ (0, import_node_fs41.writeFileSync)(globalFile, JSON.stringify({ roots: [] }, null, 2), "utf8");
80322
+ } catch {
80323
+ }
80324
+ }
80236
80325
  }
80237
- function readRoots() {
80326
+ function readRoots(configPath) {
80327
+ migrateGlobalRootsIfNeeded(configPath);
80238
80328
  try {
80239
- const raw = (0, import_node_fs41.readFileSync)(rootsFile(), "utf8");
80329
+ const raw = (0, import_node_fs41.readFileSync)(rootsFile(configPath), "utf8");
80240
80330
  const parsed = JSON.parse(raw);
80241
80331
  return Array.isArray(parsed.roots) ? parsed.roots : [];
80242
80332
  } catch {
80243
80333
  return [];
80244
80334
  }
80245
80335
  }
80246
- function writeRoots(roots) {
80247
- (0, import_node_fs41.writeFileSync)(rootsFile(), JSON.stringify({ roots }, null, 2), "utf8");
80336
+ function writeRoots(configPath, roots) {
80337
+ (0, import_node_fs41.writeFileSync)(rootsFile(configPath), JSON.stringify({ roots }, null, 2), "utf8");
80248
80338
  }
80249
80339
  function safeResolveInside2(target, roots) {
80250
80340
  const abs = (0, import_node_path44.resolve)(target);
@@ -80334,14 +80424,14 @@ async function ingestFolder(abs, ingestFile) {
80334
80424
  return { ingested, skipped };
80335
80425
  }
80336
80426
  async function dispatchSourcesRoute(req, res, deps) {
80337
- const { pathname, method, ingestFile } = deps;
80427
+ const { pathname, method, ingestFile, configPath } = deps;
80338
80428
  if (!pathname.startsWith("/api/sources/")) return false;
80339
80429
  if (!localFileOpenEnabled()) {
80340
80430
  sendJson(res, { enabled: false });
80341
80431
  return true;
80342
80432
  }
80343
80433
  if (pathname === "/api/sources/roots" && method === "GET") {
80344
- sendJson(res, { enabled: true, roots: readRoots() });
80434
+ sendJson(res, { enabled: true, roots: readRoots(configPath) });
80345
80435
  return true;
80346
80436
  }
80347
80437
  if (pathname === "/api/sources/roots" && method === "POST") {
@@ -80367,12 +80457,12 @@ async function dispatchSourcesRoute(req, res, deps) {
80367
80457
  sendJson(res, { error: `path not found: ${abs}` }, 400);
80368
80458
  return true;
80369
80459
  }
80370
- const roots = readRoots();
80460
+ const roots = readRoots(configPath);
80371
80461
  let root6 = roots.find((r6) => (0, import_node_path44.resolve)(r6.path) === abs);
80372
80462
  if (!root6) {
80373
80463
  root6 = { id: (0, import_node_crypto24.randomUUID)(), path: abs, kind, name: (0, import_node_path44.basename)(abs) || abs };
80374
80464
  roots.push(root6);
80375
- writeRoots(roots);
80465
+ writeRoots(configPath, roots);
80376
80466
  }
80377
80467
  let result;
80378
80468
  if (kind === "folder") result = await ingestFolder(abs, ingestFile);
@@ -80383,8 +80473,8 @@ async function dispatchSourcesRoute(req, res, deps) {
80383
80473
  const delMatch = /^\/api\/sources\/roots\/([^/]+)$/.exec(pathname);
80384
80474
  if (delMatch && method === "DELETE") {
80385
80475
  const id = decodeURIComponent(delMatch[1] ?? "");
80386
- const roots = readRoots().filter((r6) => r6.id !== id);
80387
- writeRoots(roots);
80476
+ const roots = readRoots(configPath).filter((r6) => r6.id !== id);
80477
+ writeRoots(configPath, roots);
80388
80478
  sendJson(res, { ok: true });
80389
80479
  return true;
80390
80480
  }
@@ -80402,7 +80492,7 @@ async function dispatchSourcesRoute(req, res, deps) {
80402
80492
  sendJson(res, { error: "path is required" }, 400);
80403
80493
  return true;
80404
80494
  }
80405
- const abs = safeResolveInside2(target, readRoots());
80495
+ const abs = safeResolveInside2(target, readRoots(configPath));
80406
80496
  if (!abs) {
80407
80497
  sendJson(res, { error: "path is outside any registered source root" }, 403);
80408
80498
  return true;
@@ -80417,7 +80507,7 @@ async function dispatchSourcesRoute(req, res, deps) {
80417
80507
  if (pathname === "/api/sources/ingest-folder" && method === "POST") {
80418
80508
  const body = await readJson(req).catch(() => ({}));
80419
80509
  const target = typeof body.path === "string" ? body.path : "";
80420
- const abs = safeResolveInside2(target, readRoots());
80510
+ const abs = safeResolveInside2(target, readRoots(configPath));
80421
80511
  if (!abs) {
80422
80512
  sendJson(res, { error: "path is outside any registered source root" }, 403);
80423
80513
  return true;
@@ -83056,6 +83146,7 @@ async function startGuiServer(options) {
83056
83146
  return await dispatchSourcesRoute(req2, res2, {
83057
83147
  db: active.db,
83058
83148
  ingestFile: (p3) => ingestLocalFile(ingestCtx, mctx, p3, false),
83149
+ configPath: active.configPath,
83059
83150
  pathname,
83060
83151
  method
83061
83152
  });