grain-tools 0.10.18 → 0.10.20
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/connection-setup.html +29 -2
- package/dist/credential-page.html +29 -2
- package/dist/mcp-app.html +1 -1
- package/dist/web-app.html +171 -10
- package/out/server/standalone.js +4415 -2834
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -17890,12 +17890,39 @@ body {
|
|
|
17890
17890
|
}
|
|
17891
17891
|
.sp-web-reauth-action {
|
|
17892
17892
|
display: flex;
|
|
17893
|
-
align-items:
|
|
17893
|
+
align-items: stretch;
|
|
17894
17894
|
gap: 8px;
|
|
17895
|
+
margin-block: 4px;
|
|
17896
|
+
}
|
|
17897
|
+
.sp-web-reauth-action .sp-web-reauth-command,
|
|
17898
|
+
.sp-web-reauth-action .sp-web-secondary-button {
|
|
17899
|
+
display: inline-flex;
|
|
17900
|
+
align-items: center;
|
|
17901
|
+
padding: 0 14px;
|
|
17902
|
+
min-height: 36px;
|
|
17903
|
+
border-radius: 6px;
|
|
17904
|
+
font-size: 14px;
|
|
17905
|
+
}
|
|
17906
|
+
.sp-web-reauth-command,
|
|
17907
|
+
.sp-web-reauth-detail code {
|
|
17908
|
+
font-variant-ligatures: none;
|
|
17909
|
+
letter-spacing: 0;
|
|
17895
17910
|
}
|
|
17896
17911
|
.sp-web-reauth-detail {
|
|
17897
|
-
|
|
17912
|
+
max-width: 52ch;
|
|
17913
|
+
color: var(--grain-ink-muted);
|
|
17914
|
+
font-size: 13px;
|
|
17915
|
+
line-height: 1.6;
|
|
17916
|
+
}
|
|
17917
|
+
.sp-web-reauth-detail code {
|
|
17918
|
+
padding: 1px 6px;
|
|
17919
|
+
border: 1px solid var(--grain-border-subtle);
|
|
17920
|
+
border-radius: 4px;
|
|
17921
|
+
background: var(--grain-surface);
|
|
17922
|
+
color: var(--grain-ink);
|
|
17923
|
+
font-family: var(--sp-font-mono, monospace);
|
|
17898
17924
|
font-size: 12px;
|
|
17925
|
+
white-space: nowrap;
|
|
17899
17926
|
}
|
|
17900
17927
|
.sp-web-connection-item {
|
|
17901
17928
|
width: 100%;
|
|
@@ -4757,12 +4757,39 @@ body {
|
|
|
4757
4757
|
}
|
|
4758
4758
|
.sp-web-reauth-action {
|
|
4759
4759
|
display: flex;
|
|
4760
|
-
align-items:
|
|
4760
|
+
align-items: stretch;
|
|
4761
4761
|
gap: 8px;
|
|
4762
|
+
margin-block: 4px;
|
|
4763
|
+
}
|
|
4764
|
+
.sp-web-reauth-action .sp-web-reauth-command,
|
|
4765
|
+
.sp-web-reauth-action .sp-web-secondary-button {
|
|
4766
|
+
display: inline-flex;
|
|
4767
|
+
align-items: center;
|
|
4768
|
+
padding: 0 14px;
|
|
4769
|
+
min-height: 36px;
|
|
4770
|
+
border-radius: 6px;
|
|
4771
|
+
font-size: 14px;
|
|
4772
|
+
}
|
|
4773
|
+
.sp-web-reauth-command,
|
|
4774
|
+
.sp-web-reauth-detail code {
|
|
4775
|
+
font-variant-ligatures: none;
|
|
4776
|
+
letter-spacing: 0;
|
|
4762
4777
|
}
|
|
4763
4778
|
.sp-web-reauth-detail {
|
|
4764
|
-
|
|
4779
|
+
max-width: 52ch;
|
|
4780
|
+
color: var(--grain-ink-muted);
|
|
4781
|
+
font-size: 13px;
|
|
4782
|
+
line-height: 1.6;
|
|
4783
|
+
}
|
|
4784
|
+
.sp-web-reauth-detail code {
|
|
4785
|
+
padding: 1px 6px;
|
|
4786
|
+
border: 1px solid var(--grain-border-subtle);
|
|
4787
|
+
border-radius: 4px;
|
|
4788
|
+
background: var(--grain-surface);
|
|
4789
|
+
color: var(--grain-ink);
|
|
4790
|
+
font-family: var(--sp-font-mono, monospace);
|
|
4765
4791
|
font-size: 12px;
|
|
4792
|
+
white-space: nowrap;
|
|
4766
4793
|
}
|
|
4767
4794
|
.sp-web-connection-item {
|
|
4768
4795
|
width: 100%;
|
package/dist/mcp-app.html
CHANGED
|
@@ -146644,7 +146644,7 @@ function CloseIcon({ size = 10 }) {
|
|
|
146644
146644
|
}
|
|
146645
146645
|
const PATHS = {
|
|
146646
146646
|
play: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 3.5v9l7-4.5-7-4.5z" }),
|
|
146647
|
-
stop: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1" }),
|
|
146647
|
+
stop: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1", fill: "currentColor", stroke: "none" }),
|
|
146648
146648
|
copy: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
146649
146649
|
/* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "5.5", y: "5.5", width: "8", height: "8", rx: "1.5" }),
|
|
146650
146650
|
/* @__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" })
|
package/dist/web-app.html
CHANGED
|
@@ -13121,6 +13121,7 @@ const TELEMETRY_EVENTS = [
|
|
|
13121
13121
|
...RESULT_ROUTING_SCHEMA.events,
|
|
13122
13122
|
"daemon_crashed",
|
|
13123
13123
|
"service_lifecycle",
|
|
13124
|
+
"engine_update",
|
|
13124
13125
|
"mcp_http_error",
|
|
13125
13126
|
"duckdb_provision_completed",
|
|
13126
13127
|
"duckdb_runtime_state",
|
|
@@ -13232,6 +13233,11 @@ const TELEMETRY_STATES = [
|
|
|
13232
13233
|
"daemon",
|
|
13233
13234
|
"standalone_mcp",
|
|
13234
13235
|
"loop",
|
|
13236
|
+
"install_grain_panel",
|
|
13237
|
+
"extension_too_old",
|
|
13238
|
+
"grain_needs_update",
|
|
13239
|
+
"updating_grain",
|
|
13240
|
+
"reconnecting_to_grain",
|
|
13235
13241
|
"unknown"
|
|
13236
13242
|
];
|
|
13237
13243
|
const TELEMETRY_SOURCES = [
|
|
@@ -13480,7 +13486,6 @@ const TELEMETRY_ERROR_CODES = [
|
|
|
13480
13486
|
const TELEMETRY_LOOP_EXECUTORS = ["jules", "antigravity", "claude", "none", "unknown"];
|
|
13481
13487
|
const TELEMETRY_ERROR_IDS = [
|
|
13482
13488
|
/** Writing the installed daemon version pointer failed. */
|
|
13483
|
-
"daemon_install_registration_failed",
|
|
13484
13489
|
/** The daemon binary was missing from every registered installation path. */
|
|
13485
13490
|
"daemon_binary_missing",
|
|
13486
13491
|
/** The installed daemon is newer than this client can safely use. */
|
|
@@ -13529,6 +13534,7 @@ const TELEMETRY_ERROR_IDS = [
|
|
|
13529
13534
|
* A capacity effect, not a query or connector failure.
|
|
13530
13535
|
*/
|
|
13531
13536
|
"session_pruned_during_query",
|
|
13537
|
+
"query_result_tab_limit",
|
|
13532
13538
|
/**
|
|
13533
13539
|
* No connection profile could be resolved for the query: the request carried
|
|
13534
13540
|
* none, and the daemon had none stored to fall back on. Nothing was ever sent
|
|
@@ -13565,7 +13571,16 @@ const TELEMETRY_ERROR_IDS = [
|
|
|
13565
13571
|
/** The supervisor rejected starting the installed service. */
|
|
13566
13572
|
"service_supervisor_start_failed",
|
|
13567
13573
|
/** The supervisor accepted the service, but it never published a reachable endpoint. */
|
|
13568
|
-
"service_started_unreachable"
|
|
13574
|
+
"service_started_unreachable",
|
|
13575
|
+
/** The release manifest could not be fetched or parsed during an update check. */
|
|
13576
|
+
"update_manifest_unreachable",
|
|
13577
|
+
/** The downloaded update artifact did not match the release manifest's SHA-256. */
|
|
13578
|
+
"update_checksum_mismatch",
|
|
13579
|
+
/** The update artifact download or its SHA-256/size verification failed. */
|
|
13580
|
+
"update_download_failed",
|
|
13581
|
+
/** The launcher restarted the previous payload after the new one failed to become ready. */
|
|
13582
|
+
"update_rollback"
|
|
13583
|
+
/** The engine exited before the attempted trigger could be classified elsewhere. */
|
|
13569
13584
|
];
|
|
13570
13585
|
const TELEMETRY_ERROR_KINDS = [
|
|
13571
13586
|
"auth",
|
|
@@ -13869,13 +13884,35 @@ function useWebCommandDispatcher(dispatch) {
|
|
|
13869
13884
|
const data = await res.json().catch(() => ({}));
|
|
13870
13885
|
dispatch({
|
|
13871
13886
|
type: "statusMessage",
|
|
13872
|
-
message: `Export failed
|
|
13887
|
+
message: `Export failed. Very large results can fail to export; try adding a LIMIT or selecting fewer columns. ${data.error || res.statusText}`
|
|
13873
13888
|
});
|
|
13874
13889
|
return;
|
|
13875
13890
|
}
|
|
13876
13891
|
const disposition = res.headers.get("Content-Disposition") || "";
|
|
13877
13892
|
const filename = /filename="?([^"]+)"?/.exec(disposition)?.[1] || "export.csv";
|
|
13878
|
-
|
|
13893
|
+
if (!res.body) {
|
|
13894
|
+
throw new Error("No response body");
|
|
13895
|
+
}
|
|
13896
|
+
const reader = res.body.getReader();
|
|
13897
|
+
let bytesReceived = 0;
|
|
13898
|
+
let lastUpdate = Date.now();
|
|
13899
|
+
dispatch({ type: "statusMessage", message: "Exporting…" });
|
|
13900
|
+
const chunks = [];
|
|
13901
|
+
while (true) {
|
|
13902
|
+
const { done, value } = await reader.read();
|
|
13903
|
+
if (done) break;
|
|
13904
|
+
if (value) {
|
|
13905
|
+
chunks.push(value);
|
|
13906
|
+
bytesReceived += value.length;
|
|
13907
|
+
const now = Date.now();
|
|
13908
|
+
if (now - lastUpdate >= 500) {
|
|
13909
|
+
const mb = (bytesReceived / (1024 * 1024)).toFixed(1);
|
|
13910
|
+
dispatch({ type: "statusMessage", message: `Exporting… ${mb} MB` });
|
|
13911
|
+
lastUpdate = now;
|
|
13912
|
+
}
|
|
13913
|
+
}
|
|
13914
|
+
}
|
|
13915
|
+
const blob = new Blob(chunks);
|
|
13879
13916
|
const url = URL.createObjectURL(blob);
|
|
13880
13917
|
const anchor = document.createElement("a");
|
|
13881
13918
|
anchor.href = url;
|
|
@@ -13884,8 +13921,12 @@ function useWebCommandDispatcher(dispatch) {
|
|
|
13884
13921
|
anchor.click();
|
|
13885
13922
|
anchor.remove();
|
|
13886
13923
|
URL.revokeObjectURL(url);
|
|
13924
|
+
dispatch({ type: "statusMessage", message: `Exported ${filename}` });
|
|
13887
13925
|
}).catch(
|
|
13888
|
-
(err) => dispatch({
|
|
13926
|
+
(err) => dispatch({
|
|
13927
|
+
type: "statusMessage",
|
|
13928
|
+
message: `Export failed. Very large results can fail to export; try adding a LIMIT or selecting fewer columns. ${err.message}`
|
|
13929
|
+
})
|
|
13889
13930
|
);
|
|
13890
13931
|
return;
|
|
13891
13932
|
}
|
|
@@ -14386,7 +14427,13 @@ function ReauthorizeNotice({
|
|
|
14386
14427
|
}
|
|
14387
14428
|
)
|
|
14388
14429
|
] }),
|
|
14389
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
14430
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "sp-web-reauth-detail", children: [
|
|
14431
|
+
reason === "expired-link" ? "This link has expired or was already used." : reason === "unreachable" ? "Grain may have stopped, or restarted on a different port. If it stopped, start it again first." : "This tab needs a fresh workspace link.",
|
|
14432
|
+
" ",
|
|
14433
|
+
"Using a browser other than your default? Run ",
|
|
14434
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "grain ui --url" }),
|
|
14435
|
+
" and paste the link here."
|
|
14436
|
+
] })
|
|
14390
14437
|
] });
|
|
14391
14438
|
}
|
|
14392
14439
|
function isUpdateMarkerStatus(value) {
|
|
@@ -148976,7 +149023,7 @@ function getConnectorGroup(id) {
|
|
|
148976
149023
|
}
|
|
148977
149024
|
const PATHS = {
|
|
148978
149025
|
play: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 3.5v9l7-4.5-7-4.5z" }),
|
|
148979
|
-
stop: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1" }),
|
|
149026
|
+
stop: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1", fill: "currentColor", stroke: "none" }),
|
|
148980
149027
|
copy: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
148981
149028
|
/* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "5.5", y: "5.5", width: "8", height: "8", rx: "1.5" }),
|
|
148982
149029
|
/* @__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" })
|
|
@@ -152769,6 +152816,52 @@ function useWebAppInitialTarget(isReady, viewHint, resultTarget, tabs, showQuery
|
|
|
152769
152816
|
}
|
|
152770
152817
|
}, [isReady, resultTarget, tabs, showQuery]);
|
|
152771
152818
|
}
|
|
152819
|
+
const DRAFTS_KEY = "grain:web-drafts";
|
|
152820
|
+
function saveWebDraft(draft, sessionId) {
|
|
152821
|
+
const allDrafts = loadAllDrafts();
|
|
152822
|
+
const sessionDrafts = allDrafts[sessionId] ?? {};
|
|
152823
|
+
sessionDrafts[draft.id] = draft;
|
|
152824
|
+
allDrafts[sessionId] = sessionDrafts;
|
|
152825
|
+
writeStoredValue(DRAFTS_KEY, JSON.stringify(allDrafts));
|
|
152826
|
+
}
|
|
152827
|
+
function loadAllSessionDrafts(sessionId) {
|
|
152828
|
+
const allDrafts = loadAllDrafts();
|
|
152829
|
+
return Object.values(allDrafts[sessionId] ?? {});
|
|
152830
|
+
}
|
|
152831
|
+
function deleteWebDraft(id, sessionId) {
|
|
152832
|
+
const allDrafts = loadAllDrafts();
|
|
152833
|
+
if (allDrafts[sessionId]?.[id]) {
|
|
152834
|
+
delete allDrafts[sessionId][id];
|
|
152835
|
+
writeStoredValue(DRAFTS_KEY, JSON.stringify(allDrafts));
|
|
152836
|
+
}
|
|
152837
|
+
}
|
|
152838
|
+
function loadAllDrafts() {
|
|
152839
|
+
const stored = readStoredValue(DRAFTS_KEY);
|
|
152840
|
+
if (!stored) return {};
|
|
152841
|
+
try {
|
|
152842
|
+
return JSON.parse(stored);
|
|
152843
|
+
} catch {
|
|
152844
|
+
return {};
|
|
152845
|
+
}
|
|
152846
|
+
}
|
|
152847
|
+
function flushDrafts({
|
|
152848
|
+
tabs,
|
|
152849
|
+
queryConnections
|
|
152850
|
+
}) {
|
|
152851
|
+
tabs.forEach((tab) => {
|
|
152852
|
+
if (!tab.sessionId || tab.sessionId === WEB_SESSION_ID) {
|
|
152853
|
+
saveWebDraft(
|
|
152854
|
+
{
|
|
152855
|
+
id: tab.id,
|
|
152856
|
+
title: tab.title,
|
|
152857
|
+
sql: tab.sql,
|
|
152858
|
+
connectionId: queryConnections[tab.id] || "built-in-duckdb"
|
|
152859
|
+
},
|
|
152860
|
+
WEB_SESSION_ID
|
|
152861
|
+
);
|
|
152862
|
+
}
|
|
152863
|
+
});
|
|
152864
|
+
}
|
|
152772
152865
|
const THEME_KEY = "grain:web-theme";
|
|
152773
152866
|
const SIDEBAR_DEFAULT_WIDTH = 220;
|
|
152774
152867
|
const SIDEBAR_MIN_WIDTH = 160;
|
|
@@ -152776,7 +152869,10 @@ const SIDEBAR_MAX_WIDTH = 400;
|
|
|
152776
152869
|
const SIDEBAR_AUTO_COLLAPSE_QUERY = "(max-width: 640px)";
|
|
152777
152870
|
const SIDEBAR_WIDTH_STORAGE_KEY = "sql-preview:web-sidebar-width";
|
|
152778
152871
|
const CONNECTIONS_COLLAPSED_KEY = "grain:web-connections-collapsed";
|
|
152779
|
-
function WebApp({
|
|
152872
|
+
function WebApp({
|
|
152873
|
+
viewHint,
|
|
152874
|
+
resultTarget
|
|
152875
|
+
}) {
|
|
152780
152876
|
const { onMessage, isReady, sendMessage, callServerTool, track } = useHostContext();
|
|
152781
152877
|
const session = useWebSession();
|
|
152782
152878
|
const {
|
|
@@ -152848,6 +152944,7 @@ function WebApp({ viewHint, resultTarget }) {
|
|
|
152848
152944
|
sendMessage("tabClosed", { tabId: id });
|
|
152849
152945
|
}
|
|
152850
152946
|
closeTab(id);
|
|
152947
|
+
deleteWebDraft(id, WEB_SESSION_ID);
|
|
152851
152948
|
}
|
|
152852
152949
|
};
|
|
152853
152950
|
const reopenQuery = () => {
|
|
@@ -152969,6 +153066,43 @@ function WebApp({ viewHint, resultTarget }) {
|
|
|
152969
153066
|
if (isReady) void fetchConnections();
|
|
152970
153067
|
}, [isReady, fetchConnections]);
|
|
152971
153068
|
useWebAppInitialTarget(isReady, viewHint, resultTarget, tabs, showQuery, openSettingsPanel);
|
|
153069
|
+
const draftsRestoredRef = reactExports.useRef(false);
|
|
153070
|
+
reactExports.useEffect(() => {
|
|
153071
|
+
if (!isReady || draftsRestoredRef.current) return;
|
|
153072
|
+
draftsRestoredRef.current = true;
|
|
153073
|
+
const drafts = loadAllSessionDrafts(WEB_SESSION_ID);
|
|
153074
|
+
const storeState = useAppStore.getState();
|
|
153075
|
+
drafts.forEach((draft) => {
|
|
153076
|
+
const existingTab = storeState.tabs.find((t) => t.id === draft.id);
|
|
153077
|
+
if (existingTab) {
|
|
153078
|
+
storeState.updateTab(draft.id, { title: draft.title, sql: draft.sql });
|
|
153079
|
+
setQueryConnections((current2) => ({ ...current2, [draft.id]: draft.connectionId }));
|
|
153080
|
+
} else {
|
|
153081
|
+
const parsed = parseProjectedTabId(draft.id);
|
|
153082
|
+
const remoteId = parsed?.tabId ?? crypto.randomUUID();
|
|
153083
|
+
storeState.addTab(draft.sql, draft.id, draft.title, void 0, {
|
|
153084
|
+
sessionId: WEB_SESSION_ID,
|
|
153085
|
+
remoteId
|
|
153086
|
+
});
|
|
153087
|
+
setQueryConnections((current2) => ({ ...current2, [draft.id]: draft.connectionId }));
|
|
153088
|
+
}
|
|
153089
|
+
});
|
|
153090
|
+
}, [isReady]);
|
|
153091
|
+
const draftsRef = reactExports.useRef({
|
|
153092
|
+
tabs,
|
|
153093
|
+
queryConnections
|
|
153094
|
+
});
|
|
153095
|
+
draftsRef.current = { tabs, queryConnections };
|
|
153096
|
+
reactExports.useEffect(() => {
|
|
153097
|
+
if (!isReady) return;
|
|
153098
|
+
const timeout = setTimeout(() => flushDrafts(draftsRef.current), 300);
|
|
153099
|
+
return () => clearTimeout(timeout);
|
|
153100
|
+
}, [tabs, queryConnections, isReady]);
|
|
153101
|
+
reactExports.useEffect(() => {
|
|
153102
|
+
const onPageHide = () => flushDrafts(draftsRef.current);
|
|
153103
|
+
window.addEventListener("pagehide", onPageHide);
|
|
153104
|
+
return () => window.removeEventListener("pagehide", onPageHide);
|
|
153105
|
+
}, []);
|
|
152972
153106
|
reactExports.useEffect(() => {
|
|
152973
153107
|
const mediaQuery = window.matchMedia(SIDEBAR_AUTO_COLLAPSE_QUERY);
|
|
152974
153108
|
const handleChange = (e2) => {
|
|
@@ -166998,12 +167132,39 @@ body {
|
|
|
166998
167132
|
}
|
|
166999
167133
|
.sp-web-reauth-action {
|
|
167000
167134
|
display: flex;
|
|
167001
|
-
align-items:
|
|
167135
|
+
align-items: stretch;
|
|
167002
167136
|
gap: 8px;
|
|
167137
|
+
margin-block: 4px;
|
|
167138
|
+
}
|
|
167139
|
+
.sp-web-reauth-action .sp-web-reauth-command,
|
|
167140
|
+
.sp-web-reauth-action .sp-web-secondary-button {
|
|
167141
|
+
display: inline-flex;
|
|
167142
|
+
align-items: center;
|
|
167143
|
+
padding: 0 14px;
|
|
167144
|
+
min-height: 36px;
|
|
167145
|
+
border-radius: 6px;
|
|
167146
|
+
font-size: 14px;
|
|
167147
|
+
}
|
|
167148
|
+
.sp-web-reauth-command,
|
|
167149
|
+
.sp-web-reauth-detail code {
|
|
167150
|
+
font-variant-ligatures: none;
|
|
167151
|
+
letter-spacing: 0;
|
|
167003
167152
|
}
|
|
167004
167153
|
.sp-web-reauth-detail {
|
|
167005
|
-
|
|
167154
|
+
max-width: 52ch;
|
|
167155
|
+
color: var(--grain-ink-muted);
|
|
167156
|
+
font-size: 13px;
|
|
167157
|
+
line-height: 1.6;
|
|
167158
|
+
}
|
|
167159
|
+
.sp-web-reauth-detail code {
|
|
167160
|
+
padding: 1px 6px;
|
|
167161
|
+
border: 1px solid var(--grain-border-subtle);
|
|
167162
|
+
border-radius: 4px;
|
|
167163
|
+
background: var(--grain-surface);
|
|
167164
|
+
color: var(--grain-ink);
|
|
167165
|
+
font-family: var(--sp-font-mono, monospace);
|
|
167006
167166
|
font-size: 12px;
|
|
167167
|
+
white-space: nowrap;
|
|
167007
167168
|
}
|
|
167008
167169
|
.sp-web-connection-item {
|
|
167009
167170
|
width: 100%;
|