grain-tools 0.10.12 → 0.10.14

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/web-app.html CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Grain</title>
6
+ <title>Grain (Beta)</title>
7
7
  <meta name="theme-color" content="#1e1e1e" />
8
8
  <link
9
9
  rel="icon"
@@ -12808,6 +12808,7 @@ function detectInitialTheme() {
12808
12808
  }
12809
12809
  const useAppStore = create$3((set2, get2) => ({
12810
12810
  theme: detectInitialTheme(),
12811
+ panelTheme: "inherit",
12811
12812
  activeView: "query",
12812
12813
  copyIncludesHeaders: true,
12813
12814
  rowDensity: "normal",
@@ -12902,41 +12903,23 @@ const useAppStore = create$3((set2, get2) => ({
12902
12903
  if (c.fontSize !== void 0) {
12903
12904
  newState.fontSize = c.fontSize !== null && c.fontSize !== void 0 && c.fontSize > 0 ? c.fontSize : null;
12904
12905
  }
12905
- if (c.maxRowsToDisplay !== void 0) {
12906
- newState.maxRowsToDisplay = c.maxRowsToDisplay;
12907
- }
12908
- if (c.telemetryEnabled !== void 0) {
12909
- newState.telemetryEnabled = c.telemetryEnabled;
12910
- }
12911
- if (c.chartsEnabled !== void 0) {
12912
- newState.chartsEnabled = c.chartsEnabled;
12913
- }
12914
- if (c.chartLayout !== void 0) {
12915
- newState.chartLayout = c.chartLayout;
12916
- }
12917
- if (c.filterResultsByActiveFile !== void 0) {
12918
- newState.filterResultsByActiveFile = c.filterResultsByActiveFile;
12919
- }
12920
- if (c.mcpEnabled !== void 0) {
12921
- newState.mcpEnabled = c.mcpEnabled;
12922
- }
12923
- if (c.mcpEntitled !== void 0) {
12924
- newState.mcpEntitled = c.mcpEntitled;
12925
- }
12926
- if (c.mcpSafeMode !== void 0) {
12927
- newState.mcpSafeMode = c.mcpSafeMode;
12928
- }
12929
- if (c.activeConnectorType !== void 0) {
12930
- newState.activeConnectorType = c.activeConnectorType;
12931
- }
12932
- if (c.activeConnectionId !== void 0) {
12933
- newState.activeConnectionId = c.activeConnectionId;
12934
- }
12935
- if (c.activeConnectionName !== void 0) {
12936
- newState.activeConnectionName = c.activeConnectionName;
12937
- }
12938
- if (c.confirmWriteQueries !== void 0) {
12939
- newState.confirmWriteQueries = c.confirmWriteQueries;
12906
+ const passthrough = [
12907
+ "maxRowsToDisplay",
12908
+ "telemetryEnabled",
12909
+ "chartsEnabled",
12910
+ "chartLayout",
12911
+ "panelTheme",
12912
+ "filterResultsByActiveFile",
12913
+ "mcpEnabled",
12914
+ "mcpEntitled",
12915
+ "mcpSafeMode",
12916
+ "activeConnectorType",
12917
+ "activeConnectionId",
12918
+ "activeConnectionName",
12919
+ "confirmWriteQueries"
12920
+ ];
12921
+ for (const key of passthrough) {
12922
+ if (c[key] !== void 0) Object.assign(newState, { [key]: c[key] });
12940
12923
  }
12941
12924
  if (c.mcpStatus !== void 0) {
12942
12925
  newState.mcpPort = ((_a3 = c.mcpStatus) == null ? void 0 : _a3.port) ?? null;
@@ -12965,7 +12948,8 @@ const useAppStore = create$3((set2, get2) => ({
12965
12948
  };
12966
12949
  set2((state) => ({
12967
12950
  tabs: [...state.tabs, newTab],
12968
- activeTabId: activate ? id : state.activeTabId
12951
+ activeTabId: activate ? id : state.activeTabId,
12952
+ guideHub: activate ? null : state.guideHub
12969
12953
  }));
12970
12954
  return id;
12971
12955
  },
@@ -12981,7 +12965,7 @@ const useAppStore = create$3((set2, get2) => ({
12981
12965
  activeTabId: newActiveId
12982
12966
  };
12983
12967
  }),
12984
- setActiveTab: (id) => set2({ activeTabId: id }),
12968
+ setActiveTab: (id) => set2({ activeTabId: id, guideHub: null }),
12985
12969
  updateTab: (id, updates) => set2((state) => ({
12986
12970
  tabs: state.tabs.map((tab) => {
12987
12971
  if (tab.id !== id) {
@@ -12991,6 +12975,10 @@ const useAppStore = create$3((set2, get2) => ({
12991
12975
  if (updates.isLoading === true && !tab.isLoading) {
12992
12976
  next.loadingStartedAt = Date.now();
12993
12977
  next.cancelled = false;
12978
+ next.notPersisted = false;
12979
+ }
12980
+ if (updates.result) {
12981
+ next.notPersisted = false;
12994
12982
  }
12995
12983
  if (updates.result !== void 0 && updates.chartSpec === void 0) {
12996
12984
  const signature = (r) => r ? r.columns.map((c) => `${c.name}:${c.type}`).join(" ") : "";
@@ -13079,6 +13067,35 @@ function useWebBootstrap() {
13079
13067
  return unsubscribe;
13080
13068
  }, []);
13081
13069
  }
13070
+ const ALL_TOOLS = [
13071
+ "list_connectors",
13072
+ "run_query",
13073
+ "get_tab_info",
13074
+ "list_sessions",
13075
+ "list_connections",
13076
+ "save_connection",
13077
+ "request_connection_credentials",
13078
+ "test_connection",
13079
+ "delete_connection",
13080
+ "list_schemas",
13081
+ "list_tables",
13082
+ "describe_table",
13083
+ "cancel_query",
13084
+ "close_tab",
13085
+ "render_chart",
13086
+ "save_chart",
13087
+ "list_charts",
13088
+ "ledger_stats",
13089
+ "verify_ledger",
13090
+ "record_ui_event"
13091
+ ];
13092
+ const WEB_PROXIED_TOOLS = [
13093
+ "list_connectors",
13094
+ "list_connections",
13095
+ "save_connection",
13096
+ "test_connection",
13097
+ "delete_connection"
13098
+ ];
13082
13099
  const TELEMETRY_EVENTS = [
13083
13100
  "extension_activated",
13084
13101
  "command_invoked",
@@ -13129,7 +13146,14 @@ const TELEMETRY_MCP_CLIENTS = [
13129
13146
  "unknown"
13130
13147
  ];
13131
13148
  new Set(TELEMETRY_MCP_CLIENTS);
13132
- const TELEMETRY_UI_ACTIONS = ["sort", "filter", "export", "tab_switch"];
13149
+ const TELEMETRY_UI_ACTIONS = [
13150
+ "sort",
13151
+ "filter",
13152
+ "export",
13153
+ "tab_switch",
13154
+ "run_query",
13155
+ "connection_created"
13156
+ ];
13133
13157
  new Set(TELEMETRY_UI_ACTIONS);
13134
13158
  const TELEMETRY_CONNECTORS = [
13135
13159
  "trino",
@@ -13145,29 +13169,7 @@ const TELEMETRY_CONNECTORS = [
13145
13169
  "custom",
13146
13170
  "unknown"
13147
13171
  ];
13148
- const TELEMETRY_TOOL_NAMES = [
13149
- "list_connectors",
13150
- "run_query",
13151
- "get_tab_info",
13152
- "list_sessions",
13153
- "list_connections",
13154
- "save_connection",
13155
- "request_connection_credentials",
13156
- "test_connection",
13157
- "delete_connection",
13158
- "list_schemas",
13159
- "list_tables",
13160
- "describe_table",
13161
- "cancel_query",
13162
- "close_tab",
13163
- "render_chart",
13164
- "save_chart",
13165
- "list_charts",
13166
- "ledger_stats",
13167
- "verify_ledger",
13168
- "record_ui_event",
13169
- "unknown"
13170
- ];
13172
+ const TELEMETRY_TOOL_NAMES = [...ALL_TOOLS, "unknown"];
13171
13173
  const TELEMETRY_COMMAND_IDS = [
13172
13174
  "grain.showDaemonInfo",
13173
13175
  "grain.mcp.restart",
@@ -13210,6 +13212,7 @@ const TELEMETRY_STATES = [
13210
13212
  "extension_host",
13211
13213
  "onboarding",
13212
13214
  "query_editor",
13215
+ "workspace_ready",
13213
13216
  "connection_manager",
13214
13217
  "connection_form",
13215
13218
  "results_grid",
@@ -13545,6 +13548,7 @@ const TELEMETRY_ERROR_IDS = [
13545
13548
  "connection_save_unknown",
13546
13549
  /** The supervisor rejected registration of the installed service. */
13547
13550
  "service_supervisor_registration_failed",
13551
+ "service_runtime_provision_failed",
13548
13552
  /** The supervisor rejected starting the installed service. */
13549
13553
  "service_supervisor_start_failed",
13550
13554
  /** The supervisor accepted the service, but it never published a reachable endpoint. */
@@ -13783,6 +13787,27 @@ function resolveProjectedTarget(tabId, payload) {
13783
13787
  projectedTabId: tabId && parsed ? tabId : createProjectedTabId(sessionId === "all" ? WEB_SESSION_ID : sessionId, remoteTabId)
13784
13788
  };
13785
13789
  }
13790
+ const WRITE_NOT_CONFIRMED_MESSAGE = "Not run. The write was not confirmed.";
13791
+ async function postWebQuery(body, confirm = (message) => window.confirm(message)) {
13792
+ var _a3;
13793
+ const send = async (payload) => {
13794
+ const res = await authorizedFetch("/api/v1/query", {
13795
+ method: "POST",
13796
+ headers: { "Content-Type": "application/json" },
13797
+ body: JSON.stringify(payload)
13798
+ });
13799
+ return { status: res.status, ok: res.ok, data: await res.json() };
13800
+ };
13801
+ const first = await send(body);
13802
+ if (first.status !== 409 || ((_a3 = first.data) == null ? void 0 : _a3.status) !== "confirmation_required") {
13803
+ return { ok: first.ok, data: first.data };
13804
+ }
13805
+ if (!confirm(`${first.data.error} Run it anyway?`)) {
13806
+ return { ok: false, data: { error: WRITE_NOT_CONFIRMED_MESSAGE } };
13807
+ }
13808
+ const second = await send({ ...body, confirmWrite: true });
13809
+ return { ok: second.ok, data: second.data };
13810
+ }
13786
13811
  function useWebCommandDispatcher(dispatch) {
13787
13812
  return reactExports.useCallback(
13788
13813
  (command, payload) => {
@@ -13938,19 +13963,14 @@ function useWebCommandDispatcher(dispatch) {
13938
13963
  clientType: tab == null ? void 0 : tab.clientType,
13939
13964
  isRemote: true
13940
13965
  });
13941
- authorizedFetch("/api/v1/query", {
13942
- method: "POST",
13943
- headers: { "Content-Type": "application/json" },
13944
- body: JSON.stringify({
13945
- query: sql,
13946
- sessionId: target.sessionId,
13947
- connectionId,
13948
- tabId: target.remoteTabId
13949
- })
13950
- }).then(async (res) => {
13966
+ postWebQuery({
13967
+ query: sql,
13968
+ sessionId: target.sessionId,
13969
+ connectionId,
13970
+ tabId: target.remoteTabId
13971
+ }).then(({ ok, data }) => {
13951
13972
  var _a3;
13952
- const data = await res.json();
13953
- if (!res.ok) {
13973
+ if (!ok) {
13954
13974
  dispatch({
13955
13975
  type: "queryError",
13956
13976
  tabId: target.projectedTabId,
@@ -14180,13 +14200,6 @@ function useWebTabSync(options) {
14180
14200
  ]);
14181
14201
  return { syncState };
14182
14202
  }
14183
- const WEB_PROXIED_TOOLS = [
14184
- "list_connectors",
14185
- "list_connections",
14186
- "save_connection",
14187
- "test_connection",
14188
- "delete_connection"
14189
- ];
14190
14203
  const PROXIED_TOOLS = new Set(WEB_PROXIED_TOOLS);
14191
14204
  function useWebToolProxy() {
14192
14205
  return reactExports.useCallback(async (name, args) => {
@@ -14205,18 +14218,13 @@ function useWebToolProxy() {
14205
14218
  }
14206
14219
  if (name === "run_query") {
14207
14220
  const sessionId = args.session || WEB_SESSION_ID;
14208
- const res = await authorizedFetch("/api/v1/query", {
14209
- method: "POST",
14210
- headers: { "Content-Type": "application/json" },
14211
- body: JSON.stringify({
14212
- query: args.sql,
14213
- sessionId,
14214
- connectionId: args.connectionId,
14215
- tabId: args.tabId
14216
- })
14221
+ const { ok, data } = await postWebQuery({
14222
+ query: args.sql,
14223
+ sessionId,
14224
+ connectionId: args.connectionId,
14225
+ tabId: args.tabId
14217
14226
  });
14218
- const data = await res.json();
14219
- if (!res.ok) {
14227
+ if (!ok) {
14220
14228
  throw new Error(data.error || "Query failed");
14221
14229
  }
14222
14230
  return {
@@ -14327,18 +14335,32 @@ function WebHostProvider({ children }) {
14327
14335
  function ReauthorizeNotice({
14328
14336
  reason
14329
14337
  }) {
14338
+ const [copied, setCopied] = reactExports.useState(false);
14339
+ const copyCommand = async () => {
14340
+ try {
14341
+ await navigator.clipboard.writeText("grain open");
14342
+ setCopied(true);
14343
+ } catch {
14344
+ setCopied(false);
14345
+ }
14346
+ };
14330
14347
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-reauth", role: "alert", children: [
14331
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-web-reauth-title", children: reason === "unreachable" ? "Grain is no longer answering at this address" : reason === "expired-link" ? "This link has already been used" : "Open this workspace from your terminal" }),
14332
- /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { children: [
14333
- reason === "expired-link" && "An authorization link works once, and only for a short while.",
14334
- reason === "no-session" && "A workspace session is short-lived. A new tab, a restart, or a session left open for a while all end up here.",
14335
- reason === "unreachable" && "The service stopped, or it came back on a different port. This page cannot find it again on its own."
14336
- ] }),
14337
- /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { children: [
14338
- "Run ",
14348
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-web-reauth-title", children: "Grain is running. Reconnect this browser tab." }),
14349
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Run this command in your terminal to open your workspace again:" }),
14350
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-reauth-action", children: [
14339
14351
  /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "sp-web-reauth-command", children: "grain open" }),
14340
- " in a terminal to get a fresh workspace."
14341
- ] })
14352
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
14353
+ "button",
14354
+ {
14355
+ type: "button",
14356
+ className: "sp-web-secondary-button",
14357
+ onClick: () => void copyCommand(),
14358
+ "aria-label": "Copy grain open",
14359
+ children: copied ? "Copied" : "Copy"
14360
+ }
14361
+ )
14362
+ ] }),
14363
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-web-reauth-detail", children: reason === "expired-link" ? "This link has expired or was already used." : reason === "unreachable" ? "Grain may have restarted since this tab opened." : "This tab needs a fresh workspace link." })
14342
14364
  ] });
14343
14365
  }
14344
14366
  function isUpdateMarkerStatus(value) {
@@ -14459,20 +14481,25 @@ function fieldVisible(field, values) {
14459
14481
  }
14460
14482
  function ConnectionSetupPage({
14461
14483
  onDone,
14462
- onCancel
14484
+ onCancel,
14485
+ initialConnection
14463
14486
  }) {
14464
14487
  var _a3;
14465
14488
  const { callServerTool, isReady } = useHostContext();
14466
14489
  const [connectors, setConnectors] = reactExports.useState([]);
14467
- const [type, setType] = reactExports.useState("");
14468
- const [values, setValues] = reactExports.useState({});
14469
- const [name, setName] = reactExports.useState("");
14490
+ const [type, setType] = reactExports.useState(String((initialConnection == null ? void 0 : initialConnection["type"]) ?? ""));
14491
+ const [values, setValues] = reactExports.useState(initialConnection ?? {});
14492
+ const [name, setName] = reactExports.useState(String((initialConnection == null ? void 0 : initialConnection["name"]) ?? ""));
14470
14493
  const [password, setPassword] = reactExports.useState("");
14471
14494
  const [showPassword, setShowPassword] = reactExports.useState(false);
14472
14495
  const [busy, setBusy] = reactExports.useState(null);
14473
14496
  const [error, setError] = reactExports.useState("");
14474
14497
  const [tested, setTested] = reactExports.useState(false);
14475
- const [draftId] = reactExports.useState(() => `conn_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`);
14498
+ const [draftId] = reactExports.useState(
14499
+ () => String(
14500
+ (initialConnection == null ? void 0 : initialConnection["id"]) ?? `conn_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
14501
+ )
14502
+ );
14476
14503
  reactExports.useEffect(() => {
14477
14504
  if (!isReady) {
14478
14505
  return;
@@ -14498,11 +14525,12 @@ function ConnectionSetupPage({
14498
14525
  setConnectors(available);
14499
14526
  const first = available[0];
14500
14527
  setType((current2) => current2 || (first == null ? void 0 : first.id) || "");
14501
- setValues(
14502
- Object.fromEntries(
14503
- Object.entries(((_a4 = first == null ? void 0 : first.schema) == null ? void 0 : _a4.properties) ?? {}).filter(([key, field]) => key !== "password" && field.default !== void 0).map(([key, field]) => [key, field.default])
14504
- )
14505
- );
14528
+ if (!initialConnection)
14529
+ setValues(
14530
+ Object.fromEntries(
14531
+ Object.entries(((_a4 = first == null ? void 0 : first.schema) == null ? void 0 : _a4.properties) ?? {}).filter(([key, field]) => key !== "password" && field.default !== void 0).map(([key, field]) => [key, field.default])
14532
+ )
14533
+ );
14506
14534
  }).catch((cause) => {
14507
14535
  if (active) {
14508
14536
  setError(cause instanceof Error ? cause.message : String(cause));
@@ -14511,7 +14539,7 @@ function ConnectionSetupPage({
14511
14539
  return () => {
14512
14540
  active = false;
14513
14541
  };
14514
- }, [callServerTool, isReady]);
14542
+ }, [callServerTool, isReady, initialConnection]);
14515
14543
  const selected = connectors.find((connector) => connector.id === type);
14516
14544
  const properties = ((_a3 = selected == null ? void 0 : selected.schema) == null ? void 0 : _a3.properties) ?? {};
14517
14545
  const entries = Object.entries(properties).filter(([key, field]) => {
@@ -14537,10 +14565,10 @@ function ConnectionSetupPage({
14537
14565
  ...Object.fromEntries(entries.map(([key, field]) => [key, values[key] ?? field.default]))
14538
14566
  });
14539
14567
  const test = async () => {
14540
- const result = await callServerTool("test_connection", {
14541
- type,
14542
- connectionProfile: { ...profile(), ...password ? { password } : {} }
14543
- });
14568
+ const result = await callServerTool(
14569
+ "test_connection",
14570
+ initialConnection && !password ? { connectionId: draftId } : { type, connectionProfile: { ...profile(), ...password ? { password } : {} } }
14571
+ );
14544
14572
  const structured = result.structuredContent;
14545
14573
  if (result.isError || (structured == null ? void 0 : structured.success) === false || /failed|error/i.test(toolMessage(result))) {
14546
14574
  throw new Error(toolMessage(result));
@@ -14641,7 +14669,7 @@ function ConnectionSetupPage({
14641
14669
  };
14642
14670
  return /* @__PURE__ */ jsxRuntimeExports.jsx("main", { className: "grain-connection-page", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grain-connection-content", children: [
14643
14671
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grain-connection-intro", children: [
14644
- /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { children: "Connect a database" }),
14672
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { children: initialConnection ? "Edit connection" : "Connect a database" }),
14645
14673
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Add a connection to start querying your data." })
14646
14674
  ] }),
14647
14675
  /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSubmit, children: [
@@ -149282,9 +149310,6 @@ function FileIcon({ size = 20 }) {
149282
149310
  function PlugIcon({ size = 17 }) {
149283
149311
  return /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { width: size, height: size, viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5.5 1.5h1.4v3h2.2v-3h1.4v3h1a.5.5 0 01.5.5v2a4 4 0 01-3.3 3.94v1.06a1.5 1.5 0 01-3 0v-1.06A4 4 0 012.4 7V5a.5.5 0 01.5-.5h2.6v-3z" }) });
149284
149312
  }
149285
- function UndoIcon({ size = 16 }) {
149286
- return /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C20.48 10.9 16.89 8 12.5 8z" }) });
149287
- }
149288
149313
  const MIN_WIDTH = 200;
149289
149314
  const KEYBOARD_RESIZE_STEP = 16;
149290
149315
  function maxWidth() {
@@ -149772,6 +149797,199 @@ function ConnectorGlyph({ type }) {
149772
149797
  function getConnectorGroup(id) {
149773
149798
  return id === "duckdb" || id === "sqlite" ? "local" : "server";
149774
149799
  }
149800
+ const PATHS = {
149801
+ play: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 3.5v9l7-4.5-7-4.5z" }),
149802
+ stop: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1" }),
149803
+ copy: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
149804
+ /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "5.5", y: "5.5", width: "8", height: "8", rx: "1.5" }),
149805
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M10.5 3.5v-.5a1 1 0 0 0-1-1H3.5a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h.5" })
149806
+ ] }),
149807
+ download: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 2.5v8M4.5 7L8 10.5 11.5 7M3 13.5h10" }),
149808
+ filter: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M2.5 3.5h11L9.25 8.5v4l-2.5 1.25V8.5L2.5 3.5z" }),
149809
+ close: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M4 4l8 8M12 4l-8 8" }),
149810
+ settings: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
149811
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "8", cy: "8", r: "2" }),
149812
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 1.75v1.5M8 12.75v1.5M14.25 8h-1.5M3.25 8h-1.5M12.42 3.58l-1.06 1.06M4.64 11.36l-1.06 1.06M12.42 12.42l-1.06-1.06M4.64 4.64L3.58 3.58" })
149813
+ ] }),
149814
+ refresh: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M13.5 8a5.5 5.5 0 1 1-1.61-3.89M13.5 2.5v3h-3" }),
149815
+ undo: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5.5 3L2.5 6l3 3M2.5 6h7a4 4 0 0 1 0 8H7" }),
149816
+ "arrow-left": /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M13 8H3M7 4L3 8l4 4" }),
149817
+ "chevron-down": /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M4 6l4 4 4-4" }),
149818
+ alert: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
149819
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "8", cy: "8", r: "6" }),
149820
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 4.75v3.75M8 10.75v.5" })
149821
+ ] }),
149822
+ info: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
149823
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "8", cy: "8", r: "6" }),
149824
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 7.25v3.75M8 4.75v.5" })
149825
+ ] }),
149826
+ warning: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 2.25L14.25 13H1.75L8 2.25zM8 6.5v3M8 11.25v.25" }),
149827
+ check: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 8.5l3 3 7-7" }),
149828
+ plus: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 3v10M3 8h10" }),
149829
+ file: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
149830
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9 1.75H4.25a1 1 0 0 0-1 1v10.5a1 1 0 0 0 1 1h7.5a1 1 0 0 0 1-1V5.5L9 1.75z" }),
149831
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9 1.75V5.5h3.75" })
149832
+ ] }),
149833
+ grid: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
149834
+ /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "2.5", y: "2.5", width: "11", height: "11", rx: "1.5" }),
149835
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M2.5 6.5h11M2.5 10h11M6.5 6.5v7" })
149836
+ ] }),
149837
+ chart: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M2.5 13.5h11M4.5 11V8M8 11V4M11.5 11V6.5" }),
149838
+ more: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
149839
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "3.5", cy: "8", r: "0.75" }),
149840
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "8", cy: "8", r: "0.75" }),
149841
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12.5", cy: "8", r: "0.75" })
149842
+ ] })
149843
+ };
149844
+ function Icon({ name, size = 14, className }) {
149845
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
149846
+ "svg",
149847
+ {
149848
+ className: className ? `sp-icon ${className}` : "sp-icon",
149849
+ width: size,
149850
+ height: size,
149851
+ viewBox: "0 0 16 16",
149852
+ fill: "none",
149853
+ stroke: "currentColor",
149854
+ strokeWidth: 1.4,
149855
+ strokeLinecap: "round",
149856
+ strokeLinejoin: "round",
149857
+ "aria-hidden": "true",
149858
+ focusable: "false",
149859
+ children: PATHS[name]
149860
+ }
149861
+ );
149862
+ }
149863
+ const VARIANT_CLASS = {
149864
+ primary: "sp-btn sp-btn-primary",
149865
+ secondary: "sp-btn",
149866
+ ghost: "sp-btn sp-btn-ghost",
149867
+ danger: "sp-btn sp-btn-danger"
149868
+ };
149869
+ const Button = reactExports.forwardRef(function Button2({ variant = "secondary", icon, children, className, type = "button", ...rest }, ref) {
149870
+ const classes = className ? `${VARIANT_CLASS[variant]} ${className}` : VARIANT_CLASS[variant];
149871
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { ref, type, className: classes, ...rest, children: [
149872
+ icon && /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: icon, size: 14 }),
149873
+ children
149874
+ ] });
149875
+ });
149876
+ const IconButton = reactExports.forwardRef(function IconButton2({ icon, label, active, className, type = "button", title, ...rest }, ref) {
149877
+ const classes = ["sp-icon-btn", active ? "active" : "", className ?? ""].filter(Boolean).join(" ");
149878
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
149879
+ "button",
149880
+ {
149881
+ ref,
149882
+ type,
149883
+ className: classes,
149884
+ "aria-label": label,
149885
+ title: title ?? label,
149886
+ ...rest,
149887
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: icon, size: 14 })
149888
+ }
149889
+ );
149890
+ });
149891
+ const TONE_ICON = {
149892
+ error: "alert",
149893
+ warning: "warning",
149894
+ info: "info",
149895
+ neutral: "info"
149896
+ };
149897
+ function Notice({
149898
+ tone,
149899
+ title,
149900
+ children,
149901
+ actions: actions2,
149902
+ onDismiss,
149903
+ dismissLabel = "Dismiss",
149904
+ role = tone === "error" ? "alert" : "status"
149905
+ }) {
149906
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `sp-notice sp-notice-${tone}`, role, children: [
149907
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-notice-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: TONE_ICON[tone], size: 14 }) }),
149908
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-notice-body", children: [
149909
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-notice-title", children: title }),
149910
+ children && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-notice-detail", children })
149911
+ ] }),
149912
+ actions2 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-notice-actions", children: actions2 }),
149913
+ onDismiss && /* @__PURE__ */ jsxRuntimeExports.jsx(
149914
+ "button",
149915
+ {
149916
+ type: "button",
149917
+ className: "sp-icon-btn sp-notice-dismiss",
149918
+ onClick: onDismiss,
149919
+ "aria-label": dismissLabel,
149920
+ title: dismissLabel,
149921
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: "close", size: 12 })
149922
+ }
149923
+ )
149924
+ ] });
149925
+ }
149926
+ function EmptyState({
149927
+ icon,
149928
+ tone = "neutral",
149929
+ title,
149930
+ children,
149931
+ actions: actions2,
149932
+ role,
149933
+ live
149934
+ }) {
149935
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
149936
+ "div",
149937
+ {
149938
+ className: `sp-empty-state sp-empty-state-${tone}`,
149939
+ role,
149940
+ "aria-live": live ? "polite" : void 0,
149941
+ children: [
149942
+ icon && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-empty-state-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: icon, size: 18 }) }),
149943
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-empty-state-title", children: title }),
149944
+ children && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-empty-state-detail", children }),
149945
+ actions2 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-empty-state-actions", children: actions2 })
149946
+ ]
149947
+ }
149948
+ );
149949
+ }
149950
+ function ProgressBar({ label = "Working" }) {
149951
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-progress", role: "progressbar", "aria-label": label, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-progress-bar" }) });
149952
+ }
149953
+ function BusyDot({ label }) {
149954
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-busy-dot", role: label ? "img" : void 0, "aria-label": label, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", {}) });
149955
+ }
149956
+ function PageHeader({ title, backLabel, onBack, meta, actions: actions2 }) {
149957
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("header", { className: "sp-page-header", children: [
149958
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: "sp-page-back", onClick: onBack, children: [
149959
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: "arrow-left", size: 14 }),
149960
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: backLabel })
149961
+ ] }),
149962
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-page-header-sep", "aria-hidden": "true", children: "/" }),
149963
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "sp-page-title", children: title }),
149964
+ meta && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-page-meta", children: meta }),
149965
+ actions2 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-page-actions", children: actions2 })
149966
+ ] });
149967
+ }
149968
+ function ToggleChip({
149969
+ checked,
149970
+ onChange,
149971
+ icon,
149972
+ children,
149973
+ title,
149974
+ ariaLabel
149975
+ }) {
149976
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
149977
+ "button",
149978
+ {
149979
+ type: "button",
149980
+ role: "switch",
149981
+ "aria-checked": checked,
149982
+ "aria-label": ariaLabel,
149983
+ title,
149984
+ className: `sp-chip${checked ? " on" : ""}`,
149985
+ onClick: () => onChange(!checked),
149986
+ children: [
149987
+ icon && /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: icon, size: 12 }),
149988
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-chip-label", children })
149989
+ ]
149990
+ }
149991
+ );
149992
+ }
149775
149993
  const OPTIONAL_DRIVERS = [
149776
149994
  {
149777
149995
  connectorType: "snowflake",
@@ -149971,7 +150189,7 @@ function schemaForConnectionMode(schema, connectorId, mode) {
149971
150189
  }
149972
150190
  };
149973
150191
  }
149974
- const ConnectionsManager = ({ initialAdd = false }) => {
150192
+ const ConnectionsManager = ({ initialAdd = false, onSubpageChange }) => {
149975
150193
  const { callServerTool, isReady, mode, sendMessage, onMessage } = useHostContext();
149976
150194
  const supportsActiveConnection = mode === "vscode";
149977
150195
  const trackUi = useTrackUi();
@@ -149999,6 +150217,7 @@ const ConnectionsManager = ({ initialAdd = false }) => {
149999
150217
  const [isPopoverOpen, setIsPopoverOpen] = reactExports.useState(false);
150000
150218
  const switcherWrapRef = reactExports.useRef(null);
150001
150219
  const isFormVisible = isFormOpen || !!selectedConnectionId;
150220
+ reactExports.useEffect(() => onSubpageChange == null ? void 0 : onSubpageChange(isFormVisible), [isFormVisible, onSubpageChange]);
150002
150221
  reactExports.useEffect(() => {
150003
150222
  if (isFormVisible) {
150004
150223
  trackUi("ui_state_shown", { state: "connection_form" });
@@ -150040,7 +150259,6 @@ const ConnectionsManager = ({ initialAdd = false }) => {
150040
150259
  }
150041
150260
  setConnectionsUnreachable(false);
150042
150261
  } catch (e2) {
150043
- console.error("Failed to fetch data:", e2);
150044
150262
  setConnectionsUnreachable(true);
150045
150263
  } finally {
150046
150264
  setIsLoading(false);
@@ -150050,7 +150268,16 @@ const ConnectionsManager = ({ initialAdd = false }) => {
150050
150268
  fetchData();
150051
150269
  }, [fetchData]);
150052
150270
  reactExports.useEffect(() => {
150053
- if (!onMessage) return;
150271
+ if (!connectionsUnreachable) {
150272
+ return;
150273
+ }
150274
+ const timer = setInterval(() => void fetchData(), 5e3);
150275
+ return () => clearInterval(timer);
150276
+ }, [connectionsUnreachable, fetchData]);
150277
+ reactExports.useEffect(() => {
150278
+ if (!onMessage) {
150279
+ return;
150280
+ }
150054
150281
  return onMessage((msg) => {
150055
150282
  if ((msg == null ? void 0 : msg.type) === "connectionsImported" || (msg == null ? void 0 : msg.type) === "updateConfig") {
150056
150283
  void fetchData();
@@ -150223,7 +150450,9 @@ const ConnectionsManager = ({ initialAdd = false }) => {
150223
150450
  }
150224
150451
  };
150225
150452
  const handleDeleteConnection = (id, name) => {
150226
- if (!isReady) return;
150453
+ if (!isReady) {
150454
+ return;
150455
+ }
150227
150456
  setPendingConfirm({
150228
150457
  title: "Delete connection",
150229
150458
  message: `Delete connection '${name}'? This can't be undone.`,
@@ -150334,6 +150563,23 @@ const ConnectionsManager = ({ initialAdd = false }) => {
150334
150563
  setSelectedType("");
150335
150564
  setToast(null);
150336
150565
  }, []);
150566
+ reactExports.useEffect(() => {
150567
+ if (!isFormVisible) {
150568
+ return;
150569
+ }
150570
+ const onKeyDown = (event) => {
150571
+ if (event.key !== "Escape" || pendingConfirm) {
150572
+ return;
150573
+ }
150574
+ const target = event.target;
150575
+ if (target instanceof Element && target.closest("input, select, textarea")) {
150576
+ return;
150577
+ }
150578
+ closeForm();
150579
+ };
150580
+ document.addEventListener("keydown", onKeyDown);
150581
+ return () => document.removeEventListener("keydown", onKeyDown);
150582
+ }, [isFormVisible, pendingConfirm, closeForm]);
150337
150583
  reactExports.useEffect(() => {
150338
150584
  function handleClickOutside(event) {
150339
150585
  if (isPopoverOpen && switcherWrapRef.current && !switcherWrapRef.current.contains(event.target)) {
@@ -150352,7 +150598,9 @@ const ConnectionsManager = ({ initialAdd = false }) => {
150352
150598
  );
150353
150599
  const emptyFormInitialData = reactExports.useMemo(() => ({}), []);
150354
150600
  const renderToast = () => {
150355
- if (!toast) return null;
150601
+ if (!toast) {
150602
+ return null;
150603
+ }
150356
150604
  const advice = toast.advice;
150357
150605
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
150358
150606
  "div",
@@ -150456,71 +150704,62 @@ const ConnectionsManager = ({ initialAdd = false }) => {
150456
150704
  }
150457
150705
  return null;
150458
150706
  };
150459
- const renderConnectionForm = () => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-detail", children: [
150460
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-detail-header", children: [
150461
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
150462
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-detail-kicker", children: "Add connection" }),
150463
- /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "sp-conn-detail-title", children: "New connection" })
150464
- ] }),
150465
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "sp-btn", onClick: closeForm, "aria-label": "Cancel", children: "Cancel" })
150466
- ] }),
150467
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-detail-body", children: [
150468
- renderToast(),
150469
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-form-section", children: "Database Type" }),
150470
- ["local", "server"].map((group) => {
150471
- const groupConnectors = connectors.filter((c) => getConnectorGroup(c.id) === group);
150472
- if (groupConnectors.length === 0) return null;
150473
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(React$2.Fragment, { children: [
150474
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-type-group-label", id: `sp-conn-type-group-${group}`, children: CONNECTOR_GROUP_LABELS[group] }),
150475
- /* @__PURE__ */ jsxRuntimeExports.jsx(
150476
- "div",
150477
- {
150478
- className: "sp-conn-type-grid",
150479
- role: "group",
150480
- "aria-labelledby": `sp-conn-type-group-${group}`,
150481
- children: groupConnectors.map((c) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
150482
- "button",
150483
- {
150484
- type: "button",
150485
- className: `sp-conn-type-card${selectedType === c.id ? " selected" : ""}`,
150486
- onClick: () => {
150487
- setSelectedType(c.id);
150488
- trackUi("first_value_step", {
150489
- step: "connector_selected",
150490
- outcome: "success"
150491
- });
150492
- },
150493
- "aria-pressed": selectedType === c.id,
150494
- children: [
150495
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-type-card-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ConnectorGlyph, { type: c.id }) }),
150496
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-type-card-name", children: c.displayName || c.id })
150497
- ]
150707
+ const renderConnectionForm = () => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-detail", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-detail-body", children: [
150708
+ renderToast(),
150709
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-form-section", children: "Database Type" }),
150710
+ ["local", "server"].map((group) => {
150711
+ const groupConnectors = connectors.filter((c) => getConnectorGroup(c.id) === group);
150712
+ if (groupConnectors.length === 0) {
150713
+ return null;
150714
+ }
150715
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(React$2.Fragment, { children: [
150716
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-type-group-label", id: `sp-conn-type-group-${group}`, children: CONNECTOR_GROUP_LABELS[group] }),
150717
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
150718
+ "div",
150719
+ {
150720
+ className: "sp-conn-type-grid",
150721
+ role: "group",
150722
+ "aria-labelledby": `sp-conn-type-group-${group}`,
150723
+ children: groupConnectors.map((c) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
150724
+ "button",
150725
+ {
150726
+ type: "button",
150727
+ className: `sp-conn-type-card${selectedType === c.id ? " selected" : ""}`,
150728
+ onClick: () => {
150729
+ setSelectedType(c.id);
150730
+ trackUi("first_value_step", {
150731
+ step: "connector_selected",
150732
+ outcome: "success"
150733
+ });
150498
150734
  },
150499
- c.id
150500
- ))
150501
- }
150502
- )
150503
- ] }, group);
150504
- }),
150505
- activeSchemaDef && activeFormSchema ? /* @__PURE__ */ jsxRuntimeExports.jsx(
150506
- DynamicForm,
150507
- {
150508
- schema: { ...activeFormSchema, type: "object" },
150509
- initialData: emptyFormInitialData,
150510
- onSubmit: (formData) => handleSaveConnection(formData),
150511
- onTest: (formData) => handleTestFormData(formData),
150512
- onCancel: closeForm,
150513
- isSubmitting: isLoading
150514
- }
150515
- ) : selectedType ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-form-hint", style: { color: "var(--sp-error)" }, children: "This connector does not define a configuration schema." }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-empty sp-conn-empty-compact", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-empty-text", children: "Choose a database type to begin." }) })
150516
- ] })
150517
- ] });
150735
+ "aria-pressed": selectedType === c.id,
150736
+ children: [
150737
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-type-card-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ConnectorGlyph, { type: c.id }) }),
150738
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-type-card-name", children: c.displayName || c.id })
150739
+ ]
150740
+ },
150741
+ c.id
150742
+ ))
150743
+ }
150744
+ )
150745
+ ] }, group);
150746
+ }),
150747
+ activeSchemaDef && activeFormSchema ? /* @__PURE__ */ jsxRuntimeExports.jsx(
150748
+ DynamicForm,
150749
+ {
150750
+ schema: { ...activeFormSchema, type: "object" },
150751
+ initialData: emptyFormInitialData,
150752
+ onSubmit: (formData) => handleSaveConnection(formData),
150753
+ onTest: (formData) => handleTestFormData(formData),
150754
+ onCancel: closeForm,
150755
+ isSubmitting: isLoading
150756
+ }
150757
+ ) : selectedType ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-form-hint sp-conn-schema-error", children: "This connector does not define a configuration schema." }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-empty sp-conn-empty-compact", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-empty-text", children: "Choose a database type to begin." }) })
150758
+ ] }) });
150518
150759
  const renderConnectionDetails = () => {
150519
150760
  if (!selectedConnection) {
150520
150761
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-detail", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-empty sp-conn-empty-compact", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-empty-text", children: "Select a connection to view details, test it, or use it for queries." }) }) });
150521
150762
  }
150522
- const status = statuses[selectedConnection.id] || "unknown";
150523
- const isActive = supportsActiveConnection && activeConnectionId === selectedConnection.id;
150524
150763
  const schemaDef = connectors.find((c) => c.id === selectedConnection.type);
150525
150764
  const canManagePassword = supportsActiveConnection && schemaHasPasswordField(schemaDef == null ? void 0 : schemaDef.schema);
150526
150765
  const editSchema = (schemaDef == null ? void 0 : schemaDef.schema) && canManagePassword ? schemaWithoutPasswordField(schemaDef.schema) : schemaDef == null ? void 0 : schemaDef.schema;
@@ -150564,78 +150803,22 @@ const ConnectionsManager = ({ initialAdd = false }) => {
150564
150803
  ] })
150565
150804
  ] });
150566
150805
  };
150567
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-detail", children: [
150568
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
150569
- "div",
150806
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-detail", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-detail-body", children: [
150807
+ renderToast(),
150808
+ modeAwareEditSchema ? /* @__PURE__ */ jsxRuntimeExports.jsx(
150809
+ DynamicForm,
150570
150810
  {
150571
- className: "sp-conn-detail-header",
150572
- style: {
150573
- display: "flex",
150574
- justifyContent: "space-between",
150575
- alignItems: "center",
150576
- flexWrap: "wrap",
150577
- gap: "16px",
150578
- paddingBottom: "16px",
150579
- borderBottom: "1px solid var(--sp-border)",
150580
- marginBottom: "16px"
150581
- },
150582
- children: [
150583
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "12px", flexWrap: "wrap" }, children: [
150584
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600, fontSize: "14px", color: "var(--sp-fg)" }, children: selectedConnection ? "Edit Connection" : "New Connection" }),
150585
- selectedConnection && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
150586
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-type-badge", children: selectedConnection.type }),
150587
- isActive && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-active-badge", children: "Active" }),
150588
- selectedConnection.builtIn && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-built-in-label", children: "Built-in" })
150589
- ] })
150590
- ] }),
150591
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
150592
- "div",
150593
- {
150594
- className: "sp-conn-detail-actions",
150595
- style: { display: "flex", gap: "8px", flexWrap: "wrap" },
150596
- children: [
150597
- supportsActiveConnection && /* @__PURE__ */ jsxRuntimeExports.jsx(
150598
- "button",
150599
- {
150600
- className: `sp-btn${isActive ? " sp-btn-primary" : ""}`,
150601
- onClick: () => handleSetActiveConnection(selectedConnection),
150602
- disabled: isActive || status === "testing",
150603
- "aria-label": `Set ${selectedConnection.name} as the active connection for queries`,
150604
- children: isActive ? "Active" : "Set active"
150605
- }
150606
- ),
150607
- !selectedConnection.builtIn && /* @__PURE__ */ jsxRuntimeExports.jsx(
150608
- "button",
150609
- {
150610
- className: "sp-btn sp-btn-ghost-danger",
150611
- onClick: () => handleDeleteConnection(selectedConnection.id, selectedConnection.name),
150612
- "aria-label": `Delete connection ${selectedConnection.name}`,
150613
- children: "Delete"
150614
- }
150615
- )
150616
- ]
150617
- }
150618
- )
150619
- ]
150620
- }
150621
- ),
150622
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-detail-body", children: [
150623
- renderToast(),
150624
- modeAwareEditSchema ? /* @__PURE__ */ jsxRuntimeExports.jsx(
150625
- DynamicForm,
150626
- {
150627
- schema: { ...modeAwareEditSchema, type: "object" },
150628
- initialData: selectedFormInitialData,
150629
- onSubmit: (formData) => handleSaveConnection(formData, selectedConnection),
150630
- onTest: (formData) => handleTestFormData(formData, selectedConnection),
150631
- isSubmitting: isLoading,
150632
- renderAfterField: renderPasswordManagementField,
150633
- actionStatus: renderConnectionStatusMessage(selectedConnection),
150634
- submitFeedback: lastSavedConnectionId === selectedConnection.id ? "Saved successfully" : void 0
150635
- }
150636
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-form-hint", style: { color: "var(--sp-error)" }, children: "This connector does not define a configuration schema." })
150637
- ] })
150638
- ] });
150811
+ schema: { ...modeAwareEditSchema, type: "object" },
150812
+ initialData: selectedFormInitialData,
150813
+ onSubmit: (formData) => handleSaveConnection(formData, selectedConnection),
150814
+ onTest: (formData) => handleTestFormData(formData, selectedConnection),
150815
+ isSubmitting: isLoading,
150816
+ renderAfterField: renderPasswordManagementField,
150817
+ actionStatus: renderConnectionStatusMessage(selectedConnection),
150818
+ submitFeedback: lastSavedConnectionId === selectedConnection.id ? "Saved successfully" : void 0
150819
+ }
150820
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-form-hint sp-conn-schema-error", children: "This connector does not define a configuration schema." })
150821
+ ] }) });
150639
150822
  };
150640
150823
  const renderGrid = () => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-grid-view", children: [
150641
150824
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-head", style: { marginBottom: 16 }, children: [
@@ -150701,7 +150884,11 @@ const ConnectionsManager = ({ initialAdd = false }) => {
150701
150884
  gap: "12px"
150702
150885
  },
150703
150886
  children: connections.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-empty", style: { gridColumn: "1 / -1" }, children: [
150704
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: connectionsUnreachable ? "Can't reach the Grain engine right now. Your connections are saved on disk and will reappear once it reconnects." : "No connections configured." }),
150887
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: connectionsUnreachable ? "Can't reach Grain. Start Grain and try again." : "No connections configured." }),
150888
+ connectionsUnreachable && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginTop: 12, display: "flex", gap: 8 }, children: [
150889
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "sp-btn", onClick: fetchData, children: "Retry" }),
150890
+ mode === "vscode" && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "sp-btn", onClick: () => sendMessage == null ? void 0 : sendMessage("startGrain"), children: "Start Grain" })
150891
+ ] }),
150705
150892
  mode === "vscode" && !connectionsUnreachable && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
150706
150893
  "button",
150707
150894
  {
@@ -150791,30 +150978,45 @@ const ConnectionsManager = ({ initialAdd = false }) => {
150791
150978
  }
150792
150979
  )
150793
150980
  ] });
150794
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-page", children: [
150795
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn", children: !isFormOpen && !selectedConnectionId ? renderGrid() : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
150981
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: isFormVisible ? "sp-conn-subpage" : "sp-conn-page", children: [
150982
+ isFormVisible ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
150796
150983
  /* @__PURE__ */ jsxRuntimeExports.jsx(
150797
- "button",
150984
+ PageHeader,
150798
150985
  {
150799
- className: "sp-btn",
150800
- onClick: closeForm,
150801
- style: { marginBottom: 12, alignSelf: "flex-start" },
150802
- children: "← Back to connections"
150986
+ backLabel: "Connections",
150987
+ onBack: closeForm,
150988
+ title: isFormOpen ? "New connection" : (selectedConnection == null ? void 0 : selectedConnection.name) ?? "Connection",
150989
+ meta: selectedConnection && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
150990
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-type-badge", children: selectedConnection.type }),
150991
+ supportsActiveConnection && activeConnectionId === selectedConnection.id && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-active-badge", children: "Active" }),
150992
+ selectedConnection.builtIn && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-built-in-label", children: "Built-in" })
150993
+ ] }),
150994
+ actions: isFormOpen ? /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "ghost", onClick: closeForm, "aria-label": "Cancel", children: "Cancel" }) : selectedConnection && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
150995
+ supportsActiveConnection && /* @__PURE__ */ jsxRuntimeExports.jsx(
150996
+ Button,
150997
+ {
150998
+ variant: "ghost",
150999
+ onClick: () => handleSetActiveConnection(selectedConnection),
151000
+ disabled: activeConnectionId === selectedConnection.id || statuses[selectedConnection.id] === "testing",
151001
+ "aria-label": `Set ${selectedConnection.name} as the active connection for queries`,
151002
+ children: activeConnectionId === selectedConnection.id ? "Active" : "Set active"
151003
+ }
151004
+ ),
151005
+ !selectedConnection.builtIn && /* @__PURE__ */ jsxRuntimeExports.jsx(
151006
+ Button,
151007
+ {
151008
+ variant: "secondary",
151009
+ className: "sp-btn-ghost-danger",
151010
+ onClick: () => handleDeleteConnection(selectedConnection.id, selectedConnection.name),
151011
+ "aria-label": `Delete connection ${selectedConnection.name}`,
151012
+ children: "Delete"
151013
+ }
151014
+ )
151015
+ ] })
150803
151016
  }
150804
151017
  ),
150805
- /* @__PURE__ */ jsxRuntimeExports.jsx(
150806
- "section",
150807
- {
150808
- "aria-label": "Connection details",
150809
- style: {
150810
- background: "var(--sp-bg)",
150811
- border: "1px solid var(--sp-border)",
150812
- borderRadius: 6
150813
- },
150814
- children: isFormOpen ? renderConnectionForm() : renderConnectionDetails()
150815
- }
150816
- )
150817
- ] }) }),
151018
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-subpage-body", children: /* @__PURE__ */ jsxRuntimeExports.jsx("section", { "aria-label": "Connection details", className: "sp-conn-subpage-column", children: isFormOpen ? renderConnectionForm() : renderConnectionDetails() }) })
151019
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn", children: renderGrid() }),
150818
151020
  /* @__PURE__ */ jsxRuntimeExports.jsx(
150819
151021
  ConfirmDialog,
150820
151022
  {
@@ -150831,6 +151033,33 @@ const ConnectionsManager = ({ initialAdd = false }) => {
150831
151033
  )
150832
151034
  ] });
150833
151035
  };
151036
+ const OPTIONS = [
151037
+ ["inherit", "Inherit"],
151038
+ ["grain-light", "Grain Light"],
151039
+ ["grain-dark", "Grain Dark"]
151040
+ ];
151041
+ function PanelThemeSetting({
151042
+ value,
151043
+ onChange
151044
+ }) {
151045
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row", children: [
151046
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151047
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Theme" }),
151048
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Inherit follows VS Code. Grain themes use the brand palette." })
151049
+ ] }),
151050
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-segmented", role: "group", "aria-label": "Panel theme", children: OPTIONS.map(([option, label]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
151051
+ "button",
151052
+ {
151053
+ type: "button",
151054
+ className: `sp-segmented-item${value === option ? " active" : ""}`,
151055
+ "aria-pressed": value === option,
151056
+ onClick: () => onChange(option),
151057
+ children: label
151058
+ },
151059
+ option
151060
+ )) })
151061
+ ] });
151062
+ }
150834
151063
  const FONT_SIZE_MIN = 8;
150835
151064
  const FONT_SIZE_MAX = 20;
150836
151065
  function formatFontSize(fontSize) {
@@ -150890,6 +151119,68 @@ function mcpHttpClientConfig(mcpServerUrl, mcpAuthToken) {
150890
151119
  }
150891
151120
  }`;
150892
151121
  }
151122
+ const GROUPS = [
151123
+ {
151124
+ key: "query",
151125
+ label: "Query execution",
151126
+ description: "Run SQL, inspect result tabs, and manage long-running work."
151127
+ },
151128
+ {
151129
+ key: "connections",
151130
+ label: "Connections",
151131
+ description: "Discover connector types and manage saved connection profiles."
151132
+ },
151133
+ {
151134
+ key: "catalog",
151135
+ label: "Catalog",
151136
+ description: "Browse schemas, tables, views, and column metadata."
151137
+ },
151138
+ {
151139
+ key: "charts",
151140
+ label: "Charts",
151141
+ description: "Render result charts and save them as workspace documents."
151142
+ },
151143
+ {
151144
+ key: "ledger",
151145
+ label: "Execution ledger",
151146
+ description: "Check the local record of executed queries."
151147
+ },
151148
+ {
151149
+ key: "sessions",
151150
+ label: "Sessions",
151151
+ description: "Inspect active assistant sessions and their open result tabs."
151152
+ }
151153
+ ];
151154
+ const TOOL_ENTRIES = {
151155
+ run_query: ["query", "Execute SQL and open the result in a tab."],
151156
+ get_tab_info: ["query", "Read tab status, columns, rows, and errors."],
151157
+ cancel_query: ["query", "Stop a query that is still running."],
151158
+ close_tab: ["query", "Close a tab from an MCP session."],
151159
+ list_connectors: ["connections", "Show available connector types and schemas."],
151160
+ list_connections: ["connections", "Show saved connection profiles."],
151161
+ save_connection: ["connections", "Create or update a connection profile."],
151162
+ request_connection_credentials: ["connections", "Ask the user to enter a connection password."],
151163
+ test_connection: ["connections", "Verify a saved or draft profile."],
151164
+ delete_connection: ["connections", "Remove a saved profile."],
151165
+ list_schemas: ["catalog", "List schemas for a stored connection."],
151166
+ list_tables: ["catalog", "List tables and views in a schema."],
151167
+ describe_table: ["catalog", "Show column details for a table."],
151168
+ render_chart: ["charts", "Render a chart from a result tab."],
151169
+ save_chart: ["charts", "Save a chart document into the workspace."],
151170
+ list_charts: ["charts", "List chart documents saved in the workspace."],
151171
+ ledger_stats: ["ledger", "Show ledger health and counts."],
151172
+ verify_ledger: ["ledger", "Verify the ledger hash chain."],
151173
+ list_sessions: ["sessions", "Show active MCP sessions and their tabs."],
151174
+ record_ui_event: null
151175
+ };
151176
+ const MCP_TOOL_GROUPS = GROUPS.map((group) => ({
151177
+ label: group.label,
151178
+ description: group.description,
151179
+ tools: ALL_TOOLS.flatMap((name) => {
151180
+ const entry = TOOL_ENTRIES[name];
151181
+ return entry && entry[0] === group.key ? [[name, entry[1]]] : [];
151182
+ })
151183
+ }));
150893
151184
  const DUMMY_PREVIEW_DATA = {
150894
151185
  query: "SELECT * FROM preview_data",
150895
151186
  columns: [
@@ -150961,43 +151252,6 @@ const MCP_LOGO = /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 1
150961
151252
  }
150962
151253
  )
150963
151254
  ] });
150964
- const MCP_TOOL_GROUPS = [
150965
- {
150966
- label: "Query execution",
150967
- description: "Run SQL, inspect result tabs, and manage long-running work.",
150968
- tools: [
150969
- ["run_query", "Execute SQL and open the result in a tab."],
150970
- ["get_tab_info", "Read tab status, columns, rows, and errors."],
150971
- ["cancel_query", "Stop a query that is still running."],
150972
- ["close_tab", "Close a tab from an MCP session."]
150973
- ]
150974
- },
150975
- {
150976
- label: "Connections",
150977
- description: "Discover connector types and manage saved connection profiles.",
150978
- tools: [
150979
- ["list_connectors", "Show available connector types and schemas."],
150980
- ["list_connections", "Show saved connection profiles."],
150981
- ["save_connection", "Create or update a connection profile."],
150982
- ["test_connection", "Verify a saved or draft profile."],
150983
- ["delete_connection", "Remove a saved profile."]
150984
- ]
150985
- },
150986
- {
150987
- label: "Catalog",
150988
- description: "Browse schemas, tables, views, and column metadata.",
150989
- tools: [
150990
- ["list_schemas", "List schemas for a stored connection."],
150991
- ["list_tables", "List tables and views in a schema."],
150992
- ["describe_table", "Show column details for a table."]
150993
- ]
150994
- },
150995
- {
150996
- label: "Sessions",
150997
- description: "Inspect active assistant sessions and their open result tabs.",
150998
- tools: [["list_sessions", "Show active MCP sessions and their tabs."]]
150999
- }
151000
- ];
151001
151255
  const PREVIEW_MAX_WIDTH = (() => {
151002
151256
  const gutterWidth = 42;
151003
151257
  const colWidths = DUMMY_PREVIEW_DATA.columns.reduce((sum2, col) => {
@@ -151031,6 +151285,7 @@ function SettingsPanel({ onSave, onClose, onShowWelcome, initialTab = "general"
151031
151285
  }, [trackUi]);
151032
151286
  const {
151033
151287
  theme: theme2,
151288
+ panelTheme,
151034
151289
  copyIncludesHeaders,
151035
151290
  setCopyIncludesHeaders,
151036
151291
  fontSize,
@@ -151064,6 +151319,9 @@ function SettingsPanel({ onSave, onClose, onShowWelcome, initialTab = "general"
151064
151319
  const showMcpTab = isStandalone || mcpEntitled;
151065
151320
  const actualInitialTab = !showMcpTab && initialTab === "mcp" ? "general" : initialTab;
151066
151321
  const [activeTab, setActiveTab] = reactExports.useState(actualInitialTab);
151322
+ const [connectionsSubpage, setConnectionsSubpage] = reactExports.useState(
151323
+ connectionsInitialAdd && actualInitialTab === "connections"
151324
+ );
151067
151325
  const [fontSizeError, setFontSizeError] = reactExports.useState(null);
151068
151326
  const [testMcpStatus, setTestMcpStatus] = reactExports.useState(null);
151069
151327
  reactExports.useEffect(() => {
@@ -151205,7 +151463,7 @@ function SettingsPanel({ onSave, onClose, onShowWelcome, initialTab = "general"
151205
151463
  }, 1e4);
151206
151464
  };
151207
151465
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-page", children: [
151208
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-page-header", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-header-row", children: [
151466
+ !(connectionsSubpage && activeTab === "connections") && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-page-header", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-header-row", children: [
151209
151467
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-title-group", children: [
151210
151468
  /* @__PURE__ */ jsxRuntimeExports.jsx(
151211
151469
  "button",
@@ -151226,7 +151484,7 @@ function SettingsPanel({ onSave, onClose, onShowWelcome, initialTab = "general"
151226
151484
  ),
151227
151485
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-settings-page-title", children: "Settings" })
151228
151486
  ] }),
151229
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
151487
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
151230
151488
  "div",
151231
151489
  {
151232
151490
  className: "sp-tab-bar-tabs sp-settings-tabs",
@@ -151234,455 +151492,445 @@ function SettingsPanel({ onSave, onClose, onShowWelcome, initialTab = "general"
151234
151492
  "aria-label": "Settings sections",
151235
151493
  onKeyDown: handleTabListKeyDown,
151236
151494
  children: [
151237
- /* @__PURE__ */ jsxRuntimeExports.jsx(
151238
- "button",
151239
- {
151240
- type: "button",
151241
- role: "tab",
151242
- id: "sp-settings-tab-general",
151243
- "aria-controls": "sp-settings-panel-general",
151244
- "aria-selected": activeTab === "general",
151245
- tabIndex: activeTab === "general" ? 0 : -1,
151246
- className: `sp-tab${activeTab === "general" ? " active" : ""}`,
151247
- onClick: () => setActiveTab("general"),
151248
- children: "General"
151249
- }
151250
- ),
151251
- /* @__PURE__ */ jsxRuntimeExports.jsx(
151252
- "button",
151253
- {
151254
- type: "button",
151255
- role: "tab",
151256
- id: "sp-settings-tab-connections",
151257
- "aria-controls": "sp-settings-panel-connections",
151258
- "aria-selected": activeTab === "connections",
151259
- tabIndex: activeTab === "connections" ? 0 : -1,
151260
- className: `sp-tab${activeTab === "connections" ? " active" : ""}`,
151261
- onClick: () => setActiveTab("connections"),
151262
- children: "Connections"
151263
- }
151264
- ),
151265
- showMcpTab && /* @__PURE__ */ jsxRuntimeExports.jsx(
151266
- "button",
151267
- {
151268
- type: "button",
151269
- role: "tab",
151270
- id: "sp-settings-tab-mcp",
151271
- "aria-controls": "sp-settings-panel-mcp",
151272
- "aria-selected": activeTab === "mcp",
151273
- tabIndex: activeTab === "mcp" ? 0 : -1,
151274
- className: `sp-tab${activeTab === "mcp" ? " active" : ""}`,
151275
- onClick: () => setActiveTab("mcp"),
151276
- children: "MCP Server"
151277
- }
151278
- )
151279
- ]
151495
+ ["general", "General"],
151496
+ ["connections", "Connections"],
151497
+ ...showMcpTab ? [["mcp", "MCP Server"]] : []
151498
+ ].map(([tab, label]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
151499
+ "button",
151500
+ {
151501
+ type: "button",
151502
+ role: "tab",
151503
+ id: `sp-settings-tab-${tab}`,
151504
+ "aria-controls": `sp-settings-panel-${tab}`,
151505
+ "aria-selected": activeTab === tab,
151506
+ tabIndex: activeTab === tab ? 0 : -1,
151507
+ className: `sp-tab${activeTab === tab ? " active" : ""}`,
151508
+ onClick: () => setActiveTab(tab),
151509
+ children: label
151510
+ },
151511
+ tab
151512
+ ))
151280
151513
  }
151281
151514
  )
151282
151515
  ] }) }),
151283
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-page-body", children: [
151284
- activeTab === "general" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
151285
- "div",
151286
- {
151287
- className: "sp-settings-layout",
151288
- role: "tabpanel",
151289
- id: "sp-settings-panel-general",
151290
- "aria-labelledby": "sp-settings-tab-general",
151291
- children: [
151292
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-main", children: [
151293
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-card", children: [
151294
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "Results" }),
151295
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "sp-settings-row", children: [
151296
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Copy includes headers" }),
151297
- /* @__PURE__ */ jsxRuntimeExports.jsx(
151298
- "input",
151299
- {
151300
- type: "checkbox",
151301
- className: "sp-settings-check",
151302
- checked: copyIncludesHeaders,
151303
- onChange: (e2) => setCopyIncludesHeaders(e2.target.checked)
151304
- }
151305
- )
151306
- ] }),
151307
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-divider" }),
151308
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "sp-settings-row", children: [
151309
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151310
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Max rows to display" }),
151311
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Rows fetched per query. Increase for larger exports." })
151312
- ] }),
151313
- /* @__PURE__ */ jsxRuntimeExports.jsx(
151314
- "input",
151315
- {
151316
- type: "number",
151317
- className: "sp-settings-input",
151318
- value: localMaxRows,
151319
- min: 1,
151320
- max: 1e5,
151321
- placeholder: "e.g. 1000",
151322
- onChange: (e2) => handleMaxRowsChange(e2.target.value),
151323
- onBlur: handleMaxRowsDone,
151324
- onKeyDown: (e2) => {
151325
- if (e2.key === "Enter") handleMaxRowsDone();
151326
- }
151327
- }
151328
- )
151329
- ] })
151330
- ] }),
151331
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-card", children: [
151332
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "Display" }),
151333
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row", children: [
151334
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151335
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Row density" }),
151336
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Height of grid rows." })
151337
- ] }),
151338
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-density-group", role: "group", "aria-label": "Row density", children: ["compact", "normal", "comfortable"].map((d) => /* @__PURE__ */ jsxRuntimeExports.jsx(
151339
- "button",
151340
- {
151341
- className: `sp-icon-btn${rowDensity === d ? " active" : ""}`,
151342
- onClick: () => {
151343
- setRowDensity(d);
151344
- onSave({ rowHeight: d });
151345
- },
151346
- title: getDensityLabel(d),
151347
- "aria-label": `${getDensityLabel(d)} row density`,
151348
- "aria-pressed": rowDensity === d,
151349
- children: DENSITY_ICONS[d]
151350
- },
151351
- d
151352
- )) })
151353
- ] }),
151354
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "sp-settings-row", children: [
151355
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151356
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Font size" }),
151357
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Results grid font size. Leave blank to inherit." })
151516
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
151517
+ "div",
151518
+ {
151519
+ className: `sp-settings-page-body${connectionsSubpage && activeTab === "connections" ? " sp-settings-page-body-subpage" : ""}`,
151520
+ children: [
151521
+ activeTab === "general" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
151522
+ "div",
151523
+ {
151524
+ className: "sp-settings-layout",
151525
+ role: "tabpanel",
151526
+ id: "sp-settings-panel-general",
151527
+ "aria-labelledby": "sp-settings-tab-general",
151528
+ children: [
151529
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-main", children: [
151530
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-card", children: [
151531
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "Results" }),
151532
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "sp-settings-row", children: [
151533
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Copy includes headers" }),
151534
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
151535
+ "input",
151536
+ {
151537
+ type: "checkbox",
151538
+ className: "sp-settings-check",
151539
+ checked: copyIncludesHeaders,
151540
+ onChange: (e2) => setCopyIncludesHeaders(e2.target.checked)
151541
+ }
151542
+ )
151543
+ ] }),
151544
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-divider" }),
151545
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "sp-settings-row", children: [
151546
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151547
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Max rows to display" }),
151548
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Rows fetched per query. Increase for larger exports." })
151549
+ ] }),
151550
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
151551
+ "input",
151552
+ {
151553
+ type: "number",
151554
+ className: "sp-settings-input",
151555
+ value: localMaxRows,
151556
+ min: 1,
151557
+ max: 1e5,
151558
+ placeholder: "e.g. 1000",
151559
+ onChange: (e2) => handleMaxRowsChange(e2.target.value),
151560
+ onBlur: handleMaxRowsDone,
151561
+ onKeyDown: (e2) => {
151562
+ if (e2.key === "Enter") handleMaxRowsDone();
151563
+ }
151564
+ }
151565
+ )
151566
+ ] })
151358
151567
  ] }),
151359
- /* @__PURE__ */ jsxRuntimeExports.jsx(
151360
- "input",
151361
- {
151362
- type: "number",
151363
- className: "sp-settings-input",
151364
- value: localFontSize,
151365
- min: FONT_SIZE_MIN,
151366
- max: FONT_SIZE_MAX,
151367
- placeholder: "Inherit",
151368
- onChange: (e2) => {
151369
- handleFontSizeChange(e2.target.value);
151370
- },
151371
- onBlur: handleFontSizeDone,
151372
- onKeyDown: (e2) => {
151373
- if (e2.key === "Enter") handleFontSizeDone();
151374
- }
151375
- }
151376
- )
151377
- ] }),
151378
- fontSizeError && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-validation-error", role: "alert", children: fontSizeError })
151379
- ] }),
151380
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-card", children: [
151381
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "Privacy" }),
151382
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
151383
- "label",
151384
- {
151385
- className: "sp-settings-row",
151386
- title: isStandalone ? "Telemetry is sent by the VS Code extension; this browser tab has no sender to control." : void 0,
151387
- children: [
151568
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-card", children: [
151569
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "Display" }),
151570
+ mode === "vscode" && /* @__PURE__ */ jsxRuntimeExports.jsx(PanelThemeSetting, { value: panelTheme, onChange: (t) => onSave({ panelTheme: t }) }),
151571
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row", children: [
151572
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151573
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Row density" }),
151574
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Height of grid rows." })
151575
+ ] }),
151576
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-density-group", role: "group", "aria-label": "Row density", children: ["compact", "normal", "comfortable"].map((d) => /* @__PURE__ */ jsxRuntimeExports.jsx(
151577
+ "button",
151578
+ {
151579
+ className: `sp-icon-btn${rowDensity === d ? " active" : ""}`,
151580
+ onClick: () => {
151581
+ setRowDensity(d);
151582
+ onSave({ rowHeight: d });
151583
+ },
151584
+ title: getDensityLabel(d),
151585
+ "aria-label": `${getDensityLabel(d)} row density`,
151586
+ "aria-pressed": rowDensity === d,
151587
+ children: DENSITY_ICONS[d]
151588
+ },
151589
+ d
151590
+ )) })
151591
+ ] }),
151592
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "sp-settings-row", children: [
151388
151593
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151389
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Product telemetry" }),
151390
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: isStandalone ? "Set from the VS Code extension — this browser tab doesn't send telemetry itself." : "Anonymous usage metrics only. SQL, results, credentials, names, and paths are never sent." })
151594
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Font size" }),
151595
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Results grid font size. Leave blank to inherit." })
151391
151596
  ] }),
151392
151597
  /* @__PURE__ */ jsxRuntimeExports.jsx(
151393
151598
  "input",
151394
151599
  {
151395
- type: "checkbox",
151396
- className: "sp-settings-check",
151397
- checked: localTelemetryEnabled,
151398
- disabled: isStandalone,
151600
+ type: "number",
151601
+ className: "sp-settings-input",
151602
+ value: localFontSize,
151603
+ min: FONT_SIZE_MIN,
151604
+ max: FONT_SIZE_MAX,
151605
+ placeholder: "Inherit",
151399
151606
  onChange: (e2) => {
151400
- setLocalTelemetryEnabled(e2.target.checked);
151401
- onSave({ telemetryEnabled: e2.target.checked });
151607
+ handleFontSizeChange(e2.target.value);
151608
+ },
151609
+ onBlur: handleFontSizeDone,
151610
+ onKeyDown: (e2) => {
151611
+ if (e2.key === "Enter") handleFontSizeDone();
151402
151612
  }
151403
151613
  }
151404
151614
  )
151405
- ]
151406
- }
151407
- )
151408
- ] }),
151409
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-card", children: [
151410
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "About" }),
151411
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row sp-settings-row-top", children: [
151412
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151413
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Grain version" }),
151414
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row-hint", children: [
151415
- "Installed: ",
151416
- currentVersion ? `v${currentVersion}` : "Checking…",
151417
- !isStandalone && (latestVersion ? ` · Latest: v${latestVersion}` : " · Checking for updates…")
151418
151615
  ] }),
151419
- updateAvailable && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-version-update", children: "An update is available." })
151616
+ fontSizeError && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-validation-error", role: "alert", children: fontSizeError })
151420
151617
  ] }),
151421
- isStandalone ? /* @__PURE__ */ jsxRuntimeExports.jsx(
151422
- "a",
151423
- {
151424
- className: "sp-btn",
151425
- href: "https://github.com/pattrnlabs/grain/releases",
151426
- target: "_blank",
151427
- rel: "noreferrer",
151428
- title: "This daemon can run standalone or inside VS Code; check the releases page for the latest version.",
151429
- children: "View Releases"
151430
- }
151431
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
151432
- "button",
151433
- {
151434
- type: "button",
151435
- className: "sp-btn",
151436
- onClick: () => sendMessage("openExtensionPage", {}),
151437
- children: updateAvailable ? "Open Update" : "Open Extension"
151438
- }
151439
- )
151440
- ] }),
151441
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-divider" }),
151442
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row sp-settings-row-top", children: [
151443
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151444
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Welcome guide" }),
151445
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Reopen setup help, latest highlights, and the getting-started checklist." })
151618
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-card", children: [
151619
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "Privacy" }),
151620
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
151621
+ "label",
151622
+ {
151623
+ className: "sp-settings-row",
151624
+ title: isStandalone ? "Telemetry is sent by the VS Code extension; this browser tab has no sender to control." : void 0,
151625
+ children: [
151626
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151627
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Product telemetry" }),
151628
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: isStandalone ? "Set from the VS Code extension — this browser tab doesn't send telemetry itself." : "Anonymous usage metrics only. SQL, results, credentials, names, and paths are never sent." })
151629
+ ] }),
151630
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
151631
+ "input",
151632
+ {
151633
+ type: "checkbox",
151634
+ className: "sp-settings-check",
151635
+ checked: localTelemetryEnabled,
151636
+ disabled: isStandalone,
151637
+ onChange: (e2) => {
151638
+ setLocalTelemetryEnabled(e2.target.checked);
151639
+ onSave({ telemetryEnabled: e2.target.checked });
151640
+ }
151641
+ }
151642
+ )
151643
+ ]
151644
+ }
151645
+ )
151446
151646
  ] }),
151447
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "sp-btn", onClick: onShowWelcome, children: "View welcome guide" })
151448
- ] })
151449
- ] })
151450
- ] }),
151451
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
151452
- "aside",
151453
- {
151454
- className: "sp-settings-preview",
151455
- style: { maxWidth: PREVIEW_MAX_WIDTH },
151456
- "aria-label": "Settings change preview",
151457
- children: [
151458
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-preview-header", children: [
151459
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "Live preview" }),
151460
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-preview-note", children: "Grid preview" })
151647
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-card", children: [
151648
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "About" }),
151649
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row sp-settings-row-top", children: [
151650
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151651
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Grain version" }),
151652
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row-hint", children: [
151653
+ "Installed: ",
151654
+ currentVersion ? `v${currentVersion}` : "Checking…",
151655
+ !isStandalone && (latestVersion ? ` · Latest: v${latestVersion}` : " · Checking for updates…")
151656
+ ] }),
151657
+ updateAvailable && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-version-update", children: "An update is available." })
151658
+ ] }),
151659
+ isStandalone ? /* @__PURE__ */ jsxRuntimeExports.jsx(
151660
+ "a",
151661
+ {
151662
+ className: "sp-btn",
151663
+ href: "https://github.com/pattrnlabs/grain/releases",
151664
+ target: "_blank",
151665
+ rel: "noreferrer",
151666
+ title: "This daemon can run standalone or inside VS Code; check the releases page for the latest version.",
151667
+ children: "View Releases"
151668
+ }
151669
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
151670
+ "button",
151671
+ {
151672
+ type: "button",
151673
+ className: "sp-btn",
151674
+ onClick: () => sendMessage("openExtensionPage", {}),
151675
+ children: updateAvailable ? "Open Update" : "Open Extension"
151676
+ }
151677
+ )
151678
+ ] }),
151679
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-divider" }),
151680
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row sp-settings-row-top", children: [
151681
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151682
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Welcome guide" }),
151683
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Reopen setup help, latest highlights, and the getting-started checklist." })
151684
+ ] }),
151685
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "sp-btn", onClick: onShowWelcome, children: "View welcome guide" })
151686
+ ] })
151687
+ ] })
151688
+ ] }),
151689
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
151690
+ "aside",
151691
+ {
151692
+ className: "sp-settings-preview",
151693
+ style: { maxWidth: PREVIEW_MAX_WIDTH },
151694
+ "aria-label": "Settings change preview",
151695
+ children: [
151696
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-preview-header", children: [
151697
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "Live preview" }),
151698
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-preview-note", children: "Grid preview" })
151699
+ ] }),
151700
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-preview-body", style: previewFontSizeStyle, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
151701
+ DataGrid,
151702
+ {
151703
+ theme: theme2,
151704
+ data: DUMMY_PREVIEW_DATA,
151705
+ copyIncludesHeaders,
151706
+ density: rowDensity,
151707
+ domLayout: "normal",
151708
+ disableFilters: true
151709
+ }
151710
+ ) }),
151711
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "sp-settings-preview-copy", children: [
151712
+ "Copy output will include",
151713
+ " ",
151714
+ copyIncludesHeaders ? "column headers and rows" : "rows only",
151715
+ "."
151716
+ ] })
151717
+ ]
151718
+ }
151719
+ )
151720
+ ]
151721
+ }
151722
+ ),
151723
+ activeTab === "connections" && /* @__PURE__ */ jsxRuntimeExports.jsx(
151724
+ "div",
151725
+ {
151726
+ className: connectionsSubpage ? "sp-settings-conn-panel-subpage" : void 0,
151727
+ role: "tabpanel",
151728
+ id: "sp-settings-panel-connections",
151729
+ "aria-labelledby": connectionsSubpage ? void 0 : "sp-settings-tab-connections",
151730
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
151731
+ ConnectionsManager,
151732
+ {
151733
+ theme: theme2,
151734
+ initialAdd: connectionsInitialAdd,
151735
+ onSubpageChange: setConnectionsSubpage
151736
+ }
151737
+ )
151738
+ }
151739
+ ),
151740
+ activeTab === "mcp" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
151741
+ "div",
151742
+ {
151743
+ className: "sp-settings-layout",
151744
+ role: "tabpanel",
151745
+ id: "sp-settings-panel-mcp",
151746
+ "aria-labelledby": "sp-settings-tab-mcp",
151747
+ children: [
151748
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-main", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-card sp-settings-mcp-card", children: [
151749
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-mcp-hero", children: [
151750
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-mcp-logo", children: MCP_LOGO }),
151751
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151752
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "Model Context Protocol" }),
151753
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-settings-mcp-description", children: "Enable the MCP server to allow AI assistants like Claude Desktop to connect to your databases." })
151754
+ ] })
151461
151755
  ] }),
151462
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-preview-body", style: previewFontSizeStyle, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
151463
- DataGrid,
151756
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
151757
+ "label",
151464
151758
  {
151465
- theme: theme2,
151466
- data: DUMMY_PREVIEW_DATA,
151467
- copyIncludesHeaders,
151468
- density: rowDensity,
151469
- domLayout: "normal",
151470
- disableFilters: true
151759
+ className: "sp-settings-row",
151760
+ title: isStandalone ? "This daemon always serves the MCP endpoint while running; disabling it here would have no effect." : void 0,
151761
+ children: [
151762
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151763
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Enable MCP Server" }),
151764
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: isStandalone ? "This daemon always serves MCP over HTTP while it runs." : "Start a local MCP server alongside the extension." })
151765
+ ] }),
151766
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
151767
+ "input",
151768
+ {
151769
+ type: "checkbox",
151770
+ className: "sp-settings-check",
151771
+ checked: isStandalone ? true : localMcpEnabled,
151772
+ disabled: isStandalone,
151773
+ onChange: (e2) => {
151774
+ setLocalMcpEnabled(e2.target.checked);
151775
+ onSave({ mcpEnabled: e2.target.checked });
151776
+ }
151777
+ }
151778
+ )
151779
+ ]
151471
151780
  }
151472
- ) }),
151473
- /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "sp-settings-preview-copy", children: [
151474
- "Copy output will include",
151475
- " ",
151476
- copyIncludesHeaders ? "column headers and rows" : "rows only",
151477
- "."
151478
- ] })
151479
- ]
151480
- }
151481
- )
151482
- ]
151483
- }
151484
- ),
151485
- activeTab === "connections" && /* @__PURE__ */ jsxRuntimeExports.jsx(
151486
- "div",
151487
- {
151488
- role: "tabpanel",
151489
- id: "sp-settings-panel-connections",
151490
- "aria-labelledby": "sp-settings-tab-connections",
151491
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(ConnectionsManager, { theme: theme2, initialAdd: connectionsInitialAdd })
151492
- }
151493
- ),
151494
- activeTab === "mcp" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
151495
- "div",
151496
- {
151497
- className: "sp-settings-layout",
151498
- role: "tabpanel",
151499
- id: "sp-settings-panel-mcp",
151500
- "aria-labelledby": "sp-settings-tab-mcp",
151501
- children: [
151502
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-main", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-card sp-settings-mcp-card", children: [
151503
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-mcp-hero", children: [
151504
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-mcp-logo", children: MCP_LOGO }),
151505
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151506
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "Model Context Protocol" }),
151507
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-settings-mcp-description", children: "Enable the MCP server to allow AI assistants like Claude Desktop to connect to your databases." })
151508
- ] })
151509
- ] }),
151510
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
151511
- "label",
151512
- {
151513
- className: "sp-settings-row",
151514
- title: isStandalone ? "This daemon always serves the MCP endpoint while running; disabling it here would have no effect." : void 0,
151515
- children: [
151781
+ ),
151782
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "sp-settings-row", children: [
151516
151783
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151517
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Enable MCP Server" }),
151518
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: isStandalone ? "This daemon always serves MCP over HTTP while it runs." : "Start a local MCP server alongside the extension." })
151784
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Safe mode" }),
151785
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Block agent writes. Your own editor queries are unaffected." })
151519
151786
  ] }),
151520
151787
  /* @__PURE__ */ jsxRuntimeExports.jsx(
151521
151788
  "input",
151522
151789
  {
151523
151790
  type: "checkbox",
151524
151791
  className: "sp-settings-check",
151525
- checked: isStandalone ? true : localMcpEnabled,
151526
- disabled: isStandalone,
151792
+ checked: localMcpSafeMode,
151527
151793
  onChange: (e2) => {
151528
- setLocalMcpEnabled(e2.target.checked);
151529
- onSave({ mcpEnabled: e2.target.checked });
151794
+ setLocalMcpSafeMode(e2.target.checked);
151795
+ onSave({ mcpSafeMode: e2.target.checked });
151530
151796
  }
151531
151797
  }
151532
151798
  )
151533
- ]
151534
- }
151535
- ),
151536
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "sp-settings-row", children: [
151537
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151538
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Safe mode" }),
151539
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Block agent writes. Your own editor queries are unaffected." })
151540
- ] }),
151541
- /* @__PURE__ */ jsxRuntimeExports.jsx(
151542
- "input",
151543
- {
151544
- type: "checkbox",
151545
- className: "sp-settings-check",
151546
- checked: localMcpSafeMode,
151547
- onChange: (e2) => {
151548
- setLocalMcpSafeMode(e2.target.checked);
151549
- onSave({ mcpSafeMode: e2.target.checked });
151550
- }
151551
- }
151552
- )
151553
- ] }),
151554
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "sp-settings-row", children: [
151555
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151556
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Confirm write queries" }),
151557
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Ask for confirmation before running a query that modifies data from the editor." })
151558
- ] }),
151559
- /* @__PURE__ */ jsxRuntimeExports.jsx(
151560
- "input",
151561
- {
151562
- type: "checkbox",
151563
- className: "sp-settings-check",
151564
- checked: localConfirmWriteQueries,
151565
- onChange: (e2) => {
151566
- setLocalConfirmWriteQueries(e2.target.checked);
151567
- onSave({ confirmWriteQueries: e2.target.checked });
151568
- }
151569
- }
151570
- )
151571
- ] }),
151572
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-divider" }),
151573
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row sp-settings-row-top", children: [
151574
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row-fill", children: [
151575
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Connection Test" }),
151576
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Verify the MCP server is running and reachable." }),
151577
- testMcpStatus && /* @__PURE__ */ jsxRuntimeExports.jsx(
151578
- "div",
151579
- {
151580
- className: `sp-settings-status ${testMcpStatus.status}`,
151581
- role: testMcpStatus.status === "error" ? "alert" : "status",
151582
- children: testMcpStatus.message
151583
- }
151584
- )
151585
- ] }),
151586
- /* @__PURE__ */ jsxRuntimeExports.jsx(
151587
- "button",
151588
- {
151589
- type: "button",
151590
- className: "sp-btn",
151591
- onClick: handleTestMcp,
151592
- "aria-label": "Test Local MCP",
151593
- children: "Test MCP Server"
151594
- }
151595
- )
151596
- ] }),
151597
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-divider" }),
151598
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row sp-settings-row-top", children: [
151599
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row-fill", children: [
151600
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Current Port" }),
151601
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Port the server is configured to listen on." })
151602
- ] }),
151603
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-port", children: mcpPort || "Not configured" })
151604
- ] }),
151605
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-divider" }),
151606
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row sp-settings-row-top", children: [
151607
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row-fill", children: [
151608
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Credential Storage" }),
151609
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "How database passwords are saved locally." })
151610
- ] }),
151611
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-port", style: { fontSize: "0.85em", opacity: 0.8 }, children: credentialBackend === "keychain" ? "OS Keychain" : credentialBackend === "extension" ? "VS Code Secure Storage" : credentialBackend === "file" ? "Local File (Permission Protected)" : credentialBackend || "Unknown" })
151612
- ] })
151613
- ] }) }),
151614
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
151615
- "aside",
151616
- {
151617
- className: "sp-settings-preview sp-settings-mcp-guide",
151618
- "aria-label": "MCP setup guide",
151619
- children: [
151620
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-preview-header", children: [
151621
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "MCP setup" }),
151622
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-preview-note", children: mcpStatusLabel })
151623
151799
  ] }),
151624
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
151625
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section-header", children: [
151626
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Server URL" }),
151627
- /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsCopyButton, { text: mcpServerUrl, label: "Copy server URL" })
151800
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "sp-settings-row", children: [
151801
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151802
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Confirm write queries" }),
151803
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Ask for confirmation before running a query that modifies data from the editor." })
151628
151804
  ] }),
151629
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "sp-settings-code-line", children: mcpServerUrl })
151805
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
151806
+ "input",
151807
+ {
151808
+ type: "checkbox",
151809
+ className: "sp-settings-check",
151810
+ checked: localConfirmWriteQueries,
151811
+ onChange: (e2) => {
151812
+ setLocalConfirmWriteQueries(e2.target.checked);
151813
+ onSave({ confirmWriteQueries: e2.target.checked });
151814
+ }
151815
+ }
151816
+ )
151630
151817
  ] }),
151631
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
151632
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section-header", children: [
151633
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "If you installed the Grain CLI" }),
151634
- /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsCopyButton, { text: mcpClientConfigCli, label: "Copy CLI configuration" })
151635
- ] }),
151636
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-mcp-description", children: [
151637
- "Run ",
151638
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Grain: Install Grain CLI" }),
151639
- " to add it to your PATH."
151818
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-divider" }),
151819
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row sp-settings-row-top", children: [
151820
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row-fill", children: [
151821
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Connection Test" }),
151822
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Verify the MCP server is running and reachable." }),
151823
+ testMcpStatus && /* @__PURE__ */ jsxRuntimeExports.jsx(
151824
+ "div",
151825
+ {
151826
+ className: `sp-settings-status ${testMcpStatus.status}`,
151827
+ role: testMcpStatus.status === "error" ? "alert" : "status",
151828
+ children: testMcpStatus.message
151829
+ }
151830
+ )
151640
151831
  ] }),
151641
- /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "sp-settings-code-block", "data-testid": "mcp-client-config-cli", children: mcpClientConfigCli })
151832
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
151833
+ "button",
151834
+ {
151835
+ type: "button",
151836
+ className: "sp-btn",
151837
+ onClick: handleTestMcp,
151838
+ "aria-label": "Test Local MCP",
151839
+ children: "Test MCP Server"
151840
+ }
151841
+ )
151642
151842
  ] }),
151643
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
151644
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section-header", children: [
151645
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "If you only have the extension" }),
151646
- /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsCopyButton, { text: mcpClientConfigNpx, label: "Copy npx configuration" })
151843
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-divider" }),
151844
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row sp-settings-row-top", children: [
151845
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row-fill", children: [
151846
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Current Port" }),
151847
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "Port the server is configured to listen on." })
151647
151848
  ] }),
151648
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-mcp-description", children: "npx starts Grain without installing the CLI separately." }),
151649
- /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "sp-settings-code-block", "data-testid": "mcp-client-config-npx", children: mcpClientConfigNpx })
151849
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-port", children: mcpPort || "Not configured" })
151650
151850
  ] }),
151651
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
151652
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section-header", children: [
151653
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "HTTP configuration (advanced)" }),
151654
- /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsCopyButton, { text: mcpHttpConfig, label: "Copy HTTP configuration" })
151851
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-divider" }),
151852
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row sp-settings-row-top", children: [
151853
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-row-fill", children: [
151854
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Credential Storage" }),
151855
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-hint", children: "How database passwords are saved locally." })
151655
151856
  ] }),
151656
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-mcp-description", children: "For clients that support custom headers, such as Cursor and VS Code. The token authorizes access to your connections and results — treat it like a password." }),
151657
- /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "sp-settings-code-block", "data-testid": "mcp-http-config", children: mcpHttpConfig })
151658
- ] }),
151659
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
151660
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Available tools" }),
151661
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-tool-groups", children: MCP_TOOL_GROUPS.map((group) => /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "sp-settings-tool-group", children: [
151662
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-tool-group-heading", children: [
151663
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151664
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-tool-group-title", children: group.label }),
151665
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-tool-group-description", children: group.description })
151857
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-port", style: { fontSize: "0.85em", opacity: 0.8 }, children: credentialBackend === "keychain" ? "OS Keychain" : credentialBackend === "extension" ? "VS Code Secure Storage" : credentialBackend === "file" ? "Local File (Permission Protected)" : credentialBackend || "Unknown" })
151858
+ ] })
151859
+ ] }) }),
151860
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
151861
+ "aside",
151862
+ {
151863
+ className: "sp-settings-preview sp-settings-mcp-guide",
151864
+ "aria-label": "MCP setup guide",
151865
+ children: [
151866
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-preview-header", children: [
151867
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-section-label", children: "MCP setup" }),
151868
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-preview-note", children: mcpStatusLabel })
151869
+ ] }),
151870
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
151871
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section-header", children: [
151872
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Server URL" }),
151873
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsCopyButton, { text: mcpServerUrl, label: "Copy server URL" })
151666
151874
  ] }),
151667
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "sp-settings-tool-count", children: [
151668
- group.tools.length,
151669
- " tool",
151670
- group.tools.length === 1 ? "" : "s"
151671
- ] })
151875
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "sp-settings-code-line", children: mcpServerUrl })
151672
151876
  ] }),
151673
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-tool-list", children: group.tools.map(([name, description]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-tool-item", children: [
151674
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "sp-settings-tool-name", children: name }),
151675
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: description })
151676
- ] }, name)) })
151677
- ] }, group.label)) })
151678
- ] })
151679
- ]
151680
- }
151681
- )
151682
- ]
151683
- }
151684
- )
151685
- ] })
151877
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
151878
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section-header", children: [
151879
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "If you installed the Grain CLI" }),
151880
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsCopyButton, { text: mcpClientConfigCli, label: "Copy CLI configuration" })
151881
+ ] }),
151882
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-mcp-description", children: [
151883
+ "Run ",
151884
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Grain: Install Grain CLI" }),
151885
+ " to add it to your PATH."
151886
+ ] }),
151887
+ /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "sp-settings-code-block", "data-testid": "mcp-client-config-cli", children: mcpClientConfigCli })
151888
+ ] }),
151889
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
151890
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section-header", children: [
151891
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "If you only have the extension" }),
151892
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsCopyButton, { text: mcpClientConfigNpx, label: "Copy npx configuration" })
151893
+ ] }),
151894
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-mcp-description", children: "npx starts Grain without installing the CLI separately." }),
151895
+ /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "sp-settings-code-block", "data-testid": "mcp-client-config-npx", children: mcpClientConfigNpx })
151896
+ ] }),
151897
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
151898
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section-header", children: [
151899
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "HTTP configuration (advanced)" }),
151900
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsCopyButton, { text: mcpHttpConfig, label: "Copy HTTP configuration" })
151901
+ ] }),
151902
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-mcp-description", children: "For clients that support custom headers, such as Cursor and VS Code. The token authorizes access to your connections and results — treat it like a password." }),
151903
+ /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "sp-settings-code-block", "data-testid": "mcp-http-config", children: mcpHttpConfig })
151904
+ ] }),
151905
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-guide-section", children: [
151906
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-row-label", children: "Available tools" }),
151907
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-tool-groups", children: MCP_TOOL_GROUPS.map((group) => /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "sp-settings-tool-group", children: [
151908
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-tool-group-heading", children: [
151909
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
151910
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-tool-group-title", children: group.label }),
151911
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-tool-group-description", children: group.description })
151912
+ ] }),
151913
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "sp-settings-tool-count", children: [
151914
+ group.tools.length,
151915
+ " tool",
151916
+ group.tools.length === 1 ? "" : "s"
151917
+ ] })
151918
+ ] }),
151919
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-settings-tool-list", children: group.tools.map(([name, description]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-settings-tool-item", children: [
151920
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "sp-settings-tool-name", children: name }),
151921
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: description })
151922
+ ] }, name)) })
151923
+ ] }, group.label)) })
151924
+ ] })
151925
+ ]
151926
+ }
151927
+ )
151928
+ ]
151929
+ }
151930
+ )
151931
+ ]
151932
+ }
151933
+ )
151686
151934
  ] });
151687
151935
  }
151688
151936
  const FEATURE_ICONS = {
@@ -151928,6 +152176,85 @@ function TabContextMenu({
151928
152176
  }
151929
152177
  );
151930
152178
  }
152179
+ function formatExecutionTime(ms) {
152180
+ return ms < 1e3 ? `${Math.round(ms)} ms` : `${(ms / 1e3).toFixed(1)} s`;
152181
+ }
152182
+ function ElapsedTime({ since }) {
152183
+ const [, setTick] = reactExports.useState(0);
152184
+ reactExports.useEffect(() => {
152185
+ const interval = setInterval(() => setTick((t) => t + 1), 100);
152186
+ return () => clearInterval(interval);
152187
+ }, []);
152188
+ return (
152189
+ // aria-hidden: this sits inside the loading live region; announcing every tick would spam screen readers.
152190
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-loading-elapsed", "aria-hidden": "true", children: formatExecutionTime(Date.now() - since) })
152191
+ );
152192
+ }
152193
+ function LoadingState({
152194
+ since,
152195
+ onCancel
152196
+ }) {
152197
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-loading", children: [
152198
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ProgressBar, { label: "Running query" }),
152199
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
152200
+ EmptyState,
152201
+ {
152202
+ role: "status",
152203
+ live: true,
152204
+ title: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
152205
+ "Running query…",
152206
+ since != null && /* @__PURE__ */ jsxRuntimeExports.jsx(ElapsedTime, { since })
152207
+ ] }),
152208
+ actions: /* @__PURE__ */ jsxRuntimeExports.jsx(
152209
+ Button,
152210
+ {
152211
+ variant: "ghost",
152212
+ icon: "stop",
152213
+ onClick: onCancel,
152214
+ "aria-label": "Cancel query execution",
152215
+ children: "Cancel"
152216
+ }
152217
+ )
152218
+ }
152219
+ )
152220
+ ] });
152221
+ }
152222
+ function QueryErrorState({
152223
+ message,
152224
+ onRerun,
152225
+ onCopy,
152226
+ rerunDisabled
152227
+ }) {
152228
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
152229
+ EmptyState,
152230
+ {
152231
+ icon: "alert",
152232
+ tone: "error",
152233
+ role: "alert",
152234
+ title: "Query failed",
152235
+ actions: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
152236
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "ghost", icon: "play", onClick: onRerun, disabled: rerunDisabled, children: "Run again" }),
152237
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "ghost", icon: "copy", onClick: onCopy, children: "Copy error" })
152238
+ ] }),
152239
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "sp-empty-state-message", children: message })
152240
+ }
152241
+ );
152242
+ }
152243
+ function NotPersistedState({
152244
+ onRerun,
152245
+ rerunDisabled
152246
+ }) {
152247
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
152248
+ EmptyState,
152249
+ {
152250
+ icon: "refresh",
152251
+ role: "status",
152252
+ title: "Results from your last session weren't kept",
152253
+ actions: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "primary", icon: "play", onClick: onRerun, disabled: rerunDisabled, children: "Run again" }),
152254
+ children: "Grain keeps the query, not the rows, so VS Code stays light. Run it again to see results."
152255
+ }
152256
+ );
152257
+ }
151931
152258
  const LandingScreen = ({
151932
152259
  onNewConnection,
151933
152260
  onNewQueryFile,
@@ -151942,13 +152269,19 @@ const LandingScreen = ({
151942
152269
  } = useAppStore();
151943
152270
  const [fetchedConnName, setFetchedConnName] = reactExports.useState(null);
151944
152271
  const [fetchedConnType, setFetchedConnType] = reactExports.useState(null);
152272
+ const [grainUnavailable, setGrainUnavailable] = reactExports.useState(false);
151945
152273
  reactExports.useEffect(() => {
151946
- if (!isReady || storeConnName) return;
152274
+ if (!isReady || storeConnName) {
152275
+ return;
152276
+ }
151947
152277
  let mounted = true;
151948
152278
  (async () => {
151949
152279
  try {
151950
152280
  const result = await callServerTool("list_connections", {});
151951
- if (!mounted) return;
152281
+ if (!mounted) {
152282
+ return;
152283
+ }
152284
+ setGrainUnavailable(false);
151952
152285
  const connections = result == null ? void 0 : result.structuredContent;
151953
152286
  const active = Array.isArray(connections) ? connections.find((c) => c == null ? void 0 : c.activeForQueries) : null;
151954
152287
  if (active == null ? void 0 : active.name) {
@@ -151958,6 +152291,9 @@ const LandingScreen = ({
151958
152291
  setFetchedConnType(active.type);
151959
152292
  }
151960
152293
  } catch {
152294
+ if (mounted) {
152295
+ setGrainUnavailable(true);
152296
+ }
151961
152297
  }
151962
152298
  })();
151963
152299
  return () => {
@@ -152004,6 +152340,10 @@ const LandingScreen = ({
152004
152340
  ] }),
152005
152341
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-landing-subtitle", children: "Execute SQL queries directly in your IDE." })
152006
152342
  ] }),
152343
+ !hasActiveConnection && grainUnavailable && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-landing-active-connection", children: [
152344
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Grain is stopped. Start Grain to see your connections." }),
152345
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "sp-btn", onClick: () => sendMessage("startGrain"), children: "Start Grain" })
152346
+ ] }),
152007
152347
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
152008
152348
  "div",
152009
152349
  {
@@ -152098,20 +152438,6 @@ function parseOptionalDependencyDetails(details) {
152098
152438
  }
152099
152439
  return null;
152100
152440
  }
152101
- function formatExecutionTime(ms) {
152102
- return ms < 1e3 ? `${Math.round(ms)} ms` : `${(ms / 1e3).toFixed(1)} s`;
152103
- }
152104
- function ElapsedTime({ since }) {
152105
- const [, setTick] = reactExports.useState(0);
152106
- reactExports.useEffect(() => {
152107
- const interval = setInterval(() => setTick((t) => t + 1), 100);
152108
- return () => clearInterval(interval);
152109
- }, []);
152110
- return (
152111
- // aria-hidden: this sits inside the loading live region; announcing every tick would spam screen readers.
152112
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-loading-elapsed", "aria-hidden": "true", children: formatExecutionTime(Date.now() - since) })
152113
- );
152114
- }
152115
152441
  function formatTabTooltip(tab) {
152116
152442
  var _a3;
152117
152443
  const lines = [tab.sql || ""];
@@ -152154,7 +152480,8 @@ function MultiTabLayout({ emptyState } = {}) {
152154
152480
  activeTabId,
152155
152481
  setActiveTab,
152156
152482
  closeTab,
152157
- theme: theme2,
152483
+ theme: hostTheme,
152484
+ panelTheme,
152158
152485
  updateTab,
152159
152486
  copyIncludesHeaders,
152160
152487
  rowDensity,
@@ -152181,9 +152508,12 @@ function MultiTabLayout({ emptyState } = {}) {
152181
152508
  setTabChartLayout
152182
152509
  } = useAppStore();
152183
152510
  const trackUi = useTrackUi();
152511
+ const theme2 = panelTheme === "grain-dark" ? "dark" : panelTheme === "grain-light" ? "light" : hostTheme;
152184
152512
  const { mode, sendMessage, onMessage } = useHostContext();
152185
152513
  const gridRef = reactExports.useRef(null);
152186
- const [copyFeedback, setCopyFeedback] = reactExports.useState(null);
152514
+ const [copyFeedback, setCopyFeedback] = reactExports.useState(
152515
+ null
152516
+ );
152187
152517
  const [contextMenu, setContextMenu] = reactExports.useState(null);
152188
152518
  const [sidePanel, setSidePanel] = reactExports.useState(null);
152189
152519
  const [pendingConfirm, setPendingConfirm] = reactExports.useState(null);
@@ -152471,6 +152801,7 @@ function MultiTabLayout({ emptyState } = {}) {
152471
152801
  const handleSaveSettings = (settings) => {
152472
152802
  sendMessage("saveSettings", { settings });
152473
152803
  applyConfig({
152804
+ ...settings.panelTheme !== void 0 ? { panelTheme: settings.panelTheme } : {},
152474
152805
  ...settings.fontSize !== void 0 ? { fontSize: settings.fontSize > 0 ? settings.fontSize : null } : {},
152475
152806
  ...settings.maxRowsToDisplay !== void 0 ? { maxRowsToDisplay: settings.maxRowsToDisplay } : {},
152476
152807
  ...settings.telemetryEnabled !== void 0 ? { telemetryEnabled: settings.telemetryEnabled } : {},
@@ -152490,10 +152821,35 @@ function MultiTabLayout({ emptyState } = {}) {
152490
152821
  return navigator.clipboard.writeText(text);
152491
152822
  }, []);
152492
152823
  const optionalDependencyDetails = parseOptionalDependencyDetails(activeTab == null ? void 0 : activeTab.errorDetails);
152493
- const gearIcon = /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", children: [
152494
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 4.754a3.246 3.246 0 100 6.492 3.246 3.246 0 000-6.492zM5.754 8a2.246 2.246 0 114.492 0 2.246 2.246 0 01-4.492 0z" }),
152495
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 01-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 01-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 01.52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 011.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 011.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 01.52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 01-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 01-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 002.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 001.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 00-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 00-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 00-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 001.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 003.06 4.474l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 002.692-1.115l.094-.319z" })
152496
- ] });
152824
+ const showErrorState = Boolean(
152825
+ (activeTab == null ? void 0 : activeTab.error) && !activeTab.result && !activeTab.isLoading && !optionalDependencyDetails
152826
+ );
152827
+ const handleCopyError = () => {
152828
+ if (!(activeTab == null ? void 0 : activeTab.error)) return;
152829
+ const text = activeTab.errorDetails ? `${activeTab.error}
152830
+
152831
+ ${activeTab.errorDetails}` : activeTab.error;
152832
+ navigator.clipboard.writeText(text).then(() => {
152833
+ setCopyFeedback("error");
152834
+ setTimeout(() => setCopyFeedback(null), 2e3);
152835
+ });
152836
+ };
152837
+ const fileFilterChip = activeFileName && activeFileUri && // The filter used to be invisible and unswitchable: focusing a .sql file
152838
+ // silently hid every tab belonging to anything else, with no way back (#151).
152839
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
152840
+ ToggleChip,
152841
+ {
152842
+ checked: filterResultsByActiveFile,
152843
+ icon: "filter",
152844
+ ariaLabel: `Only show results from ${activeFileName}`,
152845
+ title: filterResultsByActiveFile ? `Showing only results from ${activeFileName}. Click to show every result tab.` : `Showing every result tab. Click to show only results from ${activeFileName}.`,
152846
+ onChange: (next) => {
152847
+ setFilterResultsByActiveFile(next);
152848
+ sendMessage("saveSettings", { settings: { filterResultsByActiveFile: next } });
152849
+ },
152850
+ children: filterResultsByActiveFile ? activeFileName : "All results"
152851
+ }
152852
+ );
152497
152853
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `sp-app ${theme2 === "dark" ? "vscode-dark" : "vscode-light"}`, children: [
152498
152854
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-tab-bar", children: [
152499
152855
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-tab-bar-tabs", role: "tablist", "aria-label": "Result tabs", children: visibleTabs.map((tab) => {
@@ -152515,7 +152871,7 @@ function MultiTabLayout({ emptyState } = {}) {
152515
152871
  onKeyDown: (e2) => handleTabKeyDown(e2, tab.id),
152516
152872
  onContextMenu: (e2) => handleTabContextMenu(e2, tab.id),
152517
152873
  children: [
152518
- tab.isLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-spinner" }),
152874
+ tab.isLoading && /* @__PURE__ */ jsxRuntimeExports.jsx(BusyDot, { label: "Running" }),
152519
152875
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-tab-label", title: formatTabTooltip(tab), children: tab.title }),
152520
152876
  showProvenance && tab.clientType && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { id: `sp-tab-provenance-${tab.id}`, className: "sp-visually-hidden", children: formatTabTooltip(tab) }) }),
152521
152877
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -152535,68 +152891,16 @@ function MultiTabLayout({ emptyState } = {}) {
152535
152891
  );
152536
152892
  }) }),
152537
152893
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-tab-bar-actions", children: [
152538
- activeFileName && activeFileUri && /* @__PURE__ */ jsxRuntimeExports.jsxs(
152539
- "button",
152540
- {
152541
- type: "button",
152542
- className: "sp-active-file-indicator",
152543
- title: "Open Source File",
152544
- "aria-label": `Open active SQL file ${activeFileName}`,
152545
- onClick: () => sendMessage("openFile", { fileUri: activeFileUri }),
152546
- children: [
152547
- /* @__PURE__ */ jsxRuntimeExports.jsx(
152548
- "svg",
152549
- {
152550
- className: "sp-active-file-icon",
152551
- width: "12",
152552
- height: "12",
152553
- viewBox: "0 0 16 16",
152554
- fill: "currentColor",
152555
- "aria-hidden": "true",
152556
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M13.71 4.29l-3-3L10 1H4L3 2v12l1 1h9l1-1V5l-.29-.71zM10 2.41L12.59 5H10V2.41zM13 14H4V2h5v4l1 1h3v7z" })
152557
- }
152558
- ),
152559
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-active-file-name", children: activeFileName })
152560
- ]
152561
- }
152562
- ),
152563
- activeFileName && activeFileUri && // The filter used to be invisible and unswitchable: focusing a .sql file
152564
- // silently hid every tab belonging to anything else, with no way back (#151).
152565
- /* @__PURE__ */ jsxRuntimeExports.jsx(
152566
- "button",
152567
- {
152568
- type: "button",
152569
- className: `sp-active-file-toggle${filterResultsByActiveFile ? " on" : ""}`,
152570
- role: "switch",
152571
- "aria-checked": filterResultsByActiveFile,
152572
- title: filterResultsByActiveFile ? `Showing only results from ${activeFileName}. Click to show every result tab.` : `Showing every result tab. Click to show only results from ${activeFileName}.`,
152573
- onClick: () => {
152574
- const next = !filterResultsByActiveFile;
152575
- setFilterResultsByActiveFile(next);
152576
- sendMessage("saveSettings", { settings: { filterResultsByActiveFile: next } });
152577
- },
152578
- children: filterResultsByActiveFile ? "This file" : "All files"
152579
- }
152580
- ),
152894
+ fileFilterChip,
152581
152895
  mcpEnabled && (mode === "standalone" || mcpEntitled) && /* @__PURE__ */ jsxRuntimeExports.jsx(SafeModeIndicator, { safeMode: mcpSafeMode, connectorType: activeConnectorType }),
152582
- pendingCloses.size > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
152583
- "button",
152584
- {
152585
- className: "sp-icon-btn active",
152586
- onClick: handleUndoLastClose,
152587
- title: "Undo Close Tab",
152588
- "aria-label": "Undo Close Tab",
152589
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(UndoIcon, {})
152590
- }
152591
- ),
152896
+ pendingCloses.size > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(IconButton, { icon: "undo", label: "Undo Close Tab", active: true, onClick: handleUndoLastClose }),
152592
152897
  /* @__PURE__ */ jsxRuntimeExports.jsx(
152593
- "button",
152898
+ IconButton,
152594
152899
  {
152595
- className: `sp-icon-btn${settingsPanelTab ? " active" : ""}`,
152596
- onClick: settingsPanelTab ? closeSettingsPanel : handleOpenSettings,
152597
- title: "Settings",
152598
- "aria-label": "Settings",
152599
- children: gearIcon
152900
+ icon: "settings",
152901
+ label: "Settings",
152902
+ active: Boolean(settingsPanelTab),
152903
+ onClick: settingsPanelTab ? closeSettingsPanel : handleOpenSettings
152600
152904
  }
152601
152905
  )
152602
152906
  ] })
@@ -152626,74 +152930,61 @@ function MultiTabLayout({ emptyState } = {}) {
152626
152930
  id: "sp-result-tabpanel",
152627
152931
  "aria-labelledby": `sp-tab-${activeTab.id}`,
152628
152932
  children: [
152629
- activeConfirmation && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-error-card", role: "alert", children: [
152630
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-error-content", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-error-title", children: activeConfirmation.kind === "write" ? `This query will modify data (${activeConfirmation.keyword}). Run it anyway?` : activeConfirmation.kind === "multi_statement" ? `This query contains ${activeConfirmation.count} statements. Run it anyway?` : "The connection dialect could not be resolved, so Grain cannot verify this query. Run it anyway?" }) }),
152631
- /* @__PURE__ */ jsxRuntimeExports.jsx(
152632
- "button",
152633
- {
152634
- ref: runAnywayRef,
152635
- className: "sp-btn sp-btn-primary",
152636
- onClick: handleConfirmRunAnyway,
152637
- children: "Run anyway"
152638
- }
152639
- ),
152640
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "sp-btn", onClick: handleCancelConfirmation, children: "Cancel" })
152641
- ] }),
152642
- activeTab.error && /* @__PURE__ */ jsxRuntimeExports.jsxs(
152643
- "div",
152933
+ activeConfirmation && /* @__PURE__ */ jsxRuntimeExports.jsx(
152934
+ Notice,
152644
152935
  {
152645
- className: optionalDependencyDetails ? "sp-error-card" : "sp-error-bar",
152936
+ tone: "warning",
152646
152937
  role: "alert",
152647
- children: [
152648
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-error-content", children: [
152649
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-error-title", children: activeTab.error }),
152650
- optionalDependencyDetails && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
152651
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-error-guidance", children: optionalDependencyDetails.guidance }),
152652
- optionalDependencyDetails.cause && /* @__PURE__ */ jsxRuntimeExports.jsxs("details", { className: "sp-error-details", children: [
152653
- /* @__PURE__ */ jsxRuntimeExports.jsx("summary", { children: "Technical details" }),
152654
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: optionalDependencyDetails.cause })
152655
- ] })
152656
- ] })
152657
- ] }),
152658
- optionalDependencyDetails && /* @__PURE__ */ jsxRuntimeExports.jsxs(
152659
- "button",
152660
- {
152661
- className: "sp-btn sp-btn-primary",
152662
- onClick: () => handleInstallOptionalDependency(optionalDependencyDetails.dependency),
152663
- children: [
152664
- "Install ",
152665
- optionalDependencyDetails.dependency
152666
- ]
152667
- }
152668
- ),
152669
- /* @__PURE__ */ jsxRuntimeExports.jsx(
152670
- "button",
152671
- {
152672
- onClick: () => updateTab(activeTab.id, { error: null, errorDetails: null }),
152673
- "aria-label": "Close error message",
152674
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(CloseIcon, { size: 14 })
152675
- }
152676
- )
152677
- ]
152938
+ title: activeConfirmation.kind === "write" ? `This query will modify data (${activeConfirmation.keyword}). Run it anyway?` : activeConfirmation.kind === "multi_statement" ? `This query contains ${activeConfirmation.count} statements. Run it anyway?` : "The connection dialect could not be resolved, so Grain cannot verify this query. Run it anyway?",
152939
+ actions: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
152940
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { ref: runAnywayRef, variant: "primary", onClick: handleConfirmRunAnyway, children: "Run anyway" }),
152941
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "ghost", onClick: handleCancelConfirmation, children: "Cancel" })
152942
+ ] })
152678
152943
  }
152679
152944
  ),
152680
- activeTab.cancelled && !activeTab.error && !activeTab.isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-cancelled-bar", role: "status", children: [
152681
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Query cancelled" }),
152682
- /* @__PURE__ */ jsxRuntimeExports.jsx(
152683
- "button",
152684
- {
152685
- onClick: () => updateTab(activeTab.id, { cancelled: false }),
152686
- "aria-label": "Dismiss cancelled notice",
152687
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(CloseIcon, { size: 14 })
152688
- }
152689
- )
152690
- ] }),
152691
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-result-header", children: [
152945
+ activeTab.error && !showErrorState && /* @__PURE__ */ jsxRuntimeExports.jsx(
152946
+ Notice,
152947
+ {
152948
+ tone: optionalDependencyDetails ? "warning" : "error",
152949
+ title: activeTab.error,
152950
+ dismissLabel: "Close error message",
152951
+ onDismiss: () => updateTab(activeTab.id, { error: null, errorDetails: null }),
152952
+ actions: optionalDependencyDetails && /* @__PURE__ */ jsxRuntimeExports.jsxs(
152953
+ Button,
152954
+ {
152955
+ variant: "primary",
152956
+ onClick: () => handleInstallOptionalDependency(optionalDependencyDetails.dependency),
152957
+ children: [
152958
+ "Install ",
152959
+ optionalDependencyDetails.dependency
152960
+ ]
152961
+ }
152962
+ ),
152963
+ children: optionalDependencyDetails && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
152964
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-error-guidance", children: optionalDependencyDetails.guidance }),
152965
+ optionalDependencyDetails.cause && /* @__PURE__ */ jsxRuntimeExports.jsxs("details", { className: "sp-error-details", children: [
152966
+ /* @__PURE__ */ jsxRuntimeExports.jsx("summary", { children: "Technical details" }),
152967
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: optionalDependencyDetails.cause })
152968
+ ] })
152969
+ ] })
152970
+ }
152971
+ ),
152972
+ activeTab.cancelled && !activeTab.error && !activeTab.isLoading && /* @__PURE__ */ jsxRuntimeExports.jsx(
152973
+ Notice,
152974
+ {
152975
+ tone: "neutral",
152976
+ title: "Query cancelled",
152977
+ dismissLabel: "Dismiss cancelled notice",
152978
+ onDismiss: () => updateTab(activeTab.id, { cancelled: false })
152979
+ }
152980
+ ),
152981
+ activeTab.result && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-result-header", children: [
152692
152982
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-result-header-left", children: [
152693
152983
  /* @__PURE__ */ jsxRuntimeExports.jsx(
152694
- "button",
152984
+ Button,
152695
152985
  {
152696
- className: "sp-btn sp-btn-primary",
152986
+ variant: "ghost",
152987
+ icon: "play",
152697
152988
  onClick: handleRunQuery,
152698
152989
  disabled: activeTab.isLoading,
152699
152990
  "aria-label": "Re-run query",
@@ -152701,6 +152992,7 @@ function MultiTabLayout({ emptyState } = {}) {
152701
152992
  }
152702
152993
  ),
152703
152994
  activeTab.result && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
152995
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-toolbar-sep", "aria-hidden": "true" }),
152704
152996
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "sp-row-count", children: [
152705
152997
  /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: activeTab.result.rowCount }),
152706
152998
  " rows",
@@ -152710,7 +153002,7 @@ function MultiTabLayout({ emptyState } = {}) {
152710
153002
  "span",
152711
153003
  {
152712
153004
  className: "sp-truncation-badge",
152713
- title: "Result set was truncated. Use Export All to get the full data.",
153005
+ title: "Result set was truncated. Use Export to get the full data.",
152714
153006
  children: "Truncated"
152715
153007
  }
152716
153008
  )
@@ -152725,18 +153017,20 @@ function MultiTabLayout({ emptyState } = {}) {
152725
153017
  "aria-label": "Result representation",
152726
153018
  children: [
152727
153019
  /* @__PURE__ */ jsxRuntimeExports.jsx(
152728
- "button",
153020
+ Button,
152729
153021
  {
152730
- className: `sp-btn${(activeTab.representation ?? "grid") === "grid" ? " sp-btn-primary" : ""}`,
153022
+ variant: (activeTab.representation ?? "grid") === "grid" ? "secondary" : "ghost",
153023
+ icon: "grid",
152731
153024
  "aria-pressed": (activeTab.representation ?? "grid") === "grid",
152732
153025
  onClick: () => setTabRepresentation(activeTab.id, "grid"),
152733
153026
  children: "Grid"
152734
153027
  }
152735
153028
  ),
152736
153029
  /* @__PURE__ */ jsxRuntimeExports.jsx(
152737
- "button",
153030
+ Button,
152738
153031
  {
152739
- className: `sp-btn${activeTab.representation === "chart" ? " sp-btn-primary" : ""}`,
153032
+ variant: activeTab.representation === "chart" ? "secondary" : "ghost",
153033
+ icon: "chart",
152740
153034
  "aria-pressed": activeTab.representation === "chart",
152741
153035
  onClick: () => setTabRepresentation(activeTab.id, "chart"),
152742
153036
  children: "Chart"
@@ -152746,12 +153040,13 @@ function MultiTabLayout({ emptyState } = {}) {
152746
153040
  }
152747
153041
  ),
152748
153042
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-visually-hidden", role: "status", children: copyFeedback ? "Copied to clipboard" : "" }),
152749
- copyFeedback === "query" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-copy-feedback", children: "Query copied" }),
153043
+ (copyFeedback === "query" || copyFeedback === "error") && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-copy-feedback", children: copyFeedback === "query" ? "Query copied" : "Error copied" }),
152750
153044
  activeTab.result && activeTab.result.rows.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
152751
153045
  activeTab.result.rowCount > 5 && /* @__PURE__ */ jsxRuntimeExports.jsx(
152752
- "button",
153046
+ Button,
152753
153047
  {
152754
- className: "sp-btn",
153048
+ variant: "ghost",
153049
+ icon: copyFeedback === "first" ? "check" : "copy",
152755
153050
  onClick: () => {
152756
153051
  var _a4;
152757
153052
  return copyToClipboard(
@@ -152761,13 +153056,14 @@ function MultiTabLayout({ emptyState } = {}) {
152761
153056
  },
152762
153057
  title: "Copy first 5 rows as TSV",
152763
153058
  "aria-label": "Copy first 5 rows as TSV",
152764
- children: copyFeedback === "first" ? "Copied!" : "Copy First 5 Rows"
153059
+ children: copyFeedback === "first" ? "Copied" : "Copy 5"
152765
153060
  }
152766
153061
  ),
152767
153062
  /* @__PURE__ */ jsxRuntimeExports.jsx(
152768
- "button",
153063
+ Button,
152769
153064
  {
152770
- className: "sp-btn",
153065
+ variant: "ghost",
153066
+ icon: copyFeedback === "all" ? "check" : "copy",
152771
153067
  onClick: () => {
152772
153068
  var _a4;
152773
153069
  return copyToClipboard(
@@ -152777,38 +153073,24 @@ function MultiTabLayout({ emptyState } = {}) {
152777
153073
  },
152778
153074
  title: "Copy all visible rows as TSV",
152779
153075
  "aria-label": `Copy all ${activeTab.result.rowCount} rows as TSV`,
152780
- children: copyFeedback === "all" ? "Copied!" : `Copy All ${activeTab.result.rowCount}`
153076
+ children: copyFeedback === "all" ? "Copied" : "Copy all"
152781
153077
  }
152782
153078
  ),
152783
153079
  /* @__PURE__ */ jsxRuntimeExports.jsx(
152784
- "button",
153080
+ Button,
152785
153081
  {
152786
- className: "sp-btn",
153082
+ variant: "ghost",
153083
+ icon: "download",
152787
153084
  onClick: handleExportCsv,
152788
153085
  title: "Export full results as CSV",
152789
153086
  "aria-label": "Export full results as CSV",
152790
- children: "Export All"
153087
+ children: "Export"
152791
153088
  }
152792
153089
  )
152793
153090
  ] }) : null
152794
153091
  ] })
152795
153092
  ] }),
152796
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-grid-container", children: activeTab.isLoading && !activeTab.result ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-loading", role: "status", "aria-live": "polite", children: [
152797
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-spinner sp-spinner-lg" }),
152798
- /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { children: [
152799
- "Executing query…",
152800
- activeTab.loadingStartedAt != null && /* @__PURE__ */ jsxRuntimeExports.jsx(ElapsedTime, { since: activeTab.loadingStartedAt })
152801
- ] }),
152802
- /* @__PURE__ */ jsxRuntimeExports.jsx(
152803
- "button",
152804
- {
152805
- className: "sp-btn",
152806
- onClick: handleCancelQuery,
152807
- "aria-label": "Cancel query execution",
152808
- children: "Cancel"
152809
- }
152810
- )
152811
- ] }) : activeTab.result ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-split-container", children: [
153093
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-grid-container", children: activeTab.isLoading && !activeTab.result ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingState, { since: activeTab.loadingStartedAt, onCancel: handleCancelQuery }) : activeTab.result ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-split-container", children: [
152812
153094
  chartsEnabled && activeTab.representation === "chart" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
152813
153095
  "div",
152814
153096
  {
@@ -152919,7 +153201,16 @@ function MultiTabLayout({ emptyState } = {}) {
152919
153201
  onCopy: handleSidePanelCopy
152920
153202
  }
152921
153203
  )
152922
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-empty", children: "No results yet." }) })
153204
+ ] }) : showErrorState ? /* @__PURE__ */ jsxRuntimeExports.jsx(
153205
+ QueryErrorState,
153206
+ {
153207
+ message: activeTab.errorDetails ? `${activeTab.error}
153208
+
153209
+ ${activeTab.errorDetails}` : activeTab.error ?? "",
153210
+ onRerun: handleRunQuery,
153211
+ onCopy: handleCopyError
153212
+ }
153213
+ ) : activeTab.notPersisted ? /* @__PURE__ */ jsxRuntimeExports.jsx(NotPersistedState, { onRerun: handleRunQuery }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-empty", children: "No results yet." }) })
152923
153214
  ]
152924
153215
  }
152925
153216
  ),
@@ -152967,7 +153258,7 @@ const SIDEBAR_AUTO_COLLAPSE_QUERY = "(max-width: 640px)";
152967
153258
  const SIDEBAR_WIDTH_STORAGE_KEY = "sql-preview:web-sidebar-width";
152968
153259
  function WebApp({ viewHint }) {
152969
153260
  var _a3;
152970
- const { onMessage, isReady, sendMessage, callServerTool } = useHostContext();
153261
+ const { onMessage, isReady, sendMessage, callServerTool, track: track2 } = useHostContext();
152971
153262
  const session = useWebSession();
152972
153263
  const {
152973
153264
  addTab,
@@ -152982,13 +153273,20 @@ function WebApp({ viewHint }) {
152982
153273
  } = useAppStore();
152983
153274
  const [connections, setConnections] = reactExports.useState([]);
152984
153275
  const [setupOpen, setSetupOpen] = reactExports.useState(false);
153276
+ const [editingConnection, setEditingConnection] = reactExports.useState(null);
153277
+ const [openConnectionIds, setOpenConnectionIds] = reactExports.useState([]);
153278
+ const [activeConnectionId, setActiveConnectionId] = reactExports.useState(null);
153279
+ const [connectionStatus, setConnectionStatus] = reactExports.useState({});
152985
153280
  const [descriptions, setDescriptions] = reactExports.useState({});
152986
153281
  const [queryConnections, setQueryConnections] = reactExports.useState({});
152987
153282
  const [closedQueries, setClosedQueries] = reactExports.useState([]);
152988
153283
  const activeTab = tabs.find((tab) => tab.id === activeTabId);
152989
153284
  const localConnection = connections.find((connection) => connection.id === "built-in-duckdb");
152990
153285
  const selectedConnectionId = activeTab ? queryConnections[activeTab.id] ?? (localConnection == null ? void 0 : localConnection.id) ?? ((_a3 = connections[0]) == null ? void 0 : _a3.id) ?? "" : "";
152991
- const newQuery = () => {
153286
+ reactExports.useEffect(() => {
153287
+ if (isReady) track2 == null ? void 0 : track2("ui_state_shown", { state: "workspace_ready" });
153288
+ }, [isReady, track2]);
153289
+ const newQuery = (connectionId = "built-in-duckdb") => {
152992
153290
  const remoteId = crypto.randomUUID();
152993
153291
  const id = addTab(
152994
153292
  "SELECT 42;",
@@ -152997,7 +153295,32 @@ function WebApp({ viewHint }) {
152997
153295
  void 0,
152998
153296
  { sessionId: WEB_SESSION_ID, remoteId }
152999
153297
  );
153000
- setQueryConnections((current2) => ({ ...current2, [id]: "built-in-duckdb" }));
153298
+ setQueryConnections((current2) => ({ ...current2, [id]: connectionId }));
153299
+ setActiveConnectionId(null);
153300
+ };
153301
+ const openConnection = (id) => {
153302
+ setOpenConnectionIds((current2) => current2.includes(id) ? current2 : [...current2, id]);
153303
+ setActiveConnectionId(id);
153304
+ };
153305
+ const testConnection = async (id) => {
153306
+ var _a4, _b3;
153307
+ setConnectionStatus((current2) => ({ ...current2, [id]: "Testing…" }));
153308
+ try {
153309
+ const result = await callServerTool("test_connection", { connectionId: id });
153310
+ const message = ((_b3 = (_a4 = result.content) == null ? void 0 : _a4.find((item) => item.type === "text")) == null ? void 0 : _b3.text) ?? "Connection test failed";
153311
+ setConnectionStatus((current2) => {
153312
+ var _a5;
153313
+ return {
153314
+ ...current2,
153315
+ [id]: result.isError || ((_a5 = result.structuredContent) == null ? void 0 : _a5["success"]) === false ? message : "Connected"
153316
+ };
153317
+ });
153318
+ } catch (error) {
153319
+ setConnectionStatus((current2) => ({
153320
+ ...current2,
153321
+ [id]: error instanceof Error ? error.message : "Connection test failed"
153322
+ }));
153323
+ }
153001
153324
  };
153002
153325
  const closeQuery = (id) => {
153003
153326
  const tab = tabs.find((item) => item.id === id);
@@ -153056,12 +153379,22 @@ function WebApp({ viewHint }) {
153056
153379
  connection: ((_b3 = connections.find((connection) => connection.id === selectedConnectionId)) == null ? void 0 : _b3.name) ?? selectedConnectionId
153057
153380
  }
153058
153381
  });
153382
+ track2 == null ? void 0 : track2("ui_action_performed", {
153383
+ state: "query_editor",
153384
+ ui_action: "run_query",
153385
+ outcome: "success"
153386
+ });
153059
153387
  } catch (error) {
153060
153388
  if ((_c3 = useAppStore.getState().tabs.find((tab) => tab.id === id)) == null ? void 0 : _c3.cancelled) return;
153061
153389
  updateTab(id, {
153062
153390
  isLoading: false,
153063
153391
  error: error instanceof Error ? error.message : "Query failed"
153064
153392
  });
153393
+ track2 == null ? void 0 : track2("ui_action_performed", {
153394
+ state: "query_editor",
153395
+ ui_action: "run_query",
153396
+ outcome: "error"
153397
+ });
153065
153398
  }
153066
153399
  };
153067
153400
  const cancelQuery = async () => {
@@ -153331,7 +153664,7 @@ function WebApp({ viewHint }) {
153331
153664
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-no-tabs sp-web-empty-stack", children: [
153332
153665
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-web-empty-title", children: "Your workspace is ready" }),
153333
153666
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Run a query with Local Files, or connect a database. Your results and agent activity appear here." }),
153334
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "sp-web-primary-button", onClick: newQuery, children: "New query" })
153667
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "sp-web-primary-button", onClick: () => newQuery(), children: "New query" })
153335
153668
  ] });
153336
153669
  if (session.state === "needs-relaunch") {
153337
153670
  return /* @__PURE__ */ jsxRuntimeExports.jsx(ReauthorizeNotice, { reason: session.reason ?? "no-session" });
@@ -153341,6 +153674,15 @@ function WebApp({ viewHint }) {
153341
153674
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-brand", children: [
153342
153675
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-web-brand-icon", "aria-hidden": "true" }),
153343
153676
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-web-wordmark", children: "Grain" }),
153677
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
153678
+ "span",
153679
+ {
153680
+ className: "sp-web-beta-badge",
153681
+ "aria-label": "Beta feature",
153682
+ title: "The Grain workspace is in beta",
153683
+ children: "Beta"
153684
+ }
153685
+ ),
153344
153686
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-web-header-divider" }),
153345
153687
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Workspace" })
153346
153688
  ] }),
@@ -153419,13 +153761,22 @@ function WebApp({ viewHint }) {
153419
153761
  }
153420
153762
  ),
153421
153763
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-connection-list", children: [
153422
- connections.length ? connections.map((connection) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-connection-item", children: [
153423
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-web-connection-icon", "aria-hidden": "true", children: "▤" }),
153424
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
153425
- connection.name ?? connection.id,
153426
- /* @__PURE__ */ jsxRuntimeExports.jsx("small", { children: connection.type })
153427
- ] })
153428
- ] }, connection.id)) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-web-sidebar-helper", children: "No connections yet." }),
153764
+ connections.length ? connections.map((connection) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
153765
+ "button",
153766
+ {
153767
+ type: "button",
153768
+ className: "sp-web-connection-item",
153769
+ onClick: () => openConnection(connection.id),
153770
+ children: [
153771
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-web-connection-icon", "aria-hidden": "true", children: "▤" }),
153772
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
153773
+ connection.name ?? connection.id,
153774
+ /* @__PURE__ */ jsxRuntimeExports.jsx("small", { children: connection.type })
153775
+ ] })
153776
+ ]
153777
+ },
153778
+ connection.id
153779
+ )) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "sp-web-sidebar-helper", children: "No connections yet." }),
153429
153780
  /* @__PURE__ */ jsxRuntimeExports.jsx(
153430
153781
  "button",
153431
153782
  {
@@ -153438,7 +153789,7 @@ function WebApp({ viewHint }) {
153438
153789
  ] }),
153439
153790
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-web-sidebar-section-title", children: "Queries" }),
153440
153791
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-session-list", children: [
153441
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "sp-web-add-connection", onClick: newQuery, children: "+ New query" }),
153792
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "sp-web-add-connection", onClick: () => newQuery(), children: "+ New query" }),
153442
153793
  closedQueries.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "sp-web-add-connection", onClick: reopenQuery, children: "Reopen closed query" }),
153443
153794
  tabs.filter((tab) => tab.sessionId === WEB_SESSION_ID).map((tab) => /* @__PURE__ */ jsxRuntimeExports.jsx(
153444
153795
  "button",
@@ -153446,7 +153797,10 @@ function WebApp({ viewHint }) {
153446
153797
  type: "button",
153447
153798
  className: `sp-web-session-item sp-web-session-button${activeTabId === tab.id ? " active" : ""}`,
153448
153799
  "aria-pressed": activeTabId === tab.id,
153449
- onClick: () => setActiveTab(tab.id),
153800
+ onClick: () => {
153801
+ setActiveTab(tab.id);
153802
+ setActiveConnectionId(null);
153803
+ },
153450
153804
  children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-web-session-name", children: tab.title })
153451
153805
  },
153452
153806
  tab.id
@@ -153458,7 +153812,10 @@ function WebApp({ viewHint }) {
153458
153812
  type: "button",
153459
153813
  className: `sp-web-session-item sp-web-session-button${activeTabId === tab.id ? " active" : ""}`,
153460
153814
  "aria-pressed": activeTabId === tab.id,
153461
- onClick: () => setActiveTab(tab.id),
153815
+ onClick: () => {
153816
+ setActiveTab(tab.id);
153817
+ setActiveConnectionId(null);
153818
+ },
153462
153819
  children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-web-session-name", children: tab.title })
153463
153820
  },
153464
153821
  tab.id
@@ -153471,10 +153828,21 @@ function WebApp({ viewHint }) {
153471
153828
  ConnectionSetupPage,
153472
153829
  {
153473
153830
  onDone: () => {
153831
+ if (!editingConnection) {
153832
+ track2 == null ? void 0 : track2("ui_action_performed", {
153833
+ state: "connection_manager",
153834
+ ui_action: "connection_created"
153835
+ });
153836
+ }
153474
153837
  setSetupOpen(false);
153838
+ setEditingConnection(null);
153475
153839
  void fetchConnections();
153476
153840
  },
153477
- onCancel: () => setSetupOpen(false)
153841
+ onCancel: () => {
153842
+ setSetupOpen(false);
153843
+ setEditingConnection(null);
153844
+ },
153845
+ initialConnection: editingConnection ?? void 0
153478
153846
  }
153479
153847
  ) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "sp-web-main", children: [
153480
153848
  !monitorOpen && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-web-main-toolbar", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -153488,20 +153856,58 @@ function WebApp({ viewHint }) {
153488
153856
  children: "Sessions"
153489
153857
  }
153490
153858
  ) }),
153491
- !settingsPanelTab && activeTab && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-query-workspace", children: [
153492
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-query-tabs", role: "tablist", "aria-label": "Open queries", children: [
153859
+ !settingsPanelTab && (activeTab || activeConnectionId) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-query-workspace", children: [
153860
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-query-tabs", role: "tablist", "aria-label": "Open tabs", children: [
153861
+ openConnectionIds.map((id) => {
153862
+ const connection = connections.find((item) => item.id === id);
153863
+ if (!connection) return null;
153864
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
153865
+ "div",
153866
+ {
153867
+ className: `sp-web-query-tab${activeConnectionId === id ? " active" : ""}`,
153868
+ children: [
153869
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
153870
+ "button",
153871
+ {
153872
+ type: "button",
153873
+ role: "tab",
153874
+ "aria-selected": activeConnectionId === id,
153875
+ onClick: () => setActiveConnectionId(id),
153876
+ children: connection.name ?? id
153877
+ }
153878
+ ),
153879
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
153880
+ "button",
153881
+ {
153882
+ type: "button",
153883
+ "aria-label": `Close ${connection.name ?? id} tab`,
153884
+ onClick: () => {
153885
+ setOpenConnectionIds((current2) => current2.filter((item) => item !== id));
153886
+ if (activeConnectionId === id) setActiveConnectionId(null);
153887
+ },
153888
+ children: "×"
153889
+ }
153890
+ )
153891
+ ]
153892
+ },
153893
+ `connection-${id}`
153894
+ );
153895
+ }),
153493
153896
  tabs.map((tab) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
153494
153897
  "div",
153495
153898
  {
153496
- className: `sp-web-query-tab${tab.id === activeTabId ? " active" : ""}`,
153899
+ className: `sp-web-query-tab${tab.id === activeTabId && !activeConnectionId ? " active" : ""}`,
153497
153900
  children: [
153498
153901
  /* @__PURE__ */ jsxRuntimeExports.jsx(
153499
153902
  "button",
153500
153903
  {
153501
153904
  type: "button",
153502
153905
  role: "tab",
153503
- "aria-selected": tab.id === activeTabId,
153504
- onClick: () => setActiveTab(tab.id),
153906
+ "aria-selected": tab.id === activeTabId && !activeConnectionId,
153907
+ onClick: () => {
153908
+ setActiveTab(tab.id);
153909
+ setActiveConnectionId(null);
153910
+ },
153505
153911
  children: tab.title
153506
153912
  }
153507
153913
  ),
@@ -153523,93 +153929,141 @@ function WebApp({ viewHint }) {
153523
153929
  {
153524
153930
  type: "button",
153525
153931
  className: "sp-web-query-tab-action",
153526
- onClick: newQuery,
153932
+ onClick: () => newQuery(),
153527
153933
  "aria-label": "New query",
153528
153934
  children: "+"
153529
153935
  }
153530
153936
  ),
153531
153937
  closedQueries.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "sp-web-query-tab-action", onClick: reopenQuery, children: "Reopen closed query" })
153532
153938
  ] }),
153533
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-query-heading", children: [
153534
- /* @__PURE__ */ jsxRuntimeExports.jsx(
153535
- "input",
153536
- {
153537
- "aria-label": "Query title",
153538
- value: activeTab.title,
153539
- onChange: (event) => updateTab(activeTab.id, { title: event.target.value })
153540
- }
153541
- ),
153542
- /* @__PURE__ */ jsxRuntimeExports.jsx(
153543
- "textarea",
153544
- {
153545
- "aria-label": "Query description",
153546
- placeholder: "Add a description",
153547
- value: descriptions[activeTab.id] ?? "",
153548
- onChange: (event) => setDescriptions((current2) => ({
153549
- ...current2,
153550
- [activeTab.id]: event.target.value
153551
- })),
153552
- rows: 1
153553
- }
153554
- )
153555
- ] }),
153556
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-query-toolbar", children: [
153557
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { children: [
153558
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Connection" }),
153939
+ activeConnectionId ? (() => {
153940
+ const connection = connections.find((item) => item.id === activeConnectionId);
153941
+ if (!connection) return null;
153942
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-connection-detail", children: [
153943
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { children: connection.name ?? connection.id }),
153944
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { children: [
153945
+ connection.type ?? "Database",
153946
+ " ·",
153947
+ " ",
153948
+ connectionStatus[connection.id] ?? (connection.engineState === "ready" ? "Ready" : "Saved connection")
153949
+ ] }),
153950
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-connection-actions", children: [
153951
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
153952
+ "button",
153953
+ {
153954
+ type: "button",
153955
+ className: "sp-web-primary-button",
153956
+ "aria-label": `New query for ${connection.name ?? connection.id}`,
153957
+ onClick: () => newQuery(connection.id),
153958
+ children: "New query"
153959
+ }
153960
+ ),
153961
+ !connection.builtIn && /* @__PURE__ */ jsxRuntimeExports.jsx(
153962
+ "button",
153963
+ {
153964
+ type: "button",
153965
+ className: "sp-web-secondary-button",
153966
+ onClick: () => {
153967
+ setEditingConnection(connection);
153968
+ setSetupOpen(true);
153969
+ },
153970
+ children: "Edit"
153971
+ }
153972
+ ),
153973
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
153974
+ "button",
153975
+ {
153976
+ type: "button",
153977
+ className: "sp-web-secondary-button",
153978
+ onClick: () => void testConnection(connection.id),
153979
+ children: "Test"
153980
+ }
153981
+ )
153982
+ ] })
153983
+ ] });
153984
+ })() : activeTab && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
153985
+ " ",
153986
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-query-heading", children: [
153559
153987
  /* @__PURE__ */ jsxRuntimeExports.jsx(
153560
- "select",
153988
+ "input",
153561
153989
  {
153562
- "aria-label": "Query connection",
153563
- value: selectedConnectionId,
153564
- onChange: (event) => setQueryConnections((current2) => ({
153990
+ "aria-label": "Query title",
153991
+ value: activeTab.title,
153992
+ onChange: (event) => updateTab(activeTab.id, { title: event.target.value })
153993
+ }
153994
+ ),
153995
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
153996
+ "textarea",
153997
+ {
153998
+ "aria-label": "Query description",
153999
+ placeholder: "Add a description",
154000
+ value: descriptions[activeTab.id] ?? "",
154001
+ onChange: (event) => setDescriptions((current2) => ({
153565
154002
  ...current2,
153566
154003
  [activeTab.id]: event.target.value
153567
154004
  })),
153568
- children: connections.map((connection) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: connection.id, children: connection.name ?? connection.id }, connection.id))
154005
+ rows: 1
153569
154006
  }
153570
154007
  )
153571
154008
  ] }),
153572
- activeTab.isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx(
153573
- "button",
153574
- {
153575
- type: "button",
153576
- className: "sp-web-secondary-button",
153577
- onClick: () => void cancelQuery(),
153578
- children: "Cancel run"
153579
- }
153580
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
153581
- "button",
154009
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-web-query-toolbar", children: [
154010
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { children: [
154011
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Connection" }),
154012
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
154013
+ "select",
154014
+ {
154015
+ "aria-label": "Query connection",
154016
+ value: selectedConnectionId,
154017
+ onChange: (event) => setQueryConnections((current2) => ({
154018
+ ...current2,
154019
+ [activeTab.id]: event.target.value
154020
+ })),
154021
+ children: connections.map((connection) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: connection.id, children: connection.name ?? connection.id }, connection.id))
154022
+ }
154023
+ )
154024
+ ] }),
154025
+ activeTab.isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx(
154026
+ "button",
154027
+ {
154028
+ type: "button",
154029
+ className: "sp-web-secondary-button",
154030
+ onClick: () => void cancelQuery(),
154031
+ children: "Cancel run"
154032
+ }
154033
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
154034
+ "button",
154035
+ {
154036
+ type: "button",
154037
+ className: "sp-web-primary-button",
154038
+ onClick: () => void runQuery(),
154039
+ disabled: !selectedConnectionId || !activeTab.sql.trim(),
154040
+ children: [
154041
+ "Run ",
154042
+ /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { children: "⌘↵" })
154043
+ ]
154044
+ }
154045
+ )
154046
+ ] }),
154047
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
154048
+ "textarea",
153582
154049
  {
153583
- type: "button",
153584
- className: "sp-web-primary-button",
153585
- onClick: () => void runQuery(),
153586
- disabled: !selectedConnectionId || !activeTab.sql.trim(),
153587
- children: [
153588
- "Run ",
153589
- /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { children: "⌘↵" })
153590
- ]
154050
+ className: "sp-web-sql-editor",
154051
+ "aria-label": "SQL editor",
154052
+ spellCheck: false,
154053
+ value: activeTab.sql,
154054
+ onChange: (event) => updateTab(activeTab.id, { sql: event.target.value }),
154055
+ onKeyDown: (event) => {
154056
+ if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
154057
+ event.preventDefault();
154058
+ void runQuery();
154059
+ }
154060
+ }
153591
154061
  }
153592
154062
  )
153593
154063
  ] }),
153594
- /* @__PURE__ */ jsxRuntimeExports.jsx(
153595
- "textarea",
153596
- {
153597
- className: "sp-web-sql-editor",
153598
- "aria-label": "SQL editor",
153599
- spellCheck: false,
153600
- value: activeTab.sql,
153601
- onChange: (event) => updateTab(activeTab.id, { sql: event.target.value }),
153602
- onKeyDown: (event) => {
153603
- if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
153604
- event.preventDefault();
153605
- void runQuery();
153606
- }
153607
- }
153608
- }
153609
- ),
153610
154064
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-web-results-label", children: "Results" })
153611
154065
  ] }),
153612
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-web-results", children: /* @__PURE__ */ jsxRuntimeExports.jsx(MultiTabLayout, { emptyState: resultsEmptyState }) })
154066
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-web-results", hidden: Boolean(activeConnectionId), children: /* @__PURE__ */ jsxRuntimeExports.jsx(MultiTabLayout, { emptyState: resultsEmptyState }) })
153613
154067
  ] })
153614
154068
  ] })
153615
154069
  ] });
@@ -162393,7 +162847,7 @@ button.ag-side-button-button:focus {
162393
162847
  --sp-warning: var(--vscode-charts-yellow, #ca8a04);
162394
162848
 
162395
162849
  --sp-font: var(
162396
- --vscode-editor-font-family,
162850
+ --vscode-font-family,
162397
162851
  -apple-system,
162398
162852
  BlinkMacSystemFont,
162399
162853
  'Segoe UI',
@@ -162678,75 +163132,55 @@ body {
162678
163132
  gap: 6px;
162679
163133
  }
162680
163134
 
162681
- .sp-active-file-indicator {
163135
+ .sp-chip {
162682
163136
  display: inline-flex;
162683
163137
  align-items: center;
162684
- gap: 4px;
163138
+ gap: 5px;
162685
163139
  max-width: 220px;
162686
- margin-right: 8px;
162687
- padding: 2px 6px;
162688
- border: 1px solid transparent;
162689
- border-radius: var(--sp-radius);
162690
- background: var(--sp-badge-bg);
162691
- color: var(--sp-badge-fg);
163140
+ height: 22px;
163141
+ padding: 0 8px;
163142
+ border: 1px dashed var(--sp-border);
163143
+ border-radius: 999px;
163144
+ background: transparent;
163145
+ color: var(--sp-muted-fg);
162692
163146
  font: inherit;
162693
163147
  font-size: 11px;
162694
163148
  cursor: pointer;
162695
- opacity: 0.9;
163149
+ transition:
163150
+ color 0.1s,
163151
+ background 0.1s,
163152
+ border-color 0.1s;
162696
163153
  }
162697
-
162698
- .sp-active-file-indicator:hover {
162699
- opacity: 1;
162700
- border-color: var(--sp-focus-border);
163154
+ .sp-chip:hover {
163155
+ color: var(--sp-fg);
163156
+ border-color: var(--sp-muted-fg);
162701
163157
  }
162702
-
162703
- .sp-active-file-icon {
162704
- flex-shrink: 0;
163158
+ .sp-chip.on {
163159
+ border-style: solid;
163160
+ border-color: color-mix(in srgb, var(--sp-focus-border) 55%, transparent);
163161
+ background: color-mix(in srgb, var(--sp-focus-border) 14%, transparent);
163162
+ color: var(--sp-fg);
162705
163163
  }
162706
-
162707
- .sp-active-file-name {
163164
+ .sp-chip.on .sp-icon {
163165
+ color: var(--sp-focus-border);
163166
+ }
163167
+ .sp-chip:focus-visible {
163168
+ outline: 1px solid var(--sp-focus-border);
163169
+ outline-offset: 1px;
163170
+ }
163171
+ .sp-chip-label {
162708
163172
  min-width: 0;
162709
163173
  overflow: hidden;
162710
163174
  text-overflow: ellipsis;
162711
163175
  white-space: nowrap;
162712
163176
  }
162713
163177
 
162714
- .sp-active-file-state {
162715
- color: var(--sp-muted-fg);
162716
- flex-shrink: 0;
162717
- }
162718
-
162719
- /* Sibling of the file chip: states whether the tab list is narrowed to that file,
162720
- and switches it. Quiet by default so it reads as status until hovered. */
162721
- .sp-active-file-toggle {
162722
- flex-shrink: 0;
162723
- margin-right: 8px;
162724
- padding: 2px 6px;
162725
- border: 1px solid transparent;
162726
- border-radius: var(--sp-radius);
162727
- background: var(--sp-badge-bg);
162728
- color: var(--sp-muted-fg);
162729
- font: inherit;
162730
- font-size: 11px;
162731
- cursor: pointer;
162732
- opacity: 0.9;
162733
- }
162734
-
162735
- .sp-active-file-toggle.on {
162736
- color: var(--sp-badge-fg);
162737
- }
162738
-
162739
- .sp-active-file-toggle:hover {
162740
- opacity: 1;
162741
- border-color: var(--sp-focus-border);
162742
- }
162743
-
162744
163178
  .sp-tab {
162745
163179
  display: flex;
162746
163180
  align-items: center;
162747
- gap: 4px;
162748
- padding: 0 10px;
162749
- height: 35px;
163181
+ gap: 6px;
163182
+ padding: 0 10px 0 12px;
163183
+ height: 30px;
162750
163184
  cursor: pointer;
162751
163185
  white-space: nowrap;
162752
163186
  border-bottom: 2px solid transparent;
@@ -162917,8 +163351,8 @@ body {
162917
163351
  padding: 0 10px;
162918
163352
  border-bottom: 1px solid var(--sp-border);
162919
163353
  flex-shrink: 0;
162920
- height: 36px;
162921
- background: var(--sp-header-bg);
163354
+ height: 32px;
163355
+ background: var(--sp-bg);
162922
163356
  gap: 8px;
162923
163357
  }
162924
163358
 
@@ -162961,36 +163395,62 @@ body {
162961
163395
  .sp-btn {
162962
163396
  display: inline-flex;
162963
163397
  align-items: center;
162964
- gap: 4px;
162965
- padding: 3px 8px;
162966
- font-size: 11px;
163398
+ justify-content: center;
163399
+ gap: 5px;
163400
+ height: 24px;
163401
+ padding: 0 8px;
163402
+ font-size: 12px;
163403
+ font-weight: 500;
162967
163404
  font-family: var(--sp-font);
162968
163405
  cursor: pointer;
162969
- border: none;
163406
+ border: 1px solid transparent;
162970
163407
  border-radius: var(--sp-radius);
162971
163408
  background: var(--sp-btn-sec-bg);
162972
163409
  color: var(--sp-btn-sec-fg);
162973
163410
  white-space: nowrap;
162974
- transition: background 0.1s;
162975
- line-height: 1.4;
163411
+ transition:
163412
+ background 0.1s,
163413
+ color 0.1s,
163414
+ border-color 0.1s;
163415
+ line-height: 1;
162976
163416
  }
162977
163417
  .sp-btn:hover:not(:disabled) {
162978
163418
  background: var(--sp-btn-sec-hover);
162979
163419
  }
163420
+ .sp-btn:focus-visible {
163421
+ outline: 1px solid var(--sp-focus-border);
163422
+ outline-offset: 1px;
163423
+ }
162980
163424
  .sp-btn:disabled {
162981
163425
  opacity: 0.4;
162982
163426
  cursor: default;
162983
163427
  }
163428
+ .sp-btn .sp-icon {
163429
+ flex-shrink: 0;
163430
+ }
162984
163431
 
162985
163432
  .sp-btn-primary {
162986
163433
  background: var(--sp-btn-bg);
162987
163434
  color: var(--sp-btn-fg);
162988
- font-weight: 500;
162989
163435
  }
162990
163436
  .sp-btn-primary:hover:not(:disabled) {
162991
163437
  background: var(--sp-btn-hover);
162992
163438
  }
162993
163439
 
163440
+ .sp-btn-ghost {
163441
+ background: transparent;
163442
+ color: var(--sp-fg);
163443
+ }
163444
+ .sp-btn-ghost .sp-icon {
163445
+ color: var(--sp-muted-fg);
163446
+ }
163447
+ .sp-btn-ghost:hover:not(:disabled) {
163448
+ background: var(--sp-hover-bg);
163449
+ }
163450
+ .sp-btn-ghost:hover:not(:disabled) .sp-icon {
163451
+ color: var(--sp-fg);
163452
+ }
163453
+
162994
163454
  .sp-btn-danger {
162995
163455
  background: var(--sp-btn-danger-bg);
162996
163456
  color: var(--sp-btn-danger-fg);
@@ -163060,14 +163520,14 @@ body {
163060
163520
  .sp-icon-btn:hover {
163061
163521
  color: var(--sp-fg);
163062
163522
  background: var(--sp-hover-bg);
163063
- border-color: var(--sp-border);
163523
+ }
163524
+ .sp-icon-btn:focus-visible {
163525
+ outline: 1px solid var(--sp-focus-border);
163526
+ outline-offset: -1px;
163064
163527
  }
163065
163528
  .sp-icon-btn.active {
163066
- color: var(--sp-btn-bg);
163529
+ color: var(--sp-fg);
163067
163530
  background: var(--sp-hover-bg);
163068
- border-color: transparent !important;
163069
- outline: none !important;
163070
- box-shadow: none !important;
163071
163531
  }
163072
163532
 
163073
163533
  /* ── Grid container ────────────────────────────────────────── */
@@ -163084,19 +163544,22 @@ body {
163084
163544
  .sp-loading {
163085
163545
  display: flex;
163086
163546
  flex-direction: column;
163087
- align-items: center;
163088
- justify-content: center;
163089
163547
  height: 100%;
163090
- gap: 12px;
163091
163548
  color: var(--sp-muted-fg);
163092
163549
  font-size: 13px;
163093
163550
  }
163551
+ .sp-loading .sp-empty-state {
163552
+ flex: 1;
163553
+ height: auto;
163554
+ }
163094
163555
  .sp-loading p {
163095
163556
  margin: 0;
163096
163557
  }
163097
163558
  /* Live elapsed readout; tabular digits so the count-up doesn't jitter horizontally. */
163098
163559
  .sp-loading-elapsed {
163099
163560
  margin-left: 6px;
163561
+ color: var(--sp-muted-fg);
163562
+ font-weight: 400;
163100
163563
  font-variant-numeric: tabular-nums;
163101
163564
  }
163102
163565
 
@@ -163414,9 +163877,40 @@ body {
163414
163877
  --ag-row-height: 28px;
163415
163878
  --ag-header-height: 32px;
163416
163879
  --ag-icon-size: 14px;
163417
- --ag-header-column-separator-display: block;
163418
- --ag-header-column-separator-height: 100%;
163419
- --ag-header-column-separator-width: 1px;
163880
+ --ag-header-column-separator-display: none;
163881
+ --ag-header-column-resize-handle-display: block;
163882
+ --ag-header-column-resize-handle-width: 1px;
163883
+ --ag-header-column-resize-handle-height: 40%;
163884
+ --ag-header-column-resize-handle-color: var(--sp-border);
163885
+ --ag-wrapper-border-radius: 0;
163886
+ --ag-borders: none;
163887
+ --ag-row-border-color: color-mix(in srgb, var(--sp-border) 60%, transparent);
163888
+ }
163889
+
163890
+ .sp-ag-grid .ag-header-cell-resize:hover::after {
163891
+ height: 100%;
163892
+ top: 0;
163893
+ background-color: var(--sp-focus-border);
163894
+ }
163895
+
163896
+ .sp-ag-grid .ag-header-cell-text {
163897
+ font-weight: 600;
163898
+ }
163899
+
163900
+ .sp-ag-grid .ag-header-cell .ag-header-cell-filter-button,
163901
+ .sp-ag-grid .ag-header-cell .ag-sort-indicator-icon {
163902
+ color: var(--sp-muted-fg);
163903
+ }
163904
+
163905
+ .sp-ag-grid .ag-header-cell .ag-header-cell-filter-button {
163906
+ opacity: 0;
163907
+ transition: opacity 0.1s;
163908
+ }
163909
+
163910
+ .sp-ag-grid .ag-header-cell:hover .ag-header-cell-filter-button,
163911
+ .sp-ag-grid .ag-header-cell:focus-within .ag-header-cell-filter-button,
163912
+ .sp-ag-grid .ag-header-cell-filtered .ag-header-cell-filter-button {
163913
+ opacity: 1;
163420
163914
  }
163421
163915
 
163422
163916
  .sp-ag-grid .ag-header-cell-filtered {
@@ -165563,6 +166057,322 @@ body.vscode-high-contrast .ag-theme-quartz {
165563
166057
  opacity: 0.7;
165564
166058
  }
165565
166059
  }
166060
+
166061
+ /* ── Primitives: notice ────────────────────────────────────── */
166062
+ .sp-notice {
166063
+ display: flex;
166064
+ align-items: flex-start;
166065
+ gap: 8px;
166066
+ padding: 6px 8px 6px 12px;
166067
+ border-bottom: 1px solid var(--sp-border);
166068
+ background: var(--sp-header-bg);
166069
+ color: var(--sp-fg);
166070
+ font-size: 12px;
166071
+ line-height: 1.45;
166072
+ flex-shrink: 0;
166073
+ }
166074
+ .sp-notice-icon {
166075
+ display: inline-flex;
166076
+ padding-top: 2px;
166077
+ color: var(--sp-muted-fg);
166078
+ flex-shrink: 0;
166079
+ }
166080
+ .sp-notice-error .sp-notice-icon {
166081
+ color: var(--sp-error);
166082
+ }
166083
+ .sp-notice-warning .sp-notice-icon {
166084
+ color: var(--sp-warning);
166085
+ }
166086
+ .sp-notice-info .sp-notice-icon {
166087
+ color: var(--sp-focus-border);
166088
+ }
166089
+ .sp-notice-body {
166090
+ display: flex;
166091
+ flex: 1;
166092
+ flex-direction: column;
166093
+ gap: 2px;
166094
+ min-width: 0;
166095
+ padding-top: 1px;
166096
+ }
166097
+ .sp-notice-title {
166098
+ overflow-wrap: anywhere;
166099
+ }
166100
+ .sp-notice-detail {
166101
+ color: var(--sp-muted-fg);
166102
+ overflow-wrap: anywhere;
166103
+ }
166104
+ .sp-notice-actions {
166105
+ display: flex;
166106
+ align-items: center;
166107
+ gap: 6px;
166108
+ flex-shrink: 0;
166109
+ }
166110
+ .sp-notice-dismiss {
166111
+ width: 22px;
166112
+ height: 22px;
166113
+ flex-shrink: 0;
166114
+ }
166115
+
166116
+ /* ── Primitives: empty state ───────────────────────────────── */
166117
+ .sp-empty-state {
166118
+ display: flex;
166119
+ flex-direction: column;
166120
+ align-items: center;
166121
+ justify-content: center;
166122
+ gap: 6px;
166123
+ height: 100%;
166124
+ padding: 24px;
166125
+ color: var(--sp-muted-fg);
166126
+ font-size: 12px;
166127
+ text-align: center;
166128
+ }
166129
+ .sp-empty-state-icon {
166130
+ display: inline-flex;
166131
+ margin-bottom: 2px;
166132
+ color: var(--sp-muted-fg);
166133
+ }
166134
+ .sp-empty-state-error .sp-empty-state-icon {
166135
+ color: var(--sp-error);
166136
+ }
166137
+ .sp-empty-state-title {
166138
+ color: var(--sp-fg);
166139
+ font-size: 13px;
166140
+ font-weight: 600;
166141
+ }
166142
+ .sp-empty-state-detail {
166143
+ max-width: 60ch;
166144
+ line-height: 1.5;
166145
+ overflow-wrap: anywhere;
166146
+ }
166147
+ .sp-empty-state-detail code,
166148
+ .sp-empty-state-message {
166149
+ display: block;
166150
+ max-height: 40vh;
166151
+ overflow: auto;
166152
+ padding: 8px 10px;
166153
+ border: 1px solid var(--sp-border);
166154
+ border-radius: var(--sp-radius);
166155
+ background: var(--sp-header-bg);
166156
+ color: var(--sp-fg);
166157
+ font-family: var(--sp-font-mono, var(--vscode-editor-font-family, monospace));
166158
+ font-size: calc(var(--sp-font-size) - 1px);
166159
+ text-align: left;
166160
+ white-space: pre-wrap;
166161
+ word-break: break-word;
166162
+ user-select: text;
166163
+ }
166164
+ .sp-empty-state-actions {
166165
+ display: flex;
166166
+ align-items: center;
166167
+ gap: 6px;
166168
+ margin-top: 8px;
166169
+ }
166170
+ .sp-empty-state-meta {
166171
+ font-variant-numeric: tabular-nums;
166172
+ }
166173
+
166174
+ /* ── Primitives: progress ──────────────────────────────────── */
166175
+ @keyframes sp-progress-slide {
166176
+ 0% {
166177
+ transform: translateX(-100%);
166178
+ }
166179
+ 100% {
166180
+ transform: translateX(400%);
166181
+ }
166182
+ }
166183
+ .sp-progress {
166184
+ position: relative;
166185
+ height: 2px;
166186
+ overflow: hidden;
166187
+ flex-shrink: 0;
166188
+ }
166189
+ .sp-progress-bar {
166190
+ position: absolute;
166191
+ inset: 0 auto 0 0;
166192
+ width: 25%;
166193
+ background: var(--sp-focus-border);
166194
+ animation: sp-progress-slide 1.1s cubic-bezier(0.45, 0, 0.55, 1) infinite;
166195
+ }
166196
+ @keyframes sp-busy-pulse {
166197
+ 0%,
166198
+ 100% {
166199
+ opacity: 0.35;
166200
+ transform: scale(0.8);
166201
+ }
166202
+ 50% {
166203
+ opacity: 1;
166204
+ transform: scale(1);
166205
+ }
166206
+ }
166207
+ .sp-busy-dot {
166208
+ display: inline-flex;
166209
+ align-items: center;
166210
+ justify-content: center;
166211
+ width: 8px;
166212
+ height: 8px;
166213
+ flex-shrink: 0;
166214
+ }
166215
+ .sp-busy-dot > span {
166216
+ width: 6px;
166217
+ height: 6px;
166218
+ border-radius: 50%;
166219
+ background: var(--sp-focus-border);
166220
+ animation: sp-busy-pulse 1s ease-in-out infinite;
166221
+ }
166222
+ @media (prefers-reduced-motion: reduce) {
166223
+ .sp-progress-bar {
166224
+ width: 100%;
166225
+ animation: none;
166226
+ opacity: 0.6;
166227
+ }
166228
+ .sp-busy-dot > span {
166229
+ animation: none;
166230
+ }
166231
+ }
166232
+
166233
+ /* ── Primitives: page header ───────────────────────────────── */
166234
+ .sp-page-header {
166235
+ display: flex;
166236
+ align-items: center;
166237
+ gap: 6px;
166238
+ min-height: 35px;
166239
+ padding: 0 10px;
166240
+ border-bottom: 1px solid var(--sp-border);
166241
+ background: var(--sp-header-bg);
166242
+ flex-shrink: 0;
166243
+ }
166244
+ .sp-page-back {
166245
+ display: inline-flex;
166246
+ align-items: center;
166247
+ gap: 4px;
166248
+ height: 24px;
166249
+ padding: 0 6px;
166250
+ border: none;
166251
+ border-radius: var(--sp-radius);
166252
+ background: transparent;
166253
+ color: var(--sp-muted-fg);
166254
+ font: inherit;
166255
+ font-size: 12px;
166256
+ cursor: pointer;
166257
+ }
166258
+ .sp-page-back:hover {
166259
+ color: var(--sp-fg);
166260
+ background: var(--sp-hover-bg);
166261
+ }
166262
+ .sp-page-back:focus-visible {
166263
+ outline: 1px solid var(--sp-focus-border);
166264
+ outline-offset: -1px;
166265
+ }
166266
+ .sp-page-header-sep {
166267
+ color: var(--sp-border);
166268
+ }
166269
+ .sp-page-title {
166270
+ margin: 0;
166271
+ font-size: 13px;
166272
+ font-weight: 600;
166273
+ color: var(--sp-fg);
166274
+ white-space: nowrap;
166275
+ overflow: hidden;
166276
+ text-overflow: ellipsis;
166277
+ }
166278
+ .sp-page-meta {
166279
+ display: flex;
166280
+ align-items: center;
166281
+ gap: 6px;
166282
+ }
166283
+ .sp-page-actions {
166284
+ display: flex;
166285
+ align-items: center;
166286
+ gap: 6px;
166287
+ margin-left: auto;
166288
+ }
166289
+
166290
+ /* ── Result toolbar ────────────────────────────────────────── */
166291
+ .sp-toolbar-sep {
166292
+ width: 1px;
166293
+ height: 14px;
166294
+ margin: 0 4px;
166295
+ background: var(--sp-border);
166296
+ flex-shrink: 0;
166297
+ }
166298
+
166299
+ /* ── Primitives: segmented control ─────────────────────────── */
166300
+ .sp-segmented {
166301
+ display: inline-flex;
166302
+ padding: 2px;
166303
+ gap: 2px;
166304
+ border: 1px solid var(--sp-border);
166305
+ border-radius: calc(var(--sp-radius) + 2px);
166306
+ background: var(--sp-bg);
166307
+ flex-shrink: 0;
166308
+ }
166309
+ .sp-segmented-item {
166310
+ height: 22px;
166311
+ padding: 0 8px;
166312
+ border: none;
166313
+ border-radius: var(--sp-radius);
166314
+ background: transparent;
166315
+ color: var(--sp-muted-fg);
166316
+ font: inherit;
166317
+ font-size: 12px;
166318
+ cursor: pointer;
166319
+ white-space: nowrap;
166320
+ transition:
166321
+ background 0.1s,
166322
+ color 0.1s;
166323
+ }
166324
+ .sp-segmented-item:hover {
166325
+ color: var(--sp-fg);
166326
+ }
166327
+ .sp-segmented-item.active {
166328
+ background: var(--sp-header-bg);
166329
+ color: var(--sp-fg);
166330
+ font-weight: 600;
166331
+ box-shadow: inset 0 0 0 1px var(--sp-border);
166332
+ }
166333
+ .sp-segmented-item:focus-visible {
166334
+ outline: 1px solid var(--sp-focus-border);
166335
+ outline-offset: -1px;
166336
+ }
166337
+ /* ── Connection subpage ── */
166338
+ .sp-settings-page-body.sp-settings-page-body-subpage {
166339
+ padding: 0;
166340
+ overflow: hidden;
166341
+ }
166342
+
166343
+ .sp-settings-conn-panel-subpage,
166344
+ .sp-conn-subpage {
166345
+ display: flex;
166346
+ flex: 1;
166347
+ flex-direction: column;
166348
+ min-height: 0;
166349
+ height: 100%;
166350
+ }
166351
+
166352
+ .sp-conn-subpage {
166353
+ width: 100%;
166354
+ }
166355
+
166356
+ .sp-conn-subpage-body {
166357
+ flex: 1;
166358
+ min-height: 0;
166359
+ overflow-y: auto;
166360
+ padding: 24px 32px 32px;
166361
+ }
166362
+
166363
+ .sp-conn-subpage-column {
166364
+ display: block;
166365
+ max-width: 720px;
166366
+ margin-inline: auto;
166367
+ }
166368
+
166369
+ .sp-conn-subpage .sp-conn-detail-body {
166370
+ padding: 0;
166371
+ }
166372
+
166373
+ .sp-conn-schema-error {
166374
+ color: var(--sp-error);
166375
+ }
165566
166376
  @font-face {
165567
166377
  font-family: 'Manrope';
165568
166378
  src: url('data:font/woff2;base64,d09GMgABAAAAAGAAABIAAAAA8dAAAF+TAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGlAb1Vwckwo/SFZBUosuBmA/U1RBVIECAIRmL2oRCAqBrRiBil0LhDIAMIGXUgE2AiQDiGAEIAWHJgeKVxsj3ScYtwcV9GZVCXY584tmI2q3Eyqo8WzNDOZxCMLdIvv//xOSGzIK2Q+m1qq+g4KXnIuu+SqWkGkm2Oh0VdWh7qO7sztRUmV2lRp5onYqbpzY2OiRnO/SgNjyfDdLJB4m7nmukCKvpLDMrZFbf/rc5EB6dIkaYhYTNCQ9g5umVq6KOctbFYtII6/+HT7hPZo07GCh7FAOLWPB6zvp9eEj4snFNUMVTvKjP81yBriTYxFy5Pwf3k3/MnVXG2vHtsvO7el54Dg5NueXP3a77f5j2vyV896DICUU0Y+IBM0T8YmEUEREQgi+ICGUEIIjhBgCBDdBYnCWGIIRt9tDUroA4HnQjv/OTTIzX1qCFayW7OBqK1ZECfaAqr8v8N//ft/7c1XVhnMuv74BRJcxTgdZxQHpCLV3p8PGxQFLdEA67OKiCD7c/59/N/t9zr25uQk3H8IlXEIIIQSIIYaIEUKMmMSYRoqYoTxLEZFq9F/GIkVUZEjAzziOy+c4nz9jGZ8zz1HqYhyWpS6Kn28pdRyHKlqkSCki4i9SDDdDsM0OJ2ahImZPRHEWoKJNHSGCUYCCiRGJUdPfjBzm4rc3ltbCbf+/KD8Xv6/2c/DAv5v1hxWTjjjbpWZAby5BtEyglGZDkGHYDIMNzUBqzLL6t5/yRD8+3awcTk1J3neSR9cAsM8elYCsMV6D2NQBqBCWt/Hzr8yCLZdosFkuUOkLLJd/vPj48/BiM7zU46U4Pa5J8bh9W/NrbwglSkBc80vrh9NCqRNl/ytNYjVSR/kAGiOuBjA4qPIkud0/CAQ00k6g2sG6LGk9WVRPQkGFRXnSHL/b91EOFgfyT/09O8u207Mg+Gi3ZkJsPKHM4p+XPXdfqEoSouZvNomKbBTrbKYIufwzNTdcGgv9xReRRuPoE9H/31ct23cJYQZU2ANshKbCrBPkKMmxFDzbOYaiC7F7/z38TICfAEZE4IgUREgASI0/QOqcD1D0AuTHFwkpnxUncIabImXNpvgAiDNg2gVFKkzIzrkKObUp1LKLasopynXn0m1Ip7fr3mXlXFS2dGn4Ur4i9IT1xkik1Wm+Fob93l07vRHEz0aBgf9/1lK7f++ZAr8iSKARbqZAahPXqho5ZzY0+ZkSTYqsAcA2FWpbXVZJqnJWRVfWaGCjq2QJbAcU4VMAGrQbWOJhFiQQKAWYPf5c/8ykYGMdPLJRQ4/R//vqOazlDG7Rv5umIXuVbThkccQRCeL3Vf/6HrLf3htinx/2ox+9RzQJ5iScc15wjAOpQ+bMAbZ8Wml/LWc2EY7kyATrSwGNRbSaWxG8NAR3BWXC+xZ4lwQ0ki72PzK3tnMvnEhtAnuyweFPdzITTs7FAwYxIxti/xsTWdgzy7oN2ItS7BYsEgBgzUEXPtptQYPEZiT80xSvnUEMr34z8ADNRWZ9shhFn1rO6tddH01xB+/EVzkjaBKq+jkcvS3Ko48rqtB8tAhTgqZXDqKKv9BQuf4fRtTCUcjR9MryHusc0OLN6nZr/k0K/xVVhKMqylVPUbQ1UXe0l/HfHWMRH8VR+9SjeuKVZvP1zZBAXZomhgsp2xHSXOtVS+i4R/QcdbWDzzjBBakYl6bXz6evY/jRV9RxWkn2IVleU9f9NF7WR/BSkP3c3aGavgOf8olEWpQUjwlQ7B+ADiT++B90Q2zgYQcArPkEh27BoFaoz7OSU5UNgnuS/IvEPqpZSXSr6rkJQ0vMu2Evyq5+BEpZvORXxgliu/nd68qNqbsJzVBXMCmFVcfNzPd2ULOO7o7zGn1/LBcusQY0xxkOKavUpyDVTDbvyC78AQTkNhGKOLaUBwYdQh25atqbrXSzGu2wAWGSUp2uajlVIdwYsfMaXZprizWWYpBNMVe8zDAFWAKw64o0P8tXqtU0zIEfg0v/hIrkWVnVkFU1KOzGiTYbtnqfgBDjrxhAnyJvZx4ifUDx9YU+1hIwsXORNLPKfRFwgbZo6yqUlib5tkN3yN/A4t5UbBXYKUzYtNIkUBC3yD3GN5v+SPAUZBFQd3l/9iMEc+cTz5mDmhBm/v6XiLTkEkejlesIVQwiGjarn991sW/mQzfAXUQkaCnRiPhErrClSX4shKrwsXMZ1U9ytHYAJ1GpE1WFdgn9PXC1jayb6D30H47BULNUJ5qbdjlFlmPWyROJYTtEw818zx6TgqLnNQy4pHqVCuIEmufw3QMX+jbF5uoyrfMYYzvEtRWTQuJKyuJ7xUk4wVLTTPqvMwsny3JaZVYoslI9v30I/0GPGz5wT9s9DNd7bMIOkx/bDUB0HQAgaIklBgYZQsM9YR6ICCkG0mAc8wGbIIEypLqE1S7MBGFmxcnuS8bCSmieXHJ5TVjKJp+IPZxgPjfKorCAhxfyltL4wsgSRcSWKkmx4i0Lq/gtR3tXBmVSey9MK7dCrIqwwEoByFrFWie99Zplg5Q2SmFzX8m22IrwoRoxtjVhVq06jO1hrXo7JNnlSQnui+k0k8d8wqzKeahKjzgXlOiiOJeU6LI4V5ToqjjXlOi6ODc8MVOknJ/iO6PSjUHpHivVuLAnijMhjcmPi2BuCI0gys7sYmJSMR+XkZBKIxOHRJJAuwySqTiasJ6OATZLpKw8NXfZbCiDWQmy7xVnEC6qM3XmorvzuLDHYjG8Jk8IRWYssYrxlCzQi7JOs6yX0QbF2yilTZLb7NqsgWBQILH0BMrUo2QXpHHRk/OChmQ0JKMhGQ2ss7mIxpAwwi777kBOqQNQMmcnzjPsH2qH8lpYc6uuqhoXI+R9h3sGyhElwrYtpaFsKFWlyNckDyDBFTnoW3Ikn89Ni3lzTQ6kcBpIV1N36kwqCypwhfGb8PkRO6Nbuq7lZbjwICXYhRFPoloEjZVMw8gkO8dqXnZZGq14ff5V0XJQA9XikWgsnkim1vJZezQLZrFUrpyrnr9gX+45z958y6233c6SICY5/eAoO6Jz+DwuF1121XW9+vxXv6/dM+ixSXE83QBY3OLlLXwUs5JKVlHF+6xmDQHWs1GnZ96l3R/3MpRMi3ToGppjAQGJAAohNCKpu9wG53wp8xWpXo/sYFexr9cRoUxBn8wscYbz1RjvUmxV9PmAgwUUsBCnuwD3f53Dzs0ckbdCocEoj6/QkYaedDLIxMAsTMruiXnk3iSGJ+/jzUb/kM5rhI/DxQzEUfdDBxgwYcEmj3wK2gTdIZ2gpasn7qARgPw6gIWuSzkWEJAIoBBCI7pfHTslBeV1uM3k+eDbXNv3lnEa+uAwwKTpLw3IAbwUoSoTkLZ48Xk5BCsAAACAFAisXwutbL3rdNCJdDHdqmWzWfXv654pBQBNgHCg0+s5/WXBat+4GEB6HSCHYnYMEJAIoBBCI1IL8H3dxw/g2eB0l9XaYe7eTq+kz1HwwwDEdbzMP+0gQBeB7OkGDUB8HSCGAABghIBEAIUQGhH7noa4QDr2uW5xOgpvF/W/6rQykK85ABD60+1fQ8cg3WAH728zcs8Hu9QdDuQ+pNe8ePP+/LZhHOEHQXNxjrPaYYzHR9SlgQcWwGuY+UOcDEBeZ1SO+D0a6V5gZwoooYs2pgsvN9aUpByMUmH+3YQ8qBYVcBogC4qk2kJAIoBCCI3ojBrgTalK8w9JDA5gAQUsxIkL99wegBosxovPlzL0lZ1XIquo4n1Ws4aA13v4DmBXsa/jEgkqBV3omUtc4Ro3JgPQ15mkIPWm1fuRrxnMiXPopASMRSZ0n6bjFgISARRCaESZGho3Pz2XghBCCCGeIl58vgS9EHjbitJzLBzHcRxPgUC+/hRmkUQGKeaPy9NQrUBv19R4KkuSSfVprdNcThltQVu3R7H2eSB4GHbup2WHiYHDFH1X8VFKnmg7ih533ZNey3USjHiZiFiMYcnCJVNLFgRky9Ns1xdjc/X9zJkPNmwOnpXTbrL3a9PWoeki0ZNldl7UPtegtqWaDaw+r+KSy80uyy8Lu/FfBXMXAkBE0AjAIAACEjFCqHMxlLM00/5QwqnSVdrKrzIvJZ3+xtFEzTkKdg6Vwd1L+7cxjshV11x3w01fGPKtexCRHhdcdMllVwy476FHQy1J/m/5/mgFzQdE4pn3ytrXpkIQ/CyTOOeNoXxO9Lzb/kP2YXE+MWKUjbRrn6+IjfqSbcPFdCIdSjWpLDmSLlHkePTF2fhdNEUgCsMc6qCJsA95n3f5CT/qIa/2Cvd5rutcbhEbtpvWYb+zJttiJWYzrTFoRKd0SFtVKofURIy4pVMPsWYENKwGcagCQNK3YndDKwweq3IWZgEwkE8ZA2/UW349yJ1BMQ8VzuJ5AodlqDUVhc46mcpIWI/z981tvqKYzgzDVDYUfGcb62EH7YFchJkdFIpF2JnsJKRU2/Lkdv/QXWU4OzymUplk0K4aBE+WKOLVqiFRIfX3cFYOkwwrCbRI3NeygVpWagvCtZG2RAlDuT/E9gSHwfWmZ6iW5GcQ11FA7cGQEENqazO2gH9lDPZG95b7BrcjuQELAIljUBYt814MCqIFdJr+WQWo+NfimfI/V19e2ieX44aRF5TV4DL/0sTwFLdLHSbVAS2P19jVeKXOal08ZMfi1TqsuvgRa4g2ngBAOVbXuUl7oblvh7ouN8umtTd06+iLf4BWzWjdDdVqs5NxunR3zRJWR+nyyDJU4Py9ePZZQNGdwgYMmXVIh+5nU+z3Nx5W6zHmspuNl6mMK0sqGHPR9cZ10nFpSfToHlfXbty4XXBwrsVuYUH3fesAtdhZVogOxP9QecUXlU3Q6OX4uspLblQWKgwumj1uGXfBNdOMY7GGPT7IhgrK04NE06ZxE7YKJRyM1OdmMTJSB70x0hgbT9lrEcagDBszj2CvkStFJQEixy4YSeAhHpkG2GYUqinJie2C6z07t/Wrhy21Q166jCEUX99mPXp9Z+oy9jnVEWsJqof5u35O0UmaVNP5KYgPJp3C0KWFU9hJrWe/IwaamN4WQroAApF8A42rmaTM5BJALj80GojTOvarqAe3Uddtfn7x/wszZJ0PXNVRJFmjaP3pqJ6+gaGRsYmpmblFtQBb7LDHAUeccMYFV9xwxwNPjuMFFm9w+OALHj9O4E8AgQQRDAEiJEIIJQwy4UQQSRTRxBBLHBSo0KDDAMKEBRsO8XBJgAefRJJIJoVU0kgng0wECBGRRTZiJOSQSx75FFCIlCKKKaGUMsqpoJIqqqmhVp16DRo1adaiVZt2HTp16SbTo1effgMGDRk24qRRY95zymnRj0NEr6ZP208O6Ab1hw1HjN+bhszDlhHrqO2o/ZjjuHOMcoLLOLeTPE7x+oHPaX5nBJwVNGHSlGnnhJwXdsGMiyIuiYqJuyxh1px5CxYtWZZ0RcqKtKtWr2XW1jc2t7Z/zF7fuZG7iWjCcIJEplAZGJkxZ8GSFWsbR+zZOsrRjkH06xEUa2UgAJAEjq5fy+NT8R61jqh5v35bHOXYVi6kgt1eny/eYd9zgNQX6rSp5lva8iiCx4OPcVnJFv1IlbL3WM3G+2lXqtKoa1slHQe/Rp9N1F+uis/8O1yH4Pbhe+0g8QdNW5fpS51UIn/lAkvFwavLf3Xrtk6NIjqkRk2Y6toVVrCw05P3a5IL4rj9gY4sZOryb+hvGCljYy4393KrS2xlulxTjRBr9QcH28Fd1o1Zx38TO7YzDddRxZ6Y6ML7DGBMEQAAED1SptYfiAA8cLYHAGaumiP0D5g09I8um7CYgSdu3Cmi3gbAdKCaUedg0Hd2ODT8CsINXg66NNk2c5UACJZFOwC/UmfHr/JC2U/taQKZNm3S9J2Dk1G6SdY9ii793wlguEArOxXQ4wAAuXNqBUCLhC4bvogBWEIAavjaxy3bBQAgpZIhl1uxcggAoMBVAJ2rIBMQWRwCHWYkmnsbeolPG3DZxNHeOxHaewqDxO0WqlxkKVFz2H3lKUhky9coWhU0R+/YZbtrA6dWaFVajVanzdDatR7Lk0esU//3SP9RPV189A9IaEtFbXW3l7zLLi2nVX/o+X/K8AOAdwBAtOpa4A/n83nb4GsAAIP3x7vis3FBXMZ+GftLrB39cTQCCIAFQJHbAADdAwAAAKDbdJB6se90iPvUCj4kPLwFmzknoz0mTnMM5Ma2hQlnv/QQsRgSMqx4SgnUkmmk0NIxMpnNLJtlkGwxu8s+c0rErciZX4HXPzR2oN+7yry30Wit9TbYaLMtw5byNvxjtt3FxMAl00b90xVPjZsMgygkQ0LYbcAbX4ZG8RLDDz6mTSwzEUBnlByzz33/YGA0ig0j0QSERBhx5GIpJOEkUpFKlSFNo+OnT/ztz05Nzyw9Vl/1+hS/kMHEMF/jnJbJho1yJZ8D/D2302y1Idcw8kwVlLvuuTOIF/jvLVXApMQVcXknuQoafwH2Iyj6ZjQL4S03B9BsRGx5H4DmILL+PwCaiwQA+DggJf5y70fzkZAihBZMHuUeXQEAtDIAgLcG0DtAwhtA+jsA/QGgzwAAkMCIaYBsCLgQzKtwjzKmyJtEXZS8n9wl4vjOVAggNmK/AxP4Z9SPD0+lDfNmwV90rkKGK+/ZUBj1OhJEonWMWADx4cpqsVcux/frRCkMIuCnggs1+gPwfTsSG1GOFb7MixdCR2tK3DlZRyJ5EDPYmXrwkAvMqFATIEU3UZ2kXStWIkEk7JA4IGUSOAc/bscpgRJDJtP8OshYpzWjC6by6kXQIgoDM8TuJrWRm+jTxyqUTMGqsWIH55cFJ0RhYBvXYoc0Ok3zTmdk1I4EmQFjjJtKq9EEXabUAEEZjIBsshp1TqmACHd0hlhSxsUIDXeItcfnl7JxiAKY4QrNo8cOBym17pkwc0RjLEM/Z5os4mZDo3GLBzzG1jfjXE+is7h8dNFwBdchilLED/XBks9hkCUOsQPXp1cra7ORg4nVZiQw47q0KHNZ4DiN5F6KOXDP7eAr6uMLJ1yzKvf/3qbBbl9upUjtKivixlFPwopWSvXeOpMIvgd6+4eGBhVolB0YEaN1UOo1HFJCTAV6qOvJnJvvkKEa1KuO4f/dEAm3cpIYRK2/ZDgnQhqJc2ZKuYQARrqoYPZFyzmrCrVDpZZfStQknSTuz31Vg4T6IWgE4YWtrqkHg3gjFtrJWkckOIIj5Ue1sWi5sF9KjdPfK9b4Lv/jhpnnL3r8J1j+0XLuxNx1Zv1lhP9b7niRyF538te6bCND1b9Sjnzw+qWQEQ/RIIQLpE6On6mUilrJw+9zbklvSI0PaESWFBOxH9QaZf8KgIstBbdsS1htYWqXS2kq/N5BMpUHcir+7Id2/qqtg3biixQ4sY8jCA7QodXI61/CqNI/VBVOeSOHpEzIYbGrCnroppyn38ir8w2vqq5CEflG7KUF8hj0/3rWtLKteLHENspvmBK/6CI8Yg+oiRVd5s2vqOX7VhGh/a7k8vcd0svS9DTWg8PWJU/QEZQmrqeLBgfdsWtig+0cem5mqnWcnriBBA3Zcg+5LlKukbpbTI1ZMa3qHqrYsrP3gTxTas6s83reWBxm8klm2cyGmqCFtyg9EpOFk3dxsnJ/g2hNJmpKtExcN7qYm01I34NQsVI3wFdaC5ig42TqND8+plp+XEwROh5mTofMGkchKb7T5jxAS7RZo9UGLVfaeml09J+wtE0TyFxITQyDgQwGozilMaV4UbLqdRhTEEaQ/QLYYL3GLlzKO7oNVTzjnkxBoaIOBZ84FNa3IooPCeGH0no+02H9UMkbP0QcEllofcBtXp8LAB35kay4QphGuNQNoQwH7adhi1JLxJOKY0p8UFKHWMvNNwbmYAJ2Im0eEtMPXKIjG9yJBwneQigExg58bCcWak0kG9PUYyqsRlUZJvzBQbXjC7JxEKVKz775byJG54kSy6RO7WTqlg0MSOzt4vo1MBVVSMHjt7KWIq2yrSaCvaKraWffKoRkdwe+phE5Ram4hc5kl8JbCpbpAdGCfUf+uoeDqkpORcCOdeSiPXUyMhB9NNpPLgk5kLtSUzkXatLZEK6WVKdoRMYPh73h9abds2d4bJv0BIBnFQXdDU4ip47oVATUdXIwZwilNeX4R76eg7oEf8K/JjmAxzi9IfNn+Z6ab/kAY55QmEyfzaJyI21tknQkrzIvxZT/m2YYc2Koy2xbbYOE2eu+aCuqiiaae8YXPSMGyF4uqrSHx5wC8OT40063vZsl0Rw7Vi4Na88d749c3VNK4fUwhXmZBhzdPg58CL7pjlLBNYBzr2L27sCV7CdlBIX5BtoRJTdz0M6IciOmGAQP1+Yvihgw3YrEPKHE/MUjEUl6/WyRmU+tYwQyBNpp8R9Bd8lBEqkJEzdZiEwriIwaRLy6iegDbS9VU3reWBe9fc7uw5BAv15Kx+1V8DUOyb/2DqSfrybnIyANmXyIErWXFeP8YWWxT6QiceWkloFFcma42vGPhyUt4TEFJ1ftoE3OC8gjLBmjGcJJd2LjzSUnQiJOdK/KLWaoG3YyTCWL6dqcwdpzjVW/aWoFe5uoQRlZyK4BVgwKNZ/8zjZmJmGlmymZ7155kjoBsLNjT5UONEZ8tulmAKSye7ayQZ40NuACBHhcGv/VX4TKCBINvMZKwbNYIL16HXtyLTvvQ/mxJntl7jBoaV/HuB/yogHm55rzr9IGuoOG/WMfT+IxY+dVbsk1gw2t+IrxLL7WnmOAEZk2NvEB1kJON7gUflr6wld3kU6HjIT/7s98uirrVE8NvrC2kfFGIKQbPGrynMWwp9V1xPkMWQZUylA3BIxn/DZBX1VF9Vt+t1kZVOPRa4g+vWtx1y9+jDSuTP5V2mmfnMWnOrDIVN1VpaoeygtlPjEpXH5vmoX6vj6Fej4/cXMGTz/HQRMieRs7qi9hkL6FMh43+Ev6KCXU/annod4vT9z8p5nKurgNDRGgg/wB2vYwZvE+9L1tBfVadMom2YQ3skltnGBq7PDGlw3Y3FDlDujIg1USRzWjjkLXj63pSPz0dCe9Jh7Ww51OkLpcZKBXjb1h5nbTPqGBdC203DG1cjPpjm0J7OJTiLV5dlqNyeMNNwu3tBkTLiTrGH3id4q2rSeAnWaQ7VtZfO71R8SqGwkoJDMDRQNU6KlchMBKPmzBBskP1hATfeNcKxQBmin8J6Nfk5D6Runhf+TpNnuHxTr+7b2yTruty9rvmOo9e59V90bWOeGag+8ly/aQ9Y5Um5RZWSUIwDqQY8spalfLBeq6+NSzAJ+F+MtjEMn/cr4fg4NjbkBrfmldON/LekZpH1lv1ZYEUXluO7MAkWiSBFsy2aelk3/u2SNk4pk85ljPFLcnvakMSW5ZTEWYkNwAE/BFRZg7PRG0SgA3OkMeuclH6xwuoTGycLE49aWkIJyBnjA2C2wZe4/Fqk9laX4gQ0tth2W0cOQLvXYeIdRrVJnoMue6dwyDvfSFtdtmQWXTs2gwmV6OZsMl1W5A0hxMy+JyAwz6i5Jl0bPe8afMIx+j4K4DZ4MJBVGKgJUsh4hFKnL3ycNKBQqZfeJo2CNzP+nLUZEPa1SXPn/2EPvOU0Hs+iM3eC5u4x0VchYTz3zklD8+jWAFnSJjJpW19BB/YHJe9P0H6IIdj53TavJR4EeP8pXV2i/aO0kee/6SunrZPUe1/EQtzjMjTAPDhEXfiYnos+M6iEh1OIkrhrjtPBIiqoxmtN/6Iyt4+mNneRzkPlUCg0oj9uK2BXPtoeMHD2E5M7kOouolQsexToR1LK/o8Uw1lhpAjIquIuruqalVIF2tmK+GTFfLVNRCRvPbo0FmW5pAk0lCqiuWWrJCL4PqeAH9uRe9ByWzSr7gNf0hhqmFCCk1D5uxfH/uiox0Ppro0iH7Oex4+VVQ0958eQ19/mraMV9/cbMAXxBS20PFUwe3NAof5S4g48mbpSQ8yRmAD8D7WJjuYk+85+YCV7xrzBnvvHDZ4m1jlnjLqmJTvClwtNyd4H75mW7xujzAYrdcUkT9nAqcyJCTpMlx3U8919Te3jZYEBiQGRwXyOEEBrOgxQVP8GJ6hyFbWp3RHtz/kXeLpvee8dObNsMr+tzmekolf27Tm9n3XNRZv4FJmpxT1IoMV2EX79Arogahzmfm58De3hjHxnqzfXDmZnvjfDQsu8XASzaO4RFEcHP/0cONEOyO50+wfCwWvkb3vWaHKLNwO6KZuUvUaIRuaExymGP0f077S2f7SNGZnuVx+egg5npB578sZovdSiqpbkUA2ZoHzegzcIZuBkFn/A9jrYWrHNG4qKDhSrZ9cLfb0u2TFblLVxKKfmUcCOfCkhsSEzkjA1zub/R7tOZYSgO9VN5bdQ9cxzwxvyqjHR7KaOZXn/yLVcI9Z+t7XFH68sn6b2cqXBE3EDcqXI7k60+UXnpcZuvjnmOVwKX7VlcX0ZH6tm6qtliGs0X0Xeg3ZhsaS4wQpAVelUUNZ8+Z5iN8Txt9uJvh+cG6aR/u5FbspQrG0YwYz2Qksgu1JI9TmZkBTpjf/yPTkG4xQH8WcuDvs3xVOocu7JiJxluxm089daIB2a1uRXcPXgjYoGUayzloibQlPKBDfWnMgeuYyZ8lk/tUvdlDGdX8ypN7utf8RmhW2OavJ+Trvy1UuAzH4usv+4TI5Vr42EVda6vKVWNFSBzOCWXTHyEwUXOWANp7jXWot/AHkxSmAFISTu00sEf9LsU3PfuI6uVudyPJNF8kGej63WFMFVPfiNtfLiLW+duHTapI4o7gkS8iUY/d4cGYYXbm7aztr0qvr3pa/yT1rP/ppyZSdHDTEvWb3SfsXp9g7p/gQxvFx4nW8ZhpuM+uGkCK4W+kMTsGYN9Af5jeYw4yjuW2pX9+iI8Z63/IK1y2tytc/zizjrkI8Aw8M63mUMs4yQzemVa1L2DvxAahmVZuXtkHnBxuyo+RNf88Odzk2f4jNv7lAfbizuOdR+9k/GDs8h0h1JZMPcNzMwLtkWL6BbHYcMK5henzhlcIA1d0XxbStfPS/LppNGJi9ku/1L0y/n9wbCmnRGFyItwOCf7kGTYtYDDKjyiPjpdbSuVRbaSjHNdCM0D2qdM5qPImygFlwkySTSRmZ5eEmPeja3T+K5nFf/YEL3RkOM63xi+1k99ORJi+t4kjyybFDG37/v8W36/epAh6UiJxKfSAeBsnQXasUR4xnGGd5pdTLH+Q3NH1maB7S7eaoa0pLDl9mV2cL49NaqCzeJlN+OtO8VyJcxaFXJ/GDxtsEF3CA1uALNKBUqiTWgVnkyf+bOr6ZXoKNjfFb3QVTHpHREx6Nzinm5rh1NTmwJ/NwLXYWW+nMruWrhoamxVST/JyxQr3EafHyDtdRR+5ilEKy8O8tjdH2mezKNgxuYxioexUUEgVzndugThIia0EDP56trKbfqluvS494bzWpt3+7X7waTrmZUDy/IvZF8mY7YB0YDUQt6Y6nHr0O1K6uXeGOxnesPa2cyXRwS6mepZNk8EkemVTeIw2X++x2fv6uw8UrwxsbLXOlFpWJqBsBj8dWxzaL7MRPFHP95mJc8x3PvnujIlRdI6Rc1orp6TW7Gzohs59U4aBugBL5kd5Y0Xl4cDO4Dvlj0eti8bCb6bf+N34PR4VZtVU3OCCdLT54xFkkPcJy9DvHOUxqZrRg5mwfrhWkD0uj81ag3h6r75LX49JA2Tv/29XSDAf+bAvCzF/RvxuUjoyemcgQ+0cywz5yaVjW7Xycvi858V1tSvl10rwFUQHR35dzTwxiAobD6SuyQv69SBZ2TfRo1JZ6hiTyCocLu1inLsjH+aFSiWESnQe0o6HRnb6b/elV1VsCSxq2GiD1Krq84GCme0eq/o5pRhnkxL7mhHZZb8iK0iAIB4j2x8WKkzOiMC9vuHe20MixPhUtsKHQ5kYgGyFcUr0GlzVQg3zsKOd/ny+uq5aXsF63tXBPJTXgEDM5DNZLlA4obCU9+6CZGLom27PSoqVq2Ajv7z1bDn7OUKm8OKW6n7JJYmniGZDqnvRBI6VoFOseVXWPbPDnPxwKFthalyEuD3UO3yvTwRm/iJU2B/uZtTIu5zuYj2XV4TMQjXteTuGLtT8i6Mrxc2c/r4IxIwhnjdIf0ZMIqaLfnqBn0RIEU8SgxJ1x6vePG4YUlOlraoiZsL77KPsZ9VfLUwX/3LIH2bu3fnt/sTHP19R2Fa4/8e9qQ9/0PKmErGGKUQsCMScPGwreWcaKn4XEY2OveiQqm5xzZBv7x1dvvbg/68WH/x/TXiryr8ozIHY9vMg0wrmQeDwW2CaYTUjY75JBjFnhlXt1LiRtT8vWM7450CB2ahG3mwvXQ7XcOmMqU/kx+fIdRteQRYEDv/FnXJpF3pyI4/jcrZ56b1TYuLjCQOVt3H/Hj+WwNOuzzqzSe/8zG5fsWDFIXVfSMipx8G09lBmicMYOxaxXvblTUEJvdUtixVZZTeRHFOcXgieuJN+XTHiQduytYLFp9pK8dD6NSy7Cej4bM+//5yI34VOUlonh0jkn2Grnr/XHvf5MXWrleQCgjXvB4JlL3TKnhELoemo2rKobt7DwsYrkn76cE9RmS6o4fU/B/E6v/kC9P2z3aNSwtP5nDYemEqjFIt5q/PJ4pN7VxcQ3zOTAT7uUInnjnjWOTpdUT222BNpIeRPzfCyE8ZjUidrEF/dIhVxO4FrYxybKdVX30l7tAMxEy968xEL8sJ3ke9OTb7oyXs3cDgfHMpOMw72tZ59clPpTtu5Cvaz7sMDdWAvBXOGVIaMGcZ2WqvYdrpkJMCZUm4i7yOWF2w6ZQpMTT+f4m1OTzOnJ0+2NK2Pn2IODjJDNjlP03VmXKZKM8wUs6H6e9F1oMTF0ZX2ygxXbzsqF7T6YB9q2pXd6kj7X6J/GO4p0eOAEgc6bXEKverY6JaNUtcypR+oJK1BPYJ/o3ubah/JABiiL/08YtbM1DZN2yvp6V+qEvtjo4YC+xxOOsnjQlMjWBkN5yOLS67ESTtVEpjaennX6nuaEfJ8HtYvQuZfYzHpeIERkcvofzEHwtBuPNWz8N3PRNjxvQvTfW9Wt47BCJUGAr2U3zI81MwfhKaci8osVz2eI14r9RU6NDGp1ZNbcIbBny0vzFpKN7le6JmdSO5Muv7qO7Dh1z2lUkEyD5EixeT/4f9kBaadTfiYZZWBc9oAnIPADeU9LxcGFLqfmWLn+Bt1qMSDeGC3M1Wm7I0eB9IvITOm8lqRr9t5lkxZGGEfsfYmU6f4SFfNNa4RzzF8ecVw+wr31QcKDGamMd2XCzfyvV/v2ALrQJaTCQwuWS0bCkueFD0RJpzvZVbVy5qXwO2zj7CIecxMJ8+00ZiA2jHm2mQaA6uHwzatwZ/PL3Q63OK0yH2V1uho7rl945BTbP2LrhiA/fQrSIDO3lwC26UT8iqYf/1XTkrgJREJ4THXq4f0j56zoB+VejzS5SNorE/OpSZECbHpC1mWcZS6L37ICgqOimBG9pYBoqY5UbVC68DE2N7aADDQZe3j1TVd7xWXt5+uquk4XQohLTc+gS5mMCk5aa8EOL25p7sVPfueB1PlEc/qIiFoxxpXfO2Y0l5OxzdAiRvHy4uD+LgV/8ztxIKyi6m+vFZAxJTuFRfvleaVFu89sCv9i1HCSJlISppIyUu5YzcpZWMArYAs0J5MvdE51re7dARjMiOBjhvhpqdMT6ekC6bTEmdSYEXp7VslFdW3yotul9Ktm2IKn4ynVWRC8B8mazGsrBHDi0W7lK6XlSjkKYzxqXMFK9duTbqko/qPh6REnvofmrOctHUn7UDCwJfTnZL7O/nA3GsDWiia8uzrUuINeGMJLnkBLKbgMq9hCi2gGBpxCxtPhbPzz4kF57PzJOfOC7MKlrNx8czjLi+P4JfPXPb/IzC20wobntXVP2usr3v+rBFkYxonqK7PrLH22eM+NDUeNgRkhzt5cS6Ic/LPSQTns8S/B0m/GOig6Gqs88YGRs55keh8To7hNDHnTyE4tG6KusDSYp0uB7hh6ncycjbre7ofDnm615k27JUWC+cucoqLHXaXVVxavVNvWjHs2f+wpz53czejjvHe9eWp2Svy8XFos1NZN/Zr10pPF2zdHJ4HsN+9YuYTXzQEfjL2+xLJJ/zzgc5jnzvqV1IHvtzRL4eY7TRaJ5vApXV10DiU5oj3iNjP0vwCiBlBJwSBP/xuG9XOhbCFQmlmQHpbO41B4dfW8xNqa/n82toExwLLBL0+JqaenkW/459J9L/0NAqrrJyZR+8SFrO8hJVnKAdWaMczGbz4dyG9EaShEdEni5Ks2J7solKWP98Px0dCWkxtfSSdXhcTXUfxNWX4RAzKsitsqSBNOa1faMaNRaHCssgkkIgQFiYmSwp5JuLR8RpB1S9xilRmCE5gOB4Q35nKDq+viQSaSefkVI86nkddnAZdI52afg60CLNOV5umxurrR5VxIpKq67hBAenpJwIIySfuRduYfPR53JUkw/P/sUv8I+hNN3zndHprGx1cRsx2Oq1jiD4ValP4qY/LLqrNxautWV/D+7o2m2R4zHuO+j/V4KU3GESwlhRRSucntiZjvKERrIwpGKgplPbXFMTAKo6uboplUgufHlFaS2JQ4sWpCUnZyVyuJDkpITeVfZeaQqUYdnIcJEp+8QL063MWfL3kSP/QjPRwP9scq+vVNCwAmG+V9KXZ4AceU1g9lh9rhIJK8KkkbaJeqwQlZxlWUjVL0oXSn0jhweCnWljLENyPSOTgroVv4R8a4PxPoVKe1j8ZdFEYJ8EkERtkkfTzn80O4TG1rp2hlpl4SHJuVbg8h2Nyp3Bd+i61sWGudT/4CMkpsTIZSA+PbxviDgH/ZQHsKNMkbKBl0pd/tDiSYxtPJdMi09fbqVVva9aHxfDpVHStjQlzq/+kGR6eEiMbzyRXrd6ys/7P2uZfa8tDG+tPQO6yXwLWh+vnF9dwyvjhOFhcxsRkAq0095Djk58cY5hQ0KMH9da18U9WMOuAczvuXjBkXEwrk+8lf5VTuNrO0XEhg4NXxV1W2d3WaK9Ve4WhFVg0TXCNkZYEmukTioMDaAuJeTbb7BhFxRh+t6hpMYwv+OAD4oTzfA/ym6Z92JTFhXjGtF9uTUl6NOAnnmgLixt4ExcmPXBtBjib7PJy5oodnZafhRbdY3S7pRmbLYZi78gI3FIIDI7v+2DICnaS4y2GYHdz891xERHYFXkJMm6FeChLe/HEMnS5Y4X59gFqE6i0xsAjQFNHELzb/PeCx4DhpIM0dbs1IU54V0OaLYmGHNjaIt4AvRZAYI5cO6aeNKyupYE2eeKf30sm/VR6gWSpJrUCXpZ5zGXm4mceFR5VIo+tKLiVm6SqVvpsutNpGTJgf75oWIyiu3eBcgteCIXvXJeNDhUN/5VIPEepKD00IJJg7iS6/IhIdl0yk3O7ZKyExibJkyYciS9jJ8hM5W5yYtheVGTL+PndYnAIatH/dFUmK7gpjBDe1VXJrhD4jRvLA0nA0P9iv8p+f+aRwqTCrOBov7VP5SK82Kz6YYdb4X1M6aCS7cb6t5hAsi2+TJP10C5LxKbJZFkTS6D1yIATptbTQ1aqzebVq5t+09evrzPmtT1xxmAf9y+/WVq8X1JedvNmcWTvFxfdLC8p2t8v/frbscLfWutA9cKn8FMkOeZz+DkCOdd+mNi7bFxM0bbp/3lx6cKTtf7cuvpTHm58E86PeXny/eSGlkeCli2daoq2pjD/9Hl2nmgyitfISqytaXfBsGe8+s7IKqQXHqUBXK35Q5vdPobiA8XP+6jozY/hhE2onVvk3RZzMvgA/X6m3HX7ZpnT77PriG6FaoJbeKjthK2vtS3MgT5fBEAC/nJH/KhUv5wc3twkT8DuiM4etZdSTDwHuzuEFQOjDUfleYJIF6EaBONR2Afvq+NUvb/Cj/fF6XyjybGpKfg5DkpmXW2353Gxg6KLDvZ3kCN75FADaBOh8F8YfD0glGEl9HreFejfI4HbJaOL8KIR3F6uqjK8BgE25chX5JjuzXN0CcTQX2foEOxFvCAiPnsJOJa3Y5x2hpadmE8CId4HxNc/BJpzfzkI3LvLHhkeGgE/bbKa0tf1dhvTppJTptJT06anU5Lv6eTkqfR0jxNPxlRGdFVUVFV0NDBwlCrJZQyc4p56H+xG3D1dVEq3I5zCXrCPOEzZgYhEwl6V2Oms/4JdmAGc+xcjwNd+PKaZd+d2ge7EiFBr575x8eqWUeW5j+IU1Jq0Ls2uVqTVJujT2BhiS3AUrSSUVE6FoeVlJAqx1ceGzGyNs46kZKX7xgf6FirSVAaoP8+tfM8fnPgzV36Z80NNW+ZPFy7+mNmacRsf7WUTkhz0OKDBEGakcJOEiTRuSgovMZMPaJiyvZCQcDtsh0I0IDv3HrlwfX+GTG67KDNlie3qJDb0iZDZ4Hz9Qo1ibcaGlyVovzS6A8kqtJIW5sR9Up63jvyCijdHfM29kayVtnp59dTiJJsZMXStxyiWEVWkF/QmcEfzsyAeTumEZZ462lYs5K3gwXoil2V4flqgM1JMPy8WGybOBTPnDOVA6ZyUAC8bYMK5a/iGaiMBU9uQmpfU4Rwc24ijCWIiqJQx7zzz+DvKWUKmMCElt2bZm6+ZVIKereCROLGDnlyjIswmSSLNEgg6lslWHqigElzsIr+M6hYJXNEKJbdTlcvU1uGJRe2ugVFVXtEJgQQtpDVPuIFvVbv/ytrayvqrW5Z1lHDGUI706JGse4mBc/N2TaYs0JuIJVqNRiKirIe1mi/znkAOLsno9kYY7Rca+nO33169redAkGCTKGZVAWGWNRqeEi8CPcTh12drDExraMSDO2nooPcrn/BmZnjPqyp5TxJGvLLquZCe0MF7jtkE8GkpC+E1NeELKcnh88DAUOZ1z3xySvgCJiuJIVrKzFjKEmUsUhNlQctcHLTMJfy+ru/YwYE/lx3g4B5Txz5Lp0yXzrytN//fytbftB741p/7IcoJ5YSKQnxPJRx7EIp4QCCNasEcqDEKpp8c/4MXnsBQcP7pGlxDhrx1z8nQw1dvJ87z9/4P0m+5PdyDeschzASo/3sw8ebIxkHTGQF3n/NIn9PF8OSFNy+y8fD9n2dh/YMVOpz7/W99Vt9sd/z7fR2vtx43Xz7XEfbnq4xTX1jHqw14bXzWEfH7a9Zn7Lu1Lpnst4/d/bxhYlb7Qr9AoI/wE4A3RAdAGJYvAVRZsrYBq9rU2oLI3xAy+WoLAJSVjraliaXFbVZ3KYwdlyb85aJm25zYXBrR1WI9Sdmzb7V+7zHr9+3u3r81wOpPHZ50QE+tn+0J8AR/ATPf0H7M/rG9zf5X+3H7/9lPtP2tTplOq9Ehb2lUZSBD+1lAg24MBezXZAPQX/oA900x5UaMgk6/G1w9HLaLtstq72b2q57/u65RB7VP2L6Kt4NgsDGX1VXaVLvqQedVDMhf6mxT6x9KPgxH/GggDB10eQBUhIHcBV6wdTl9L5e6dKyIQtRfKv9iPSK+QoAWdAOQzwMajCL+37G1OF9eSJcDszUFABwExPJCg6N78hVSeNVAOg+PEcbUulsD6OXGCwLAS2LqK6dieY0bkJ8R3Eht3k68WLuiX5MtBbQFwB/VyiRh60Gye4gp2skTZPkUPLIkyUVU0hw4B1bQ4637c7Q+/xV7AbrKW2gC+Cn/Dbwe1gTKu8ABOnGta2+TMo9wpLyX0OGJ6EgpTo7eMMJGKyB+oB7tMRlrqS9N4lxrOf8fnju5i+/SZJeKFgW4Uu0i67NWIDo8ht3UwXFlGjd69/rUulVvmtSBWG8QGkrpvc3MZLFAHCeqJjx02jQs0Wb1XhWN1O3e7U302wXBMVqO0eA8B2ceIHEd/qZdWP5T6d9OPW9O56EBQBDAB4oDAPDBAABw8qzWrJ/LdLJVqgM6qykz2vtWZ7+wDvvU7tioa32uL/BlHvDtvt8/8St+28cDyG3j/fhV9CZa4EgNqSUdTD1pOmfnQP5XvpwfF2XxlNryp3K63CiPynRV1qy6vV4bNMO24eKYPf52Jo5xzapnn8wezXXz0vnB+ZV574KUpC+2Lf666FicX/QtHi2eLF4tKWn60rZ0L3+2PL+SyvJXm1Z/WpHugc4xnSCdFh2Zzp6ugq6PbqLuad3v9DT08HoivVN6D/SP6evru+mH6HP0pfr9+vP6a/rr+jf0v9X/zcDFINQgxeCUwZrBc0NDQztDtqHMcM7wtdFxI5KRzOgQpYkyQKFR1ignlCfKFxWECkP1oU6iJlDzqBXU+6gt1DbqNuojY6Sxs7GvMd+41fiU8U3jf0xUTcJMykxaTG6a/GJ6wpRsyjcVmhaaVpt2mo6avkK7o8PQCehi9Ax6Fb2O3kF/LEDUWblJyns5m8E8TZhC+GGRSQ5SKmlEjAwFarQcIcJ3YhkzfPykm2CGEps8YI/kdGXhkYxr+ufMRDrq7ukts/m2lFRxK9vdkY71bNF6bKVqZWSFsfKwIlpNWxtZ46xnbfxsKmxGbc7ZbNnctXlo89bmyFbJ1tDWwTbOttC2ynbUdpewP4JBBSpgD0DDsN8ScruzRxU4wynyeRDyLuDYa4Y342zT4x18V/B+802UytgiiC708hhDid/pnhAz76xwSjS5WmvrsUWt1o3W3aYDtobEeMvLdWZeccp0JJm/080xLwnB73uVTjJdiIhHYUjSvEH2UI1jKJFKLE6X2/wdZvObiYRME+nPapDNO/GA9kQzzjocz/OI1VAtfNp5Eald0bQ/Sr2O0qME+/BPMEB/Lvi9UHTiBBWq1xNIxgmEBBB2U/MU/FBFUQVDMXFjqTA6xgGU5Lk4fwoXmd35HYG07sxLT4xbPE+JHm0AKb+6bbdUsvFwZD2T+8fymmnOHV9LhaSQEBjR5FRXOLq0zahdPhvZsUukk76hsa7j5Zzv9RZG04E8syspIHAMAn6RNzFONmy8EicIh1i/JAhPUP70kCNHzjg0fQD55+Ifn0HQd76Qpusrjd9MwHOuYzdCrI3svBOquu/slok9dvMuFxRUUb22HVHXFdX6Khvsjl/0wB04tZB5ScksxrhgbYZ1fcdinby/V0P0k/zD37gkXFnwM0aUrybMOjcxVJg412pZarxZvzZbVdHgwo0HcgjMvtAfe/c8Ce+GMSM7QJIhRqxqiCQYbsEUPL1L7s8QLx8buSqghHLGMRnJcXJzGosn5L3xfPyVomzqWMztk75iOgvkchPff0hPSoKSVXp9JlPs9P6rMAGusS9NaUlcpq9EY+QUp0o3ObHPdQkK5YhJhaxxVqlQAavBPxIUYBJHodsf4aNB7xF3IYPo6UfgC8Db6xnkyEsVJpDO7kfK9TFNobKUu7g2UPNacN62KCYILwvnLFay16asH1kttvnv5xtSe2f0IOIjhDQh4xsKhXClQc3B5aDmVh2OJvwg25vPxIDDO1z52+ed9DEoZNSqChdyXr+UlDbOpnDFS1lCo1I2AWF8KW1phZCDdGqCKKhmZcRLC/rEO/KZ82PzI4MbsgDdZDvvKLwv3KlfjffffBkJzg/Xzc9ThxiZC3SbFBS0R+/FCjypfbs+LwNrGPsPiIkax5aLDTWRj8IfwgpeLKdhVyxWnc/rRy0siqzDvHa3TMHTuyVGDOjHFb80GJ4w5qeZEqleTDIEPrc+xy6c3A52IG/wq8BgEFKOOUZFxEngODhEpo2YdCnzA0GsH+1vSI68EX4BLoPld0rdd7hQs44r9VwjBnK+nWhgwkggLXbDO6GMuvZE34nj5A1iafffEXRow16QgSJbwUvdscBhRVfr1DN88mJIiQYUNXQOyGnRhljYgMXnlJ7yQQDdeimFN15YWwyybcY0nYbmIwnYhPrNiubgDlhFZYR2GbXnCnUc7ghCsgMBJNJYHwQxaZ8qjKfsjuwfDvqUtqh/94b9sOw+tQs9+gZvBHFk9g1pfM55IAwa7+MKCgonKKAaiGiqj9p5ErKL4o2uaXwrbGmqUMIXHdsKTV3Jsy0OhH5xYfae9Jz5CwtycyxuZPTQ3pEoXfbUIYR5NlRDEmaBg3BkGp9BVkuPgsxYCrui85ZZ8lqbs5IXNCePk5jcZwG+iKYSy2R4Ki7MLRQ7HoepAy6NLDkL0YhE51OZ7LXJVv3+RSf9CqiDWaqlTeD8VbrmjPiS1WJqRDe1tFWykA/cW40MWe4P89KvYoliFVGhhsQzk//bxliih3qOtTqg5OdBk1Atdeee0X//vEHjogf96P24iuZtTWX/sS08giIGEY7ACspnb/YIvmj/v0/5OyxAauPuxakL9RB8WhG/15OC+6YLyZTEsTIs3aFoyP1qUl0bxmdYCo+uK+77KyPXHk6fGjXoNu3ws8bZzDw/rW/QZZ1sVOXOPAwgUPvM5R56xUb2coRBrGj4DikylBAc1E5DQm308/KlmOmlaQ3JiJMW6cIT09KOl5vAtM4mhhLrQInoFyElgQutWbPNRuxBiL8sF6Eo8ZYzk6dc1ZXPb6U0/iWBiqZ1ZzKAO6j91pj6RVR+BY6TzW9BNGhmW77zKKE5LUHBDk61EO5ZtZx1E2GKtqNLH7zn1tYrVYv9ltLcPLvK7GX5Un1bII5OazilNMR+xgjX9Udo8fqIbgcQIlutyj9a9wNC2zGGIq+koSJAv7rPgrr48BPLwU6Vuo1jl+1S+57Qo3wEk5GZ0TrOEKyB/TMampnl9+i5dNRvTaaiURBphROZoWqQZ6G65rRzlGvjZxiIOBzTK6lQrZ0ZKFYrx/WaJ8Miod2rk9OwO4wtZDU8kO4Q36LDVM8xiFiLrsHomriMjGLfIlgNDYtNGB8FK5fYxnDuja6ejT3ptepGjHiVt8BKiqZd7jqbfNWLiFcxulVGVKghajb50aHyd2XSSGaFPDCO6yTcdzl93+DEWWoFwSCOJHzmYpu7I5zqt8j/h2+I44Ve4Z8KrhKIzDyBeECqGVk9UsFlRU1jdCcprfsOFw9RPBr4bMp9UMfTUcgFnWn6927GfJKJSaVAEFU13CFDgECbcJWwgJzdC4cGvTmuM85PdUp0vcJCRAlL2SwbIUCxerVY6FEVZjwwO+7NgHGLZA8L9nwHhRj3HJ42xNZojf+hM6jdotCK4tgIjVTHZzk13Zu3mA2iCQt1TiRoUyodCEfd5e64Co65Pu8+28LPwooRxUxtSTWpGXMK1J9xGasvUh0MkC9xUqcRk5MYcVY8Chw4+HrNDSZtl9bc19VDaoZmFanLBXgsgrG/bmzrQZ3A2jXqP1eEeW2TzoNzG95JqGTM0DWiN14RLUz8zTKSZMPex9w1jp3o14eaqLCFkXeWPKatrNjTKHFlGAd30ydnKp5eNGd5Xb0101G5FTK9g9Wcp+KKsYGOcA8RT9nVTS8RhMXoirAOuP3vK9uylFiYa3Wtun9yLnZHqjlvvj3HbHIjQYXUASvpjlwZRoKn6fmjS2OkatjP3oplng9zRytBr6y+0LS9gvv28J0+jrPMSV9uP0k8F317W1enWrXd3VGR0a/tV7Rt+tdGs5iQZthvKw5qQ6plCh0ot5EiaZze8lYio2k/mZ6/YZ39bRcsdYw5szkZngkJnob8/opr3JEltpMkCI039EPTdj/H0vDDeb8XK8dlXx6fGwv4xsaHCwsFaC+K4bffCBOzckrcrxQb/ZNrATVxaRkvRtSLqbhu8jXcvDX2Oe7GvfFM6/QabVqG5e2SBtbEQW5Ri9dAQIzcC/vvUDSyJM3bISHC5r7TCx+GMfkbfGQmRBLku8ZoMz7SDhw92D7qPHxAxFUZbckUn9kR9CgC2urWxjTJDMzEnjG/2668efS62whAkM97Oz7UuWX/6vn4k+E3YUxKCH/+EEK92noD6uTE+xukD9456AdlVmvONnFQyTjL65trKQ7M6qTyjlcwyxt26xisl2Wir82aNeBvIIlpam1saFe8xb7AKeXlN/uOj1AjFx8vcVQMeMIoWT+YX5hW+/Gdj8OYUZ4niRBrZoef4b/Bd996ip+SXCsK8OYejokj7cIbyG/5dLVGygMvUau/Fym727o9I2ed3u/vtsljSVPfwPMm1esl1vDLKq8/2sdczmtBtBwLCH57KsJHHPaZe8ftrzi2KRD8P1WMkRhjF2rU5j4YdStSsbvOMWhY1P6HFD7D5xPXZJ6FhFBxvNm6aF1TXpTKs+Ky3Chm+WiWJ/f/7CoVFI7hzzPgDWMK4DF6KiQYn/b7K/p251Q6gTd31JobpXBwrbRHyHxttOr/cQWWcmizbLGqKmI/37IbsYm18kZkCO0NKO21kvxt9y3/s7HYW1cuBcBr8xEtFIun891W1TSrAXA0odYZkwV5P5QdwjTBwZuFIa4O9OlbH8xFTIT+FiNHr9abXERx7a2aHRzUkSTV/aFmXdDTHo39h+yDQ8hBDJlsmhsAY3/j2vAT//77H60WQ+yYr9S4xnHXKLFOrZzXZc8nzLfVBxhmHCdO39oxaypSjjEJhToaxf0fRyvM7S3YYF8ouG4YbeGLSvSl8XzrGg5Vfb+Qr++OwWHNGUUzrbr1LPh1tOfOz3J+IUtzCgf203H7qc9Mk2hc06ojpRA9raogaQnvCd7pHuOFkxJGDbSGXm5zcW+mlB2t6+W80xHITuNSg4xT92nYHDDbIx58XtXoW2FOR0C3C/UW3kZdl9YBtNVMCrl8SfViHBXufks1vRzB6grhDwW0VWQCyFEcNEU5KhO0vNGQg45YDZgJcWHFOT0Nyd9RUFxvB5kNTywhxZD8jnQPw3yOXhh8aW18bkbO/o6afP46Ju6fjjrdYxN4tkLV8ldo6NfpbFfnY4qv4O1o4nex5aosmyuoem25bhRoUZY/bhtoh5llukEs06xzEkLl3Bxr7o/q8pxdsD7WDktaAwH63ZVtDXwVzojyM3xIMOzhEYJ+6ZQVHQzV7UHCA/wSgd4NIf61wWqv2i2wfzHdFywrvC3J+ZtVe9b+lJtjPhP3JMtuUFj4+ERnF8ds6Z2KYU4CTJ2079392VQCKMZ+8uOF+tW4RD5Gm8mFyKgqRJAUS+a9SwBmZPY5bNJtqAZ1ZwuuSxu6nwdMZJUzrvuIY6ZEfcCFDfW2kYr893ONwXdymjfLZc9NKITWX9u3yxP+eXNyFi5w/mjqUmWKaV6+zWIyuhGjRFJ6JZTfOm1A+dXNSx2W+n4SLKKQNfARrW5Bbqjvu7D16kLws1sLS7WUnOKIO2vFxuoWkPf0e9Msa5PYmOQMJpSomnomzhJnmRDO2YINSdXx/OkspVSLXm8Btzz6LE8YPRSXs9gExXFXCxUbKKzbgOeRSF5q99C2gMyn43XG7Lm2juJQwRXzpiD2C+MA7cjKxqjK+CRwB+/yNULvSuTiN+1s2X1vzo9ZjcMn+xJbIRLx23d6uarUadaRmWfsacMcyyxDPE922BP0qM1be3u1Qv5ov/O6vTgl7xztKS7sK4sfxRJoA8+G4ckXScGEngCHgEYXRCM5rGBDOlP38/gSo6qYgFBWHJjFnOPKMd50sm4/azrYabH3yy2mMe9VjBVJjjsiUyFmdCp88zHpdCKeVV074gi+hnX5Cv6P12aXrTnp6EP6wPba4R1lz/w4QycvqQ4ZtbsNW8JHRXtbjs7b4j5/ctzgTDClTbv21gdC150+RnWXSrXcQCT17Ruw79J4alubNGcmMHhrMZKfKIVcsut2f7Lxwe2qKXARNyc8A8jv/PH/a4/ByFcrhWDF15eA81h2MPtQcXptV4ESsOwGUhPS67tGs6/Dc+8aV8FSXsu6GgPWGajf9e5675Rgv3VFg1FRn7Kp/ICzqyYbkoXDtk92uGTdbbPKXAgHZO3nIEHirNcBIekoDQNhHg5afzPiPvoE1itoup5zErLco39YIGzLS69M5mzwh/hIykOE5cRtVvd1dGEphaC3goIIBzPChoR2lEbTQU4LLNQ4uL85Jv9X6nrBhc+rbN5aXMUPCJdFRqMLd15+GUt+XFktWGiYKqBwEor1rou4DjFyp6sYmeRzaITlfa6qVtdRQyDYeICKjidDpnk2EbJR5aa5CyJfwfXOHX+B72WMRBQXE5FNiL16goD3/BB0xILP0n5CQWIu8FqxEWud/zbPfnJt5NL10UQ6P0mfHh2NkIyRx7NqYTyVnt9gIrOX5xYWUlM35PeYZEyDiiBWhog3FUj0xaR9c/78+YU1NIOTtBCjCBY3sXwNtFrJ+NLlxdTiwvJihhRrA4SOLJgWkZvABPGLSTAMqEskKun08v1z5qia4OZTGAXj+LmV8mk59NQPnyeYO1H4g18RojQ2VM0dIbfOUxZ5Zsu8Zasmm0AU9VtU59LYuV6rqNf8cNSpn1b//X1nJnJt4nk3UgiIBv8N31PbPN3M6lncRuq4U95RQLKkVS3KmukyW1UYEUNQF9xTE43zrod4jVaFS9tBSFxBDVI7QFAdY31x797ofRainnmJD3cyIDFipN3XJt7RXadDFkZ31G3d81RoH8eNAKoSqSDkXB26Z6Y6yrEcnd/nX+vawNtaNWopIM8plbRiwTp/nnmTuPiWUZMxeZxxuR5FBuK1PJ8FdCeqKJqj7trq7CpcbNujkYEN3Mx6GjpuHSbWi2sn0hmg5pFj4RAQ2ekbA7Popk0ryC4GuwI824+pcweBasD1REiTsJJrUDe3AH+LRZLxZsc69pU5jIkjHKiF911Ckdciuc89Spe7zIvIzVLDU3eiUhVLqkImJ46iYxeZnRDax0wkOvee+i/gMv7mzyg7+M2IFxQtHKSIxkBkxRC75qX3WqkDx4XUkZBaXeAQLpcbjVrEzFCmHxg/mJ9v0SmppMGa5x2ejZiyAVorfWPsnsB9Pupgtk6OYK+63ubp02GdJUrhyE7LeNuFKGNwcyGRmJsUKT3al53fDNK17Y0Whme1Ql0ysUivUo50cTXCUVItcT6VDolmcvPCDGLFZGJOZ/QNzxs9tS901W4ZRtridQJ8KPoPCjFNwjtupnAA7lbydkt38hxqmSZzXQgw2UybycgpS5DWOBC4O8h0iif5tv1at/UwnJOKUGm/Rn2OkhdrdmqNnXohDhNlgSOgoW6oTotr7MijOa9IvJCuDcJlAX2n+EVcPwHD7x1WFJthZJB8rPJJOJYJMc9j4ZXbRkoJxpgRC1xGuq5zj+l1p0njOoEhyvL39KuHA1LqMFtmetSbw2biR0xyUfQ4sJwf/kv5psDheheRoTGNVq3u7tNhFMWT6fs2S5Qwyjc9Mu6o05a4uE46gRnM43PTj/wNh4kQ41PlcwGemLXfvGP7EXvLzHSFkc1pPsq7Rym3/Z58WWNfojX4dve0lzPa2SrxoDNnRKYPq90Wh6cj+qSxRfJ4ZNaq2sI6G4mX5rNF3tvs6qD4Erz/lAASvl/6fur7le/Pvj/5vnvVh9CTabMYVsnl42bi11U3OMF92yODx+vtr6GxQrV6Q7cuJbE/EwHL7sPy8Gjw8AGM+4magQebseLIYmD3UQH2fqZ6Yd2EX1aL+Q4fQAyWha0cfjsCCP58XHvgQP4d07ZE3OSO8YjvOObJaUbW9q6Q8P2973O+b/g+7Ft2pOk89rcgMXPx0NJKWuyVy5Oxqwv3cU8nx8l2ecuk5sDNibDnYjzByokJhqiNVk5AumOsaHisSo5DD34TrLT44b2aKj1hnMBM/gZgDCtvxODBYu06AhHuDog1zZwoaC4WJJrdOQPFetiT0Vwwy5y3G13eFLxbHsbiTO/7wuEQO3XR0bCNwSWaz/cuhwmDJzp3HzSrAd3FFY1GRlf8+H0o/eNe2t4FkHTtWD46Oc0mcIfBs577T5aXqHVZFhnTFZQLDQZ/MeG5IFriO8rhiZARP+gTD9LpbAs9lYpHLk5+lk4zUyypEoK3lKUmVeE4Wsypj85zPtJlxmg5OW506JGVimW1kpUEbwQr+JyEMy38UTo1Tn/r0hrcnuKjBgNWcOou9vlMKl9E8EEVQ7Q3ih5A6n7rB6/p+9Ho7hNBhpF/4jtIDzGsdiaJgfU4Rg9Z1dKZ5zTnKLwun2U9ulkqrHBJCXIxYwjQCWJlkR12wi/PEVj+yJT2jWWCkt+01+tNiWA/IuC/mQoBrxVjKNT8GJKopVG6LwT3ZPkMeJtFn1aXrPTESZkxRyzJZG+oj5vZC4mVD1b5ZKplbGI76L7JlGmTvItiAu56KLmOQOwdpGLwcImZQZ+tSARj2u3lvBFEaR/sw8B0SKVYplHSoZIhp/0ydm65Jsu+daWCAPyUCr9nXoYGDbrhPkeVe1kDMzjD/IdmBd4KYkxawUb/ESTi36SvhdKepa8n/GdnKsoqaKBfk2w4DMYmO1niNoonJsz6qzMxUWhstnHQK5q4vOZPXLo4+1F6rvR2eqDMagHXK7tuQRL4nsvMLrlJrUyUh95xNbk+dRWgkQSpURaag+xo4WykFaWiePQJ7VPYOhnO8Lbuhvh3/lM0l+7MJ6DmUB2WL/jsiT0F2vWEHZAmWoV3oeiKL2amoWRADjCLgSMTlOBMFEwIWmg/I1d6u0LHqNlJ4g17KoujR0wGUWgSCxPqkYvGeTtw0owNNWfKlXYoYcDd754I5hmNfM7wayJWwwF1/n8oGLsw4R1V4On/aFqIE+b/KDb8EY2rnI/mZrYlBhOrQbLC8E6oCkAac2KjSUAYO+2iNqVD1evpgrefPMfEarlzavQ1J6KGzGtB4exwmqM1t+Avr/QYwoS39T0NZAeA2pls7oTSqXl7t+F7k6ST+XIeddZV9Dg4yWn8rYxZVCqISV/b6fZwPsrp2WtVCPQMoEat/jAnXn2D5oojyRsvH0+dnrk4NT3Er2dTQLtgtj8HVCduTV7XH2d4Sk92lzwXtQo5LFpDMED1rOx47+sOoUO9k1Ga+1vEtOHgAj5OsXayaetei44sm2du2ocM+ubXtDJxntcj5JmhfGtx9M47/rMzM/13kieyRvVJJcHEz56XoTebive7IwFNK5zZdqTuvN9spgYi61iSYnfpLN3bOwpK2vOuTbEaR8LggWjKUSqZCNGzSy6xlF36EEaiF7WbLkGVX89xFC69RbNjAw1xQRITiCDXxR6Gzw5GvHeG+gf1KDTdfxaoALeFcuAXYXgukkQQWq3orMHaJS61wV9i/SbzvtaXzEGW2drqyLzi5JgT0Zdn7NLBM7fJnOcrzj8tCOzRZeJ/BSXaeh9paYEwbiVjTwXz8UnDb27Y/KvLkIbK/MkuOgZ5KrXEgrafFWtH2Nli5Sr/sR2YsFnTtvaXkVnFkcWrCDj+RT6OpdYiEHkxnmxuqYgYgGQAoW/p8YfKb4eSN4BIMJpujk28BJFl8bRBAHuvJngNw8hHnpNzubALHDR2dBqdbQHa8i39wuJJEfz21ZjfZEwg4L+x0Azyd5dV4zHKS5AF82QJzxnTKg3s/G5/18porI/jJiTirfw9s8UG33Qrx+VX37AZTzPzio3YbB9dr7h75ISLz0OOl+Kn7lNP0R5w1LhmHXcoBV7u2Vx4k/yOYnzaPv2oLTJnMS7eYF7J6vet0vOoON9E4zjPqnOrwSud6Rmbum2261Ap2qN37wj3vVRopBbIifgmXb3ENVMiOi0N+xa1GqXr4f1IFnGQebV2AXmRdLsldxRuYfsfJq22PxzCWgblYZ1vgkTy2HGrfv10tgsz59hY/U1BBsz8pXw95itqD6Uf/LC50lRgSRwJusJJ3sPTfFB3NbeqTjVfX9aFYvdVsEFWffZXZH5ACtNweJhuXq7yV9H2bcoytu3JGtkB1r5z3kAWlAY3fc8qE3bQLFcWR4kUzFaGUwpKlAe0AkA+qsWeNHeYtXI51yKeR36S19+hKMGKPNqUpq2yj3ZYxsdE3cTpgZOig0gjVNuU8Bnoz11DNugSwY+OxIOsNq1cKBAGoBaXAz4MAhYh5xxuIAU3Z1TkGp0BjQoBrgoQvl40kZN/KWc+4eXBfoHkGPekanwCp1VHWuojviY7+AwfLOOD8X0q0M0t3m8IQ15juZ859i5aFhlE9dVsBZmngvsnELQaA98d1uR6w2JmE7+YdAcqjGwvMjIangjm9yDic5WQvLKHD8qeMHsg4evasc/d6lKSZ8bPdiQ/T/1gdEwPX1mI8E4/R90uUce5O41js89TvCWdgVNnqWMsQ/uxiZGtEHLoQuzXi5PHTNWAQFfUK283LqecxJ9D7XlB/ZmCsQ1v8tEZmEHweF619pXu8WvrZtp3sM11P2oLMCnnZPD9j+lskJhaPH7EGkq7Qg/RPBsKe8njOKhA0S4BIijpeRP2et5yYjMH7F2sJeLMrV0+MbHaxZHviybs53ds+6miizU7rxJQDt5N3+l1IlN/B6J1dcAX75z3QwsYTWg6+xMpxM3DOcHJWTeT/IBg7b/VJ3dRwMOuTrtguVnbEghSlyOR7u+rGPGH2/VZfb4rEd0H26aBUPe8SKk0ks7xExiDzS9pBBBv5gVu1owNkxK1xj+4FeUjiIjMDNXZ+t3G3P09FMZ5vnxWzs+SGxWyHuLRuTqbFYvTFjg3dgXiMOTMd7ZVTAQJEGTYtwmvwYlI9dqQVmKILjP+Td4x5g9bgFCINhGtG0U4yyZ5cuYH4cXdAXQYMGAf9QqwXZ3bOQETYKFY1jkaFF+s/O6jnnupdGr/ZZRK4yWLErFXq0rarckuUGe8ZUew2UR6K0Q8jqWstfq9IRF1/JeCexm/azgSZ+QSXjBbko/iBHgxKeiFMf8MMCo6IQmp91OnzzJthOxM1GHEdVQn2K4e71897B7kH8hHChEazH9xFi8e3cSvFSefvX0lwZ6KBgYj84jex1ac6mXM8xi+z/BiGWmq/kJ9l+oPfLwA1CWaEirdCAkhbPBurDGobIN3c0WqUEc8bC/oAdcEUl7/F7w3XkHoQ7xPhWSvvlGzdvt7f1ucLXImi41LnZppBJhjFV7CFw0p3O88JNWe6JeXX6+fcznOSnDiDS58d+Phz6V8wKv3CvWd3oN8D4wWaFlpOldZw1NB+mhutD2ATpejo/2/uWRwdDzQXCIWEWEgLl4dQdUj+O//brch6tn28DqB1Na5Wc3sqN4kaNem/6FEWSbvtM2pEzZfgY6Q+hwPqwFdPPaHG6k0Df2qnbwXRRgHy0l6Ni7k/OQpJTT+cG4MiYyc8VDqBSdaI6DNb0A4/Wbjh9AsWRIzcS3CpBjzi+/LUjJGVaz8+VgrQbjCirs4NcdtsazoLfl3+olOjn0GHqVrlzYlOEIORE3sEeZ+GrtinJhXi0L5Gn2aWa2lQHUQY/9wdTZablSFdsUoE5iZgDx2k02nH+GZ1ih2GFmYbcdMz3PfuYa0YnpHZ9XpP4yGtuoPTiyt6TcH6Y3Z9gtQ6JOr46TAQ2SWQX/4pGmokxkaBe9BNDN9UndqnKu+0+HIy1l6ae+K030LNXClRFdkhxCjDZv0IAI7pmHvQ3ar1daIb43r+dg3cvdMysOb4/8+coirTQcnHl0cAUOBnqPYsGJACd9KqAu7kxTHpxjzPZHJRCWfmrxaJqUiO5Fv3ZZOyW47WJjdyMY1ti6D7yFbP9tsXuTOqxnHtqjpHi3NaKWfUaOeWytQS/8v7hCC2Rpv7u8Ix/T3D9iW9CVCuthgwDHWQ4nw4mkHqVKrUNnnOD0SrqcnoKRDxw0oMtwhWnICDUndoOXU7VgmCuhnOeFxC6EIzy+nOBjo3W6e1k2Z98NLPz00k2KOiImVmleGsAeufmR93w0bVBSvpEcy/znIZYP+KKheT3gVq5sSSMWKFL1rVKXpEZo8r0GFBEMH2NDgZCaRtGqBy281XHpxL4QCMz6C4svEdFK9NiGeyjzHwqnr4Wn6+GBKbV1eq1Y28pYeWja6QBGfvTvtFg4Ffb7w/NyPEpbe6iQmUcfek9YuM/HoNzRd47BbrL6Y1KqbortZz34uAfJcp8cYiWo4FqEb4xMDdtJ8Hbjr2KNXBChHRWHoaDF29NhlmPT5YerVz87JweVLd90tVU8M1tiLZDJu3FQwYnoJsFLjLjlLT1R2P2pq3PWtEbfwbtccp5s08HqZKbphNMmrmU87/noICv/NXSq1VpugzUrX6Y1iZGWkCCamAxTlVKPAg1S5ZpXlqIiCpjj87Yc1fXPZoFPp9Wk685/46whVNMXt2Spx6GWxKtv+VqHlyLtVCHtz9BovfY5jccZolgnO5+Z4fE4RDoWmrT45F23m5q5FH3t747IneHkTS/XWAjW16qU4fDnoqdIlSfD0ypy2WsliSZIuzPbQke1I2P3yZZh08ztvXvqtGLwbjjk5jamxTdY1zEr/YHB9jS5hLLvdn+aL8COjchBa3KcZki/tpGHBEwGE2VmFh99OdIhFyin4CltNTnc9nD7vpLQB5+f8FqSR1l4N5otyvpNAVNwHlRWCoBnp6AdyvTQGlTcR0albZAEQFluDvQMdNBe8SXECcH9Dck8T5I/phwzmYNKr7I8EixYu8DDR9EnIQOwDMfeYSd6OsKfcyN/EJk0LfpxY1AFFMbo4duLDKS6A33AF8c1qrh74wYjK3qLm9iApHvvnRzH574XmFPw35MniaKWEAPM48nlU3vNMYyLyaI0n6Lakg/t5H3yESv79szagJ3MMlToe5JNjlyeX911nQMlS+d/Wcys0xltFgBI0BxdlShLtf5e1aX0zGyQQgulXFQB5ffa//erTVbc7/qAJYR8AcO95shEAwP21fjrO/4XHooFaCgAFAwBAYGxTXeKjCmjrLUHq78Vo5XSVq60HQ+OYFO+vnUEy1OOQuZzGwLaqNCY66u2uobKMDP05CgHtuzlaF1BTqwHypJrMA1rHxrqACT+uFmqg0Fkz66rWDiDc6gTOcFn+XUr/gYohgDGbRdteCicVhrYTtkkBlj6z7qCpOfB3TQF4mpC0XArQ9AzsTJXj1NjE+MpjpB0uv7f07MJPaGMFiSttPq4G/KDt8U88Bx5ivPInB2RuIwdLGJZV69GrdlE99H2QOg1bX2JobxrPKI7xZnsrQwcYQy1s+TH+8reG5IrEQvh2xaE8fwuScpP6VkKgmfC3UJLrIfW79toTr6QeOEPnHNqyiePQGjhqRhNhzrp7KjhYAvNUOYEyhb8aCFQ15YYt+cw0bqK+s8TFK9X/30oNdU0+M67Dn06IDFJX1AwUC7Zr+yGKlclr6iSBUA7zLVCbe3BMujr1OlJ3goK9r1rEud2U15QEKcDdNo4OU9xHPTqazTFyzzUYAJ1y2tUEgddkA/Wb6zit1x5sp8TFDtvJrR22EN+C6aK43ArhFBAjyOcA0YaWWZKZARxtHWAY6uJSgFgibX/YSD2ryPhuCTUNIbWDq1VGVHIw8e88axNDwZZMDUw2LU4YWgeANe0AlkZCxdCPo52rc4x3i2oLNenCAsSF8CvwbibrEa1hbLvfzEO38LJRMGy60kk+NCtCKxpAKxMg8R7qvCcJfSRENVuaCABaM95B9LkKk+Ycgj1NJ2knoMq6oLaKu5KyRgoTAmCCEQLWBAxiw0mEaYAERj8ByF1FAa2bEBROJR2yARGoHCUGjToxoFaOAS07CXB0IY0/vPsBR0iRo0CuPBncwpSSKpCCJJdYunXPFIzhTkJk0wyxkH7rRSRmoMuCKItj9QKLK4ZoOiEpM16NF1gMNK5vab+XLaMjDY7X5jnhkVsoTQHRVJY6l3uhLE7fvbkKCHhQkMUwHZ8eWGqBLztXDktvd5ynK5Vb+zNEiBVZMZ35dB3e0lkNsfSWKegVWnp5HvejDAEZY5Ko1A6y1DSRICpKkpobTa+lY8IChTIhILLGiqRSTqtSJYlUhSxJFUuX4iZLgvQ6KYwn/jq9vhajwPq3MvtSLadZ6bGzT+fwNccMN912h5Ozi+uBu+65P0K3Sz+eyetDHxF4oHd1Dfs977G7XdhDjwg95nfCP+BbgaEbq8uSQ2IiDFmucN+IkJeAzvwxcN/VE1LFSrKKxvv2RUlTiq5MhUrlJqucZfgBxMTSxsZRrbauZlxwXai+olvJOe+FRALo9nko+KOeCFsQBeNGIseP4u1fB5KoE4MYJEFSJENyxUqhOLGKl1IJ4pQolZKkVrI0SpFWqdIpTXqlK0OZMtBl3SthsWSSafw/po9VOWbcPiUoCgszS0ZlyUQMRqUqBZyZLbOyZdEc5SDPXM4Yxo1V85TLRZd0GGE1apCe0xgWcfJkU77smg8RhxaogBbH5WUheyjiROqpSZ0ekCrFL3ykDScu6Li1SB4tJo3IfkIHiEnwfWA0Xr3FIbV+Luy+r931IH9yxCWOa6/3tqKXtHDS/+87zxz4y8yBb6Qb6aAFaRRY66J9hBjC6ipuk0n6eq9/qau81l/l7r3lkPRNUzfffo1rlfWXYqMivOptTyxcjfTLv5F1+k3eyHo4Z7UwoUwRpJCyYzuxUzuzc7uwS7tap6uSKoXQSwc74QEkBTu3C7u0q5P03V+NQ36K23OcM6STWplbDHrw+k3cRNlx/6JvAef73VHZcfqh8Mg3c6NbtrNaJ4D+T0n9EnEfgmIJtF16E6+VgetcyaY/qoOen71QlaF3IKR9klwfCLurd7pb0EVpOptQ7Fnwvlm62XHbm4CxRkoicJ/nvxoenXRqh+tmhmOsUDPPaADNTln7BnOK8R3pVKNEVGOO7kZZ/B4HNM03EMzzcz0/7APloWx9/IdSSznX7Rm6cqvIEY4XfIKIvYxp4lHWnLiz1tbXiXUF') format('woff2');
@@ -166186,6 +166996,18 @@ body {
166186
166996
  font-size: 21px;
166187
166997
  font-weight: 600;
166188
166998
  }
166999
+ .sp-web-beta-badge {
167000
+ align-self: center;
167001
+ margin-left: 8px;
167002
+ padding: 2px 6px;
167003
+ border: 1px solid var(--sp-focus-border, var(--grain-accent));
167004
+ border-radius: 4px;
167005
+ color: var(--sp-focus-border, var(--grain-accent));
167006
+ font-size: 11px;
167007
+ font-weight: 600;
167008
+ letter-spacing: 0.04em;
167009
+ text-transform: uppercase;
167010
+ }
166189
167011
  .sp-web-header-divider {
166190
167012
  width: 1px;
166191
167013
  height: 22px;
@@ -166233,6 +167055,32 @@ body {
166233
167055
  gap: 10px;
166234
167056
  padding: 9px 10px;
166235
167057
  font-size: 12px;
167058
+ width: 100%;
167059
+ border: 0;
167060
+ background: transparent;
167061
+ color: var(--sp-fg);
167062
+ text-align: left;
167063
+ cursor: pointer;
167064
+ }
167065
+ .sp-web-connection-item:hover {
167066
+ background: var(--sp-sidebar-bg);
167067
+ }
167068
+ .sp-web-connection-item:focus-visible {
167069
+ outline: 1px solid var(--sp-accent);
167070
+ }
167071
+ .sp-web-connection-detail {
167072
+ padding: 16px;
167073
+ }
167074
+ .sp-web-connection-detail h2 {
167075
+ margin: 0 0 8px;
167076
+ font-size: 13px;
167077
+ }
167078
+ .sp-web-connection-detail p {
167079
+ color: var(--sp-fg-muted);
167080
+ }
167081
+ .sp-web-connection-actions {
167082
+ display: flex;
167083
+ gap: 8px;
166236
167084
  }
166237
167085
  .sp-web-connection-item small {
166238
167086
  display: block;
@@ -166504,6 +167352,44 @@ body {
166504
167352
  --ag-input-background-color: var(--grain-ground);
166505
167353
  --ag-input-foreground-color: var(--grain-ink);
166506
167354
  --ag-input-border-color: var(--grain-border-subtle);
167355
+ }
167356
+ .sp-web-reauth-action {
167357
+ display: flex;
167358
+ align-items: center;
167359
+ gap: 8px;
167360
+ }
167361
+ .sp-web-reauth-detail {
167362
+ color: var(--sp-muted);
167363
+ font-size: 12px;
167364
+ }
167365
+ .sp-web-connection-item {
167366
+ width: 100%;
167367
+ border: 0;
167368
+ background: transparent;
167369
+ color: var(--grain-ink);
167370
+ font: inherit;
167371
+ text-align: left;
167372
+ cursor: pointer;
167373
+ }
167374
+ .sp-web-connection-item:hover,
167375
+ .sp-web-connection-item:focus-visible {
167376
+ background: var(--grain-surface);
167377
+ outline: 1px solid var(--grain-accent);
167378
+ }
167379
+ .sp-web-connection-detail {
167380
+ padding: 24px;
167381
+ }
167382
+ .sp-web-connection-detail h1 {
167383
+ margin: 0 0 8px;
167384
+ font-size: 18px;
167385
+ }
167386
+ .sp-web-connection-detail p {
167387
+ color: var(--grain-ink-muted);
167388
+ }
167389
+ .sp-web-connection-actions {
167390
+ display: flex;
167391
+ gap: 8px;
167392
+ margin-top: 16px;
166507
167393
  }</style>
166508
167394
  </head>
166509
167395
  <body>