@64mb/freelens-power 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/out/main/common/power-logs.js.map +1 -1
  2. package/out/main/common/store/logs-preferences-store.js +5 -0
  3. package/out/main/common/store/logs-preferences-store.js.map +1 -1
  4. package/out/main/dashboard-views.js +26 -0
  5. package/out/main/dashboard-views.js.map +1 -1
  6. package/out/main/power-logs-database.js +5 -0
  7. package/out/main/power-logs-database.js.map +1 -1
  8. package/out/main/power-logs-ipc.js +8 -0
  9. package/out/main/power-logs-ipc.js.map +1 -1
  10. package/out/renderer/ai-diagnostic-handoff.js +1 -0
  11. package/out/renderer/ai-diagnostic-handoff.js.map +1 -1
  12. package/out/renderer/ai-extras.js +7 -2
  13. package/out/renderer/ai-extras.js.map +1 -1
  14. package/out/renderer/ai.js +1 -1
  15. package/out/renderer/ai.js.map +1 -1
  16. package/out/renderer/appearance.js +2 -0
  17. package/out/renderer/appearance.js.map +1 -1
  18. package/out/renderer/dashboard.js +35 -8
  19. package/out/renderer/dashboard.js.map +1 -1
  20. package/out/renderer/index.js +2 -1
  21. package/out/renderer/index.js.map +1 -1
  22. package/out/renderer/power-log-json-cell.js +14 -6
  23. package/out/renderer/power-log-json-cell.js.map +1 -1
  24. package/out/renderer/power-log-json.js +100 -16
  25. package/out/renderer/power-log-json.js.map +1 -1
  26. package/out/renderer/power-log-sql-editor.js +91 -0
  27. package/out/renderer/power-log-sql-editor.js.map +1 -0
  28. package/out/renderer/power-logs-client.js +4 -0
  29. package/out/renderer/power-logs-client.js.map +1 -1
  30. package/out/renderer/power-logs-sql-drafts.js +76 -0
  31. package/out/renderer/power-logs-sql-drafts.js.map +1 -0
  32. package/out/renderer/power-logs-sql-row.js +3 -11
  33. package/out/renderer/power-logs-sql-row.js.map +1 -1
  34. package/out/renderer/power-logs.css.js +8 -4
  35. package/out/renderer/power-logs.css.js.map +1 -1
  36. package/out/renderer/power-logs.js +190 -141
  37. package/out/renderer/power-logs.js.map +1 -1
  38. package/out/renderer/preferences/preferences.js +2 -9
  39. package/out/renderer/preferences/preferences.js.map +1 -1
  40. package/out/renderer/preferences/section-navigation.js +44 -0
  41. package/out/renderer/preferences/section-navigation.js.map +1 -0
  42. package/out/renderer/src/common/power-log-json-preset.js +23 -25
  43. package/out/renderer/src/common/power-log-json-preset.js.map +1 -1
  44. package/out/renderer/src/common/power-log-sql-level.js +25 -0
  45. package/out/renderer/src/common/power-log-sql-level.js.map +1 -0
  46. package/out/renderer/src/common/power-logs-ai.js +4 -0
  47. package/out/renderer/src/common/power-logs-ai.js.map +1 -1
  48. package/out/renderer/src/common/power-logs.js.map +1 -1
  49. package/out/renderer/src/common/store/logs-preferences-store.js +5 -0
  50. package/out/renderer/src/common/store/logs-preferences-store.js.map +1 -1
  51. package/package.json +1 -1
@@ -11,15 +11,18 @@ const require_diagnostic_explain_button = require("./diagnostic-explain-button.j
11
11
  const require_power_log_field_filters = require("./src/common/power-log-field-filters.js");
12
12
  const require_power_log_json_filter_sql = require("./src/common/power-log-json-filter-sql.js");
13
13
  const require_power_log_json_preset = require("./src/common/power-log-json-preset.js");
14
+ const require_power_log_sql_level = require("./src/common/power-log-sql-level.js");
14
15
  const require_power_logs_ai = require("./src/common/power-logs-ai.js");
15
16
  const require_power_log_json_fields = require("./power-log-json-fields.js");
16
17
  const require_power_log_json = require("./power-log-json.js");
17
18
  const require_power_log_message = require("./power-log-message.js");
18
19
  const require_power_log_json_cell = require("./power-log-json-cell.js");
20
+ const require_power_log_sql_editor = require("./power-log-sql-editor.js");
19
21
  const require_power_logs_css = require("./power-logs.css.js");
20
22
  const require_power_logs_client = require("./power-logs-client.js");
21
23
  const require_power_logs_collector = require("./power-logs-collector.js");
22
24
  const require_power_logs_entrypoints = require("./power-logs-entrypoints.js");
25
+ const require_power_logs_sql_drafts = require("./power-logs-sql-drafts.js");
23
26
  const require_power_logs_sql_row = require("./power-logs-sql-row.js");
24
27
  const require_resource_search_client = require("./resource-search-client.js");
25
28
  const require_power_logs_transport = require("./power-logs-transport.js");
@@ -44,7 +47,7 @@ var levels = [
44
47
  var examples = [
45
48
  ["Recent", "SELECT message, level, container, pod, timestamp FROM logs ORDER BY timestamp DESC NULLS LAST LIMIT 200"],
46
49
  ["Errors", "SELECT pod, container, count(*) AS errors FROM logs WHERE level IN ('error','fatal') GROUP BY pod, container ORDER BY errors DESC"],
47
- ["Timeline", "SELECT strftime('%Y-%m-%d %H:%M', timestamp / 1000, 'unixepoch') AS minute, level, count(*) AS lines FROM logs GROUP BY minute, level ORDER BY minute"],
50
+ ["Timeline", "SELECT strftime('%Y-%m-%d %H:%M', timestamp / 1000, 'unixepoch') AS minute, UPPER(level) AS level, count(*) AS lines FROM logs GROUP BY minute, level ORDER BY minute"],
48
51
  ["JSON", "SELECT fieldsJson FROM logs WHERE fieldsJson IS NOT NULL LIMIT 100"]
49
52
  ];
50
53
  var sqlColumnWidth = (column) => column === "message" || column === "fieldsJson" ? 360 : column === "level" ? 90 : 200;
@@ -133,7 +136,10 @@ var SourcePicker = require__global_external_mobx_react.observer(() => {
133
136
  return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
134
137
  className: "power-logs-source",
135
138
  children: [
136
- /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Resource" }),
139
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", {
140
+ className: "power-logs-control-label",
141
+ children: "Resource"
142
+ }),
137
143
  /* @__PURE__ */ require_jsx_runtime.jsx(LogSelect, {
138
144
  label: "Resource",
139
145
  value,
@@ -182,6 +188,7 @@ function LogRows({ records, loading, selected, onSelect, follow, setFollow, comp
182
188
  follow,
183
189
  compact
184
190
  ]);
191
+ const levelCharacters = require__global_external_react.useMemo(() => records.reduce((width, record) => Math.max(width, record.level.length), 4), [records]);
185
192
  const rowHeight = compact ? 20 : 34;
186
193
  const start = Math.max(0, Math.floor(top / rowHeight) - 5);
187
194
  const visible = records.slice(start, start + Math.ceil(height / rowHeight) + 10);
@@ -190,6 +197,7 @@ function LogRows({ records, loading, selected, onSelect, follow, setFollow, comp
190
197
  children: /* @__PURE__ */ require_jsx_runtime.jsx("div", {
191
198
  className: "power-logs-stream",
192
199
  ref: viewport,
200
+ style: { "--power-log-level-ch": levelCharacters },
193
201
  "aria-label": "Log lines",
194
202
  "aria-busy": loading,
195
203
  onScroll: (event) => {
@@ -226,12 +234,14 @@ function LogRows({ records, loading, selected, onSelect, follow, setFollow, comp
226
234
  }),
227
235
  /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
228
236
  className: "power-log-origin",
229
- title: sourceKey(record),
237
+ id: `power-log-source-${record.key}`,
230
238
  children: [
231
- record.container,
239
+ record.pod,
232
240
  record.previous ? " (previous)" : "",
233
- " · ",
234
- record.pod
241
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Tooltip, {
242
+ targetId: `power-log-source-${record.key}`,
243
+ children: sourceKey(record)
244
+ })
235
245
  ]
236
246
  }),
237
247
  /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_message.LogMessage, { text: record.message })
@@ -265,7 +275,7 @@ function LogDetails({ record, jsonColumn, onClose, onFilter }) {
265
275
  })] }),
266
276
  /* @__PURE__ */ require_jsx_runtime.jsx("p", {
267
277
  className: "power-logs-muted",
268
- children: [record.pod && sourceKey(record), record.level].filter(Boolean).join(" · ")
278
+ children: [record.pod && sourceKey(record), record.level.toUpperCase()].filter(Boolean).join(" · ")
269
279
  }),
270
280
  record.message && /* @__PURE__ */ require_jsx_runtime.jsx("pre", { children: /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_message.LogMessage, { text: record.message }) }),
271
281
  record.fieldsJson && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
@@ -336,12 +346,17 @@ function Workspace({ clusterId, contextId }) {
336
346
  clusterId,
337
347
  contextId
338
348
  }), [clusterId, contextId]);
349
+ const drafts = require__global_external_react.useMemo(() => new require_power_logs_sql_drafts.CaptureSqlDrafts((id, query) => client.saveSql(id, query), () => setError("Could not save the SQL draft. Your edits remain available in this workspace.")), [client]);
350
+ require__global_external_react.useEffect(() => () => {
351
+ drafts.dispose();
352
+ }, [drafts]);
339
353
  const [target, setTarget] = require__global_external_react.useState(initialTarget);
340
354
  const [records, setRecords] = require__global_external_react.useState([]);
341
355
  const [sources, setSources] = require__global_external_react.useState([]);
342
356
  const [notes, setNotes] = require__global_external_react.useState([]);
343
357
  const [status, setStatus] = require__global_external_react.useState("Choose a resource to read its logs");
344
358
  const [error, setError] = require__global_external_react.useState("");
359
+ const [sqlError, setSqlError] = require__global_external_react.useState("");
345
360
  const [tab, setTab] = require__global_external_react.useState("live");
346
361
  const [text, setText] = require__global_external_react.useState("");
347
362
  const [level, setLevel] = require__global_external_react.useState("");
@@ -356,6 +371,7 @@ function Workspace({ clusterId, contextId }) {
356
371
  require_power_logs_limits.applyPowerLogsLimits(preferences.limits);
357
372
  }, [preferences.limits]);
358
373
  const [compact, setCompact] = require__global_external_react.useState(preferences.compactRows === true);
374
+ const [hideResourceName, setHideResourceName] = require__global_external_react.useState(preferences.hideResourceNameColumn === true);
359
375
  const [selected, setSelected] = require__global_external_react.useState();
360
376
  const [selectedSqlRow, setSelectedSqlRow] = require__global_external_react.useState();
361
377
  const [liveCoverage, setLiveCoverage] = require__global_external_react.useState();
@@ -366,8 +382,12 @@ function Workspace({ clusterId, contextId }) {
366
382
  const [captureId, updateCaptureId] = require__global_external_react.useState("");
367
383
  const captureSelection = require__global_external_react.useRef(captureId);
368
384
  captureSelection.current = captureId;
369
- const setCaptureId = (id) => {
385
+ const setCaptureId = (id, snapshot = captures.find((item) => item.id === id)) => {
386
+ if (id !== captureSelection.current) drafts.flush();
370
387
  captureSelection.current = id;
388
+ const query = snapshot ? drafts.read(snapshot, examples[0][1]) : examples[0][1];
389
+ sqlDraft.current = query;
390
+ updateSql(query);
371
391
  updateCaptureId(id);
372
392
  };
373
393
  const deleting = require__global_external_react.useRef(/* @__PURE__ */ new Set());
@@ -382,6 +402,7 @@ function Workspace({ clusterId, contextId }) {
382
402
  const setSql = (query) => {
383
403
  sqlDraft.current = query;
384
404
  updateSql(query);
405
+ if (captureSelection.current) drafts.set(captureSelection.current, query);
385
406
  };
386
407
  const captureKeys = require__global_external_react.useRef(/* @__PURE__ */ new Map());
387
408
  require__global_external_react.useEffect(() => {
@@ -662,7 +683,7 @@ function Workspace({ clusterId, contextId }) {
662
683
  if (current) try {
663
684
  const saved = await client.finish(current.id, partial || controller.signal.aborted, [...new Set(captureNotes)].slice(0, 20));
664
685
  if (alive.current) {
665
- setCaptureId(saved.id);
686
+ setCaptureId(saved.id, saved);
666
687
  setCaptures((items) => [saved, ...items.filter((item) => item.id !== saved.id)]);
667
688
  setTab("captures");
668
689
  setSql(examples[0][1]);
@@ -690,6 +711,7 @@ function Workspace({ clusterId, contextId }) {
690
711
  setError("");
691
712
  setResult(void 0);
692
713
  setSelectedSqlRow(void 0);
714
+ setSqlError("");
693
715
  setResultEvidence({
694
716
  capture: captured,
695
717
  sql: query
@@ -697,10 +719,24 @@ function Workspace({ clusterId, contextId }) {
697
719
  setResultPage(0);
698
720
  try {
699
721
  const value = await client.query(captured.id, id, query);
700
- if (alive.current && queryRef.current === id) if (value.error) setError(value.error);
701
- else setResult(value);
722
+ if (alive.current && queryRef.current === id) if (value.error) {
723
+ setSqlError(value.error);
724
+ setResultEvidence({
725
+ capture: captured,
726
+ sql: query,
727
+ error: value.error
728
+ });
729
+ } else setResult(value);
702
730
  } catch {
703
- if (alive.current && queryRef.current === id) setError("Query stopped or failed. Use one read-only SELECT or EXPLAIN; external files, networking and extensions are disabled.");
731
+ if (alive.current && queryRef.current === id) {
732
+ const message = "Query stopped or failed. Use one read-only SELECT or EXPLAIN; external files, networking and extensions are disabled.";
733
+ setSqlError(message);
734
+ setResultEvidence({
735
+ capture: captured,
736
+ sql: query,
737
+ error: message
738
+ });
739
+ }
704
740
  } finally {
705
741
  if (queryRef.current === id) {
706
742
  if (alive.current) setQueryId("");
@@ -710,12 +746,20 @@ function Workspace({ clusterId, contextId }) {
710
746
  };
711
747
  const applyPreset = async (title, selectedCapture, execute = false) => {
712
748
  const captured = selectedCapture ?? captures.find((item) => item.id === captureId);
749
+ if (queryRef.current || !selectedCapture && captureController.current) return;
750
+ if (title === "Recent") {
751
+ const query = examples[0][1];
752
+ setSql(query);
753
+ setSelectedSqlRow(void 0);
754
+ if (execute && captured) runQuery(captured, query);
755
+ return;
756
+ }
713
757
  if (queryRef.current || !selectedCapture && captureController.current || !captured || captured.state === "collecting" || deleting.current.has(captured.id)) return;
714
758
  const draft = execute ? examples[0][1] : sqlDraft.current;
715
759
  const id = (0, node_crypto.randomUUID)();
716
760
  queryRef.current = id;
717
761
  setQueryId(id);
718
- setError("");
762
+ setSqlError("");
719
763
  let prepared;
720
764
  try {
721
765
  let keys = captureKeys.current.get(captured.id);
@@ -729,14 +773,14 @@ function Workspace({ clusterId, contextId }) {
729
773
  }
730
774
  if (!alive.current || queryRef.current !== id || captureSelection.current !== captured.id) return;
731
775
  if (sqlDraft.current !== draft) {
732
- setError("SQL draft changed while detecting JSON fields. Select the preset again to replace it.");
776
+ setSqlError("SQL draft changed while detecting JSON fields. Select the preset again to replace it.");
733
777
  return;
734
778
  }
735
- prepared = require_power_log_json_preset.buildJsonProjectionSql(keys, { recent: title === "Recent" });
779
+ prepared = require_power_log_json_preset.buildJsonProjectionSql(keys);
736
780
  setSql(prepared);
737
781
  setSelectedSqlRow(void 0);
738
782
  } catch {
739
- if (alive.current) setError("Could not detect JSON fields for this snapshot. Try the preset again.");
783
+ if (alive.current) setSqlError("Could not detect JSON fields for this snapshot. Try the preset again.");
740
784
  } finally {
741
785
  if (queryRef.current === id) {
742
786
  queryRef.current = "";
@@ -762,7 +806,7 @@ function Workspace({ clusterId, contextId }) {
762
806
  const applySqlFilters = (filters) => {
763
807
  if (queryRef.current || captureController.current || sqlFilteringDisabled) return;
764
808
  if (!resultEvidence || resultEvidence.sql !== sql) {
765
- setError("Run the edited SQL query before filtering its results");
809
+ setSqlError("Run the edited SQL query before filtering its results");
766
810
  return;
767
811
  }
768
812
  try {
@@ -777,19 +821,19 @@ function Workspace({ clusterId, contextId }) {
777
821
  setSql(query);
778
822
  runQuery(void 0, query);
779
823
  } catch {
780
- setError("This JSON filter cannot be added to the query. Check the field path and value size.");
824
+ setSqlError("This JSON filter cannot be added to the query. Check the field path and value size.");
781
825
  }
782
826
  };
783
827
  const addSqlFilter = (column, filter) => {
784
828
  const columnNames = result?.columns.map((name) => name.replace(/[A-Z]/g, (letter) => letter.toLowerCase())) ?? [];
785
829
  if (!result?.columns.includes(column) || new Set(columnNames).size !== columnNames.length) {
786
- setError("JSON filters require unique result column names, ignoring case");
830
+ setSqlError("JSON filters require unique result column names, ignoring case");
787
831
  return;
788
832
  }
789
833
  const filters = activeSqlFilters?.filters ?? [];
790
834
  if (filters.some((current) => current.column === column && sameFieldFilter(current, filter))) return;
791
835
  if (filters.length >= 32) {
792
- setError("Up to 32 JSON filters can be combined");
836
+ setSqlError("Up to 32 JSON filters can be combined");
793
837
  return;
794
838
  }
795
839
  applySqlFilters([...filters, {
@@ -831,12 +875,12 @@ function Workspace({ clusterId, contextId }) {
831
875
  const sql = event.detail?.sql;
832
876
  if (typeof sql !== "string" || !sql.trim() || sql.length > 65536) return;
833
877
  setTab("captures");
834
- setSql(sql);
835
878
  setSelectedSqlRow(void 0);
836
879
  if (!captureId) {
837
880
  const latest = captures.find((item) => item.state !== "collecting" && item.target.uid === target?.uid);
838
881
  if (latest) setCaptureId(latest.id);
839
882
  }
883
+ setSql(sql);
840
884
  requestAnimationFrame(() => document.getElementById("power-logs-sql")?.focus());
841
885
  };
842
886
  window.addEventListener("power-logs-sql-draft", insert);
@@ -847,12 +891,19 @@ function Workspace({ clusterId, contextId }) {
847
891
  target?.uid
848
892
  ]);
849
893
  const attachAi = () => {
850
- const resource = tab === "captures" ? result && resultEvidence ? resultEvidence.capture.target : chosenCapture?.target ?? target : target;
851
- const summary = tab === "captures" && result && resultEvidence ? require_power_logs_ai.powerLogQueryEvidence(resultEvidence.capture, resultEvidence.sql, selectedSqlRow ? {
852
- ...result,
894
+ const queryEvidence = result ?? (resultEvidence?.error ? {
895
+ columns: [],
896
+ rows: [],
897
+ truncated: false,
898
+ elapsedMs: 0,
899
+ error: resultEvidence.error
900
+ } : void 0);
901
+ const resource = tab === "captures" ? queryEvidence && resultEvidence ? resultEvidence.capture.target : chosenCapture?.target ?? target : target;
902
+ const summary = tab === "captures" && queryEvidence && resultEvidence ? require_power_logs_ai.powerLogQueryEvidence(resultEvidence.capture, resultEvidence.sql, selectedSqlRow ? {
903
+ ...queryEvidence,
853
904
  rows: [selectedSqlRow.row],
854
905
  truncated: false
855
- } : result) : tab === "live" && selected ? require_power_logs_ai.powerLogLineEvidence(resource, selected) : JSON.stringify({
906
+ } : queryEvidence) : tab === "live" && selected ? require_power_logs_ai.powerLogLineEvidence(resource, selected) : JSON.stringify({
856
907
  target: resource,
857
908
  ...tab === "captures" ? {
858
909
  sql,
@@ -865,6 +916,7 @@ function Workspace({ clusterId, contextId }) {
865
916
  });
866
917
  if (!require_ai_diagnostic_handoff.explainDiagnosticFinding({
867
918
  clusterId,
919
+ source: "logs",
868
920
  title: tab === "captures" ? selectedSqlRow ? "SQL row" : "SQL query" : selected ? "Log line" : "Power Logs",
869
921
  summary: summary.slice(0, 6e3),
870
922
  ...resource ? { resource: {
@@ -879,7 +931,9 @@ function Workspace({ clusterId, contextId }) {
879
931
  deleting.current.add(id);
880
932
  setDeletingCount(deleting.current.size);
881
933
  try {
934
+ await drafts.flush();
882
935
  await client.delete(id);
936
+ drafts.forget(id);
883
937
  if (id === captureSelection.current) {
884
938
  setCaptureId("");
885
939
  setResult(void 0);
@@ -910,6 +964,7 @@ function Workspace({ clusterId, contextId }) {
910
964
  className: "power-logs",
911
965
  "data-testid": "power-logs-page",
912
966
  "data-compact": compact || void 0,
967
+ "data-hide-resource-name": hideResourceName || void 0,
913
968
  children: [
914
969
  /* @__PURE__ */ require_jsx_runtime.jsx("style", { children: require_power_logs_css.powerLogsStyles }),
915
970
  /* @__PURE__ */ require_jsx_runtime.jsxs("header", { children: [
@@ -950,11 +1005,7 @@ function Workspace({ clusterId, contextId }) {
950
1005
  onClick: () => {
951
1006
  setTab("captures");
952
1007
  const latest = captures.find((item) => item.state !== "collecting" && (!target || item.target.uid === target.uid));
953
- if (!captureId && latest && !capturePending) {
954
- setCaptureId(latest.id);
955
- setSql(examples[0][1]);
956
- applyPreset("Recent", latest, true);
957
- }
1008
+ if (!captureId && latest && !capturePending) setCaptureId(latest.id);
958
1009
  refreshCaptures();
959
1010
  },
960
1011
  children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
@@ -1011,6 +1062,14 @@ function Workspace({ clusterId, contextId }) {
1011
1062
  preferences.compactRows = value;
1012
1063
  },
1013
1064
  children: "Compact density"
1065
+ }),
1066
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Switch, {
1067
+ checked: hideResourceName,
1068
+ onChange: (value) => {
1069
+ setHideResourceName(value);
1070
+ preferences.hideResourceNameColumn = value;
1071
+ },
1072
+ children: "Hide resource name column"
1014
1073
  })
1015
1074
  ] })]
1016
1075
  }),
@@ -1232,91 +1291,94 @@ function Workspace({ clusterId, contextId }) {
1232
1291
  ] }) : /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
1233
1292
  /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1234
1293
  className: "power-logs-toolbar power-logs-snapshot-bar",
1235
- children: [/* @__PURE__ */ require_jsx_runtime.jsx(LogSelect, {
1236
- label: "Capture snapshot",
1237
- onKeyDown: (event) => {
1238
- if (event.key !== "Delete" || !event.altKey || (event.target.value ?? "").length > 0) return;
1239
- const button = document.querySelector(".Select__menu .Select__option--is-focused [data-capture-delete]");
1240
- if (button && !button.disabled) {
1241
- event.preventDefault();
1242
- event.stopPropagation();
1243
- deleteCapture(button.dataset.captureDelete);
1244
- }
1245
- },
1246
- renderOption: (option, context) => {
1247
- const item = captures.find((item) => item.id === option.value);
1248
- if (context !== "menu" || !item) return option.label;
1249
- return /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1250
- className: "power-logs-snapshot-option",
1251
- title: option.label,
1252
- children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1253
- className: "power-logs-option-text",
1254
- children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: item.target.name }), /* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
1255
- require_date_time.formatDateTime(item.createdAt),
1256
- " · ",
1257
- item.rows.toLocaleString(),
1258
- " lines ·",
1259
- " ",
1260
- item.state[0].toUpperCase(),
1261
- item.state.slice(1)
1262
- ] })]
1263
- }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
1264
- type: "button",
1265
- "aria-label": `Delete snapshot ${item.target.name}`,
1266
- title: "Delete snapshot (Alt+Delete)",
1267
- "data-capture-delete": item.id,
1268
- onKeyDown: (event) => event.stopPropagation(),
1269
- disabled: !!queryId || capturePending || deletingCount > 0 || item.state === "collecting",
1270
- onMouseDown: (event) => {
1271
- event.preventDefault();
1272
- event.stopPropagation();
1273
- },
1274
- onClick: (event) => {
1275
- event.preventDefault();
1276
- event.stopPropagation();
1277
- deleteCapture(item.id);
1278
- },
1279
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Icon, {
1280
- material: "delete_outline",
1281
- size: 16,
1282
- tooltip: "Delete snapshot",
1283
- tooltipOverrideDisabled: true
1284
- })
1285
- })]
1286
- });
1287
- },
1288
- value: captureId,
1289
- disabled: !!queryId || capturePending || deletingCount > 0,
1290
- onChange: (value) => {
1291
- setCaptureId(value);
1292
- setResult(void 0);
1293
- setResultEvidence(void 0);
1294
- setSelectedSqlRow(void 0);
1295
- const snapshot = captures.find((item) => item.id === value);
1296
- if (snapshot) {
1297
- setSql(examples[0][1]);
1298
- applyPreset("Recent", snapshot, true);
1299
- }
1300
- },
1301
- options: [{
1302
- value: "",
1303
- label: "Choose a snapshot"
1304
- }, ...captures.map((item) => ({
1305
- value: item.id,
1306
- label: `${item.target.name} · ${require_date_time.formatDateTime(item.createdAt)} · ${item.rows.toLocaleString()} lines · ${item.state[0].toUpperCase()}${item.state.slice(1)}`
1307
- }))]
1308
- }), /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
1309
- type: "button",
1310
- disabled: !captureId || !!queryId || deleting.current.has(captureId),
1311
- title: "Export JSON Lines (NDJSON)",
1312
- onClick: () => {
1313
- client.export(captureId).catch(() => setError("Could not export capture"));
1314
- },
1315
- children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1316
- material: "download",
1317
- size: 16
1318
- }), " Export JSON"]
1319
- })]
1294
+ children: [
1295
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", {
1296
+ className: "power-logs-control-label",
1297
+ children: "Snapshot"
1298
+ }),
1299
+ /* @__PURE__ */ require_jsx_runtime.jsx(LogSelect, {
1300
+ label: "Capture snapshot",
1301
+ onKeyDown: (event) => {
1302
+ if (event.key !== "Delete" || !event.altKey || (event.target.value ?? "").length > 0) return;
1303
+ const button = document.querySelector(".Select__menu .Select__option--is-focused [data-capture-delete]");
1304
+ if (button && !button.disabled) {
1305
+ event.preventDefault();
1306
+ event.stopPropagation();
1307
+ deleteCapture(button.dataset.captureDelete);
1308
+ }
1309
+ },
1310
+ renderOption: (option, context) => {
1311
+ const item = captures.find((item) => item.id === option.value);
1312
+ if (context !== "menu" || !item) return option.label;
1313
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1314
+ className: "power-logs-snapshot-option",
1315
+ title: option.label,
1316
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1317
+ className: "power-logs-option-text",
1318
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: item.target.name }), /* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
1319
+ require_date_time.formatDateTime(item.createdAt),
1320
+ " · ",
1321
+ item.rows.toLocaleString(),
1322
+ " lines ·",
1323
+ " ",
1324
+ item.state[0].toUpperCase(),
1325
+ item.state.slice(1)
1326
+ ] })]
1327
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
1328
+ type: "button",
1329
+ "aria-label": `Delete snapshot ${item.target.name}`,
1330
+ title: "Delete snapshot (Alt+Delete)",
1331
+ "data-capture-delete": item.id,
1332
+ onKeyDown: (event) => event.stopPropagation(),
1333
+ disabled: !!queryId || capturePending || deletingCount > 0 || item.state === "collecting",
1334
+ onMouseDown: (event) => {
1335
+ event.preventDefault();
1336
+ event.stopPropagation();
1337
+ },
1338
+ onClick: (event) => {
1339
+ event.preventDefault();
1340
+ event.stopPropagation();
1341
+ deleteCapture(item.id);
1342
+ },
1343
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Icon, {
1344
+ material: "delete_outline",
1345
+ size: 16,
1346
+ tooltip: "Delete snapshot",
1347
+ tooltipOverrideDisabled: true
1348
+ })
1349
+ })]
1350
+ });
1351
+ },
1352
+ value: captureId,
1353
+ disabled: !!queryId || capturePending || deletingCount > 0,
1354
+ onChange: (value) => {
1355
+ setCaptureId(value);
1356
+ setResult(void 0);
1357
+ setResultEvidence(void 0);
1358
+ setSelectedSqlRow(void 0);
1359
+ setSqlError("");
1360
+ },
1361
+ options: [{
1362
+ value: "",
1363
+ label: "Choose a snapshot"
1364
+ }, ...captures.map((item) => ({
1365
+ value: item.id,
1366
+ label: `${item.target.name} · ${require_date_time.formatDateTime(item.createdAt)} · ${item.rows.toLocaleString()} lines · ${item.state[0].toUpperCase()}${item.state.slice(1)}`
1367
+ }))]
1368
+ }),
1369
+ /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
1370
+ type: "button",
1371
+ disabled: !captureId || !!queryId || deleting.current.has(captureId),
1372
+ title: "Export JSON Lines (NDJSON)",
1373
+ onClick: () => {
1374
+ client.export(captureId).catch(() => setError("Could not export capture"));
1375
+ },
1376
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1377
+ material: "download",
1378
+ size: 16
1379
+ }), " Export JSON"]
1380
+ })
1381
+ ]
1320
1382
  }),
1321
1383
  !!chosenCapture?.notes.length && /* @__PURE__ */ require_jsx_runtime.jsxs("details", {
1322
1384
  className: "power-logs-snapshot-notes power-logs-muted",
@@ -1331,6 +1393,7 @@ function Workspace({ clusterId, contextId }) {
1331
1393
  children: [
1332
1394
  /* @__PURE__ */ require_jsx_runtime.jsx("label", {
1333
1395
  htmlFor: "power-logs-sql",
1396
+ className: "power-logs-control-label",
1334
1397
  title: `Read-only SELECT · ${limits.queryMs / 1e3} s · ${limits.resultRows.toLocaleString()} rows · Schema: logs(timestamp INTEGER in milliseconds, pod, podUid, container, level, message, fieldsJson, previous INTEGER 0 or 1)`,
1335
1398
  children: "SQL query"
1336
1399
  }),
@@ -1363,7 +1426,7 @@ function Workspace({ clusterId, contextId }) {
1363
1426
  size: 16
1364
1427
  }),
1365
1428
  " ",
1366
- "SQL: ",
1429
+ "SQL · ",
1367
1430
  title
1368
1431
  ]
1369
1432
  }, title))
@@ -1375,30 +1438,15 @@ function Workspace({ clusterId, contextId }) {
1375
1438
  disabled: sqlFilteringDisabled,
1376
1439
  onRemove: (index) => applySqlFilters((activeSqlFilters?.filters ?? []).filter((_, item) => item !== index))
1377
1440
  }),
1378
- /* @__PURE__ */ require_jsx_runtime.jsx("textarea", {
1379
- rows: 2,
1441
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_sql_editor.SqlEditor, {
1380
1442
  id: "power-logs-sql",
1381
1443
  value: sql,
1382
- maxLength: 65536,
1383
- onChange: (event) => setSql(event.target.value),
1384
- spellCheck: false,
1385
- onClick: (event) => {
1386
- event.stopPropagation();
1387
- if (event.detail >= 3) {
1388
- event.preventDefault();
1389
- window.getSelection()?.removeAllRanges();
1390
- event.currentTarget.select();
1391
- }
1392
- },
1393
- onMouseDown: (event) => {
1394
- event.stopPropagation();
1395
- if (event.detail >= 3) {
1396
- event.preventDefault();
1397
- window.getSelection()?.removeAllRanges();
1398
- event.currentTarget.focus();
1399
- event.currentTarget.select();
1400
- }
1401
- }
1444
+ onChange: setSql
1445
+ }),
1446
+ sqlError && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
1447
+ role: "alert",
1448
+ className: "power-logs-notice power-logs-error power-logs-query-error",
1449
+ children: sqlError
1402
1450
  }),
1403
1451
  result ? /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
1404
1452
  /* @__PURE__ */ require_jsx_runtime.jsx("div", {
@@ -1445,10 +1493,11 @@ function Workspace({ clusterId, contextId }) {
1445
1493
  },
1446
1494
  children: row.map((cell, j) => /* @__PURE__ */ require_jsx_runtime.jsx("td", {
1447
1495
  "data-column": result.columns[j],
1448
- "data-level": result.columns[j] === "level" ? String(cell) : void 0,
1496
+ "data-level": require_power_log_sql_level.sqlCellLevel(result.columns[j], cell),
1449
1497
  children: /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_json_cell.LogJsonCell, {
1450
1498
  value: cell === null ? null : String(cell),
1451
- message: result.columns[j] === "message",
1499
+ column: result.columns[j],
1500
+ rawValue: cell,
1452
1501
  Icon,
1453
1502
  onFilter: sqlFilteringDisabled ? void 0 : (filter) => addSqlFilter(result.columns[j], filter)
1454
1503
  })
@@ -1457,7 +1506,7 @@ function Workspace({ clusterId, contextId }) {
1457
1506
  ]
1458
1507
  }), result.rows.length === 0 && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
1459
1508
  className: "power-logs-empty",
1460
- children: chosenCapture?.rows === 0 ? "This snapshot contains no log lines. Try a longer capture window." : "This query returned no rows. Adjust the query or choose SQL: Recent."
1509
+ children: chosenCapture?.rows === 0 ? "This snapshot contains no log lines. Try a longer capture window." : "This query returned no rows. Adjust the query or choose SQL · Recent."
1461
1510
  })]
1462
1511
  }), selectedSqlRow && /* @__PURE__ */ require_jsx_runtime.jsx(LogDetails, {
1463
1512
  record: selectedSqlRow.record,