@64mb/freelens-power 1.6.1 → 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 (85) hide show
  1. package/README.md +1 -1
  2. package/out/main/common/power-logs.js.map +1 -1
  3. package/out/main/common/resource-search-kind-priority.js +23 -0
  4. package/out/main/common/resource-search-kind-priority.js.map +1 -0
  5. package/out/main/common/store/logs-preferences-store.js +5 -0
  6. package/out/main/common/store/logs-preferences-store.js.map +1 -1
  7. package/out/main/dashboard-views.js +26 -0
  8. package/out/main/dashboard-views.js.map +1 -1
  9. package/out/main/power-logs-database.js +5 -0
  10. package/out/main/power-logs-database.js.map +1 -1
  11. package/out/main/power-logs-ipc.js +8 -0
  12. package/out/main/power-logs-ipc.js.map +1 -1
  13. package/out/main/power-logs-query.js +1 -0
  14. package/out/main/power-logs-query.js.map +1 -1
  15. package/out/main/resource-search-database.js +7 -2
  16. package/out/main/resource-search-database.js.map +1 -1
  17. package/out/main/resource-search-worker.js +6 -2
  18. package/out/main/resource-search-worker.js.map +1 -1
  19. package/out/renderer/_virtual/_global-external_react.js +2 -1
  20. package/out/renderer/ai-diagnostic-handoff.js +1 -0
  21. package/out/renderer/ai-diagnostic-handoff.js.map +1 -1
  22. package/out/renderer/ai-extras.js +7 -2
  23. package/out/renderer/ai-extras.js.map +1 -1
  24. package/out/renderer/ai.js +1 -1
  25. package/out/renderer/ai.js.map +1 -1
  26. package/out/renderer/appearance.js +2 -0
  27. package/out/renderer/appearance.js.map +1 -1
  28. package/out/renderer/dashboard.js +35 -8
  29. package/out/renderer/dashboard.js.map +1 -1
  30. package/out/renderer/index.js +2 -1
  31. package/out/renderer/index.js.map +1 -1
  32. package/out/renderer/power-log-json-cell.js +82 -0
  33. package/out/renderer/power-log-json-cell.js.map +1 -0
  34. package/out/renderer/power-log-json-fields.js +144 -0
  35. package/out/renderer/power-log-json-fields.js.map +1 -0
  36. package/out/renderer/power-log-json.js +223 -20
  37. package/out/renderer/power-log-json.js.map +1 -1
  38. package/out/renderer/power-log-message-tokens.js +60 -0
  39. package/out/renderer/power-log-message-tokens.js.map +1 -0
  40. package/out/renderer/power-log-message.js +26 -0
  41. package/out/renderer/power-log-message.js.map +1 -0
  42. package/out/renderer/power-log-sql-editor.js +91 -0
  43. package/out/renderer/power-log-sql-editor.js.map +1 -0
  44. package/out/renderer/power-logs-client.js +4 -0
  45. package/out/renderer/power-logs-client.js.map +1 -1
  46. package/out/renderer/power-logs-sql-drafts.js +76 -0
  47. package/out/renderer/power-logs-sql-drafts.js.map +1 -0
  48. package/out/renderer/power-logs-sql-row.js +5 -13
  49. package/out/renderer/power-logs-sql-row.js.map +1 -1
  50. package/out/renderer/power-logs.css.js +39 -6
  51. package/out/renderer/power-logs.css.js.map +1 -1
  52. package/out/renderer/power-logs.js +372 -135
  53. package/out/renderer/power-logs.js.map +1 -1
  54. package/out/renderer/preferences/logs-preferences.js +159 -38
  55. package/out/renderer/preferences/logs-preferences.js.map +1 -1
  56. package/out/renderer/preferences/preferences.js +2 -9
  57. package/out/renderer/preferences/preferences.js.map +1 -1
  58. package/out/renderer/preferences/section-navigation.js +44 -0
  59. package/out/renderer/preferences/section-navigation.js.map +1 -0
  60. package/out/renderer/resource-search-model.js +2 -1
  61. package/out/renderer/resource-search-model.js.map +1 -1
  62. package/out/renderer/resource-search-sync.js +1 -1
  63. package/out/renderer/resource-search-sync.js.map +1 -1
  64. package/out/renderer/resource-search.js +84 -81
  65. package/out/renderer/resource-search.js.map +1 -1
  66. package/out/renderer/src/common/power-log-field-filters.js +92 -0
  67. package/out/renderer/src/common/power-log-field-filters.js.map +1 -0
  68. package/out/renderer/src/common/power-log-json-filter-sql.js +106 -0
  69. package/out/renderer/src/common/power-log-json-filter-sql.js.map +1 -0
  70. package/out/renderer/src/common/power-log-json-preset.js +34 -0
  71. package/out/renderer/src/common/power-log-json-preset.js.map +1 -0
  72. package/out/renderer/src/common/power-log-level.js +171 -0
  73. package/out/renderer/src/common/power-log-level.js.map +1 -0
  74. package/out/renderer/src/common/power-log-parser.js +7 -29
  75. package/out/renderer/src/common/power-log-parser.js.map +1 -1
  76. package/out/renderer/src/common/power-log-sql-level.js +25 -0
  77. package/out/renderer/src/common/power-log-sql-level.js.map +1 -0
  78. package/out/renderer/src/common/power-logs-ai.js +4 -0
  79. package/out/renderer/src/common/power-logs-ai.js.map +1 -1
  80. package/out/renderer/src/common/power-logs.js.map +1 -1
  81. package/out/renderer/src/common/resource-search-kind-priority.js +28 -0
  82. package/out/renderer/src/common/resource-search-kind-priority.js.map +1 -0
  83. package/out/renderer/src/common/store/logs-preferences-store.js +5 -0
  84. package/out/renderer/src/common/store/logs-preferences-store.js.map +1 -1
  85. package/package.json +1 -1
@@ -8,12 +8,21 @@ const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runti
8
8
  const require_ai_diagnostic_handoff = require("./ai-diagnostic-handoff.js");
9
9
  const require_date_time = require("./src/common/date-time.js");
10
10
  const require_diagnostic_explain_button = require("./diagnostic-explain-button.js");
11
+ const require_power_log_field_filters = require("./src/common/power-log-field-filters.js");
12
+ const require_power_log_json_filter_sql = require("./src/common/power-log-json-filter-sql.js");
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");
11
15
  const require_power_logs_ai = require("./src/common/power-logs-ai.js");
16
+ const require_power_log_json_fields = require("./power-log-json-fields.js");
12
17
  const require_power_log_json = require("./power-log-json.js");
18
+ const require_power_log_message = require("./power-log-message.js");
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");
13
21
  const require_power_logs_css = require("./power-logs.css.js");
14
22
  const require_power_logs_client = require("./power-logs-client.js");
15
23
  const require_power_logs_collector = require("./power-logs-collector.js");
16
24
  const require_power_logs_entrypoints = require("./power-logs-entrypoints.js");
25
+ const require_power_logs_sql_drafts = require("./power-logs-sql-drafts.js");
17
26
  const require_power_logs_sql_row = require("./power-logs-sql-row.js");
18
27
  const require_resource_search_client = require("./resource-search-client.js");
19
28
  const require_power_logs_transport = require("./power-logs-transport.js");
@@ -38,8 +47,8 @@ var levels = [
38
47
  var examples = [
39
48
  ["Recent", "SELECT message, level, container, pod, timestamp FROM logs ORDER BY timestamp DESC NULLS LAST LIMIT 200"],
40
49
  ["Errors", "SELECT pod, container, count(*) AS errors FROM logs WHERE level IN ('error','fatal') GROUP BY pod, container ORDER BY errors DESC"],
41
- ["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"],
42
- ["JSON", "SELECT pod, json_extract(fieldsJson, '$.message') AS message FROM logs WHERE fieldsJson IS NOT NULL LIMIT 100"]
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"],
51
+ ["JSON", "SELECT fieldsJson FROM logs WHERE fieldsJson IS NOT NULL LIMIT 100"]
43
52
  ];
44
53
  var sqlColumnWidth = (column) => column === "message" || column === "fieldsJson" ? 360 : column === "level" ? 90 : 200;
45
54
  function initialTarget() {
@@ -127,7 +136,10 @@ var SourcePicker = require__global_external_mobx_react.observer(() => {
127
136
  return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
128
137
  className: "power-logs-source",
129
138
  children: [
130
- /* @__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
+ }),
131
143
  /* @__PURE__ */ require_jsx_runtime.jsx(LogSelect, {
132
144
  label: "Resource",
133
145
  value,
@@ -176,6 +188,7 @@ function LogRows({ records, loading, selected, onSelect, follow, setFollow, comp
176
188
  follow,
177
189
  compact
178
190
  ]);
191
+ const levelCharacters = require__global_external_react.useMemo(() => records.reduce((width, record) => Math.max(width, record.level.length), 4), [records]);
179
192
  const rowHeight = compact ? 20 : 34;
180
193
  const start = Math.max(0, Math.floor(top / rowHeight) - 5);
181
194
  const visible = records.slice(start, start + Math.ceil(height / rowHeight) + 10);
@@ -184,6 +197,7 @@ function LogRows({ records, loading, selected, onSelect, follow, setFollow, comp
184
197
  children: /* @__PURE__ */ require_jsx_runtime.jsx("div", {
185
198
  className: "power-logs-stream",
186
199
  ref: viewport,
200
+ style: { "--power-log-level-ch": levelCharacters },
187
201
  "aria-label": "Log lines",
188
202
  "aria-busy": loading,
189
203
  onScroll: (event) => {
@@ -220,30 +234,29 @@ function LogRows({ records, loading, selected, onSelect, follow, setFollow, comp
220
234
  }),
221
235
  /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
222
236
  className: "power-log-origin",
223
- title: sourceKey(record),
237
+ id: `power-log-source-${record.key}`,
224
238
  children: [
225
- record.container,
239
+ record.pod,
226
240
  record.previous ? " (previous)" : "",
227
- " · ",
228
- 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
+ })
229
245
  ]
230
246
  }),
231
- /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: record.message })
247
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_message.LogMessage, { text: record.message })
232
248
  ]
233
249
  }, record.key))
234
250
  })
235
251
  })
236
252
  });
237
253
  }
238
- function LogDetails({ record, jsonColumn, onClose }) {
254
+ function LogDetails({ record, jsonColumn, onClose, onFilter }) {
239
255
  const structuredFields = require__global_external_react.useMemo(() => {
240
256
  if (!record?.fieldsJson) return "";
241
- try {
242
- return JSON.stringify(JSON.parse(record.fieldsJson), null, 2);
243
- } catch {
244
- return record.fieldsJson;
245
- }
257
+ return require_power_log_json_fields.formatJsonLiteral(record.fieldsJson);
246
258
  }, [record?.fieldsJson]);
259
+ const fieldsEnabled = require__global_external_react.useMemo(() => !require_power_log_json_fields.jsonHasDuplicateKeys(record.fieldsJson ?? ""), [record.fieldsJson]);
247
260
  const [copyFeedback, setCopyFeedback] = require__global_external_react.useState("");
248
261
  require__global_external_react.useEffect(() => setCopyFeedback(""), [structuredFields]);
249
262
  return /* @__PURE__ */ require_jsx_runtime.jsxs("aside", {
@@ -262,9 +275,9 @@ function LogDetails({ record, jsonColumn, onClose }) {
262
275
  })] }),
263
276
  /* @__PURE__ */ require_jsx_runtime.jsx("p", {
264
277
  className: "power-logs-muted",
265
- children: [record.pod && sourceKey(record), record.level].filter(Boolean).join(" · ")
278
+ children: [record.pod && sourceKey(record), record.level.toUpperCase()].filter(Boolean).join(" · ")
266
279
  }),
267
- record.message && /* @__PURE__ */ require_jsx_runtime.jsx("pre", { children: record.message }),
280
+ record.message && /* @__PURE__ */ require_jsx_runtime.jsx("pre", { children: /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_message.LogMessage, { text: record.message }) }),
268
281
  record.fieldsJson && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
269
282
  className: "power-logs-structured-heading",
270
283
  children: [
@@ -293,31 +306,72 @@ function LogDetails({ record, jsonColumn, onClose }) {
293
306
  children: copyFeedback
294
307
  })
295
308
  ]
296
- }), /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_json.LogJson, { text: structuredFields })] })
309
+ }), /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_json.LogJson, {
310
+ text: structuredFields,
311
+ fieldsEnabled,
312
+ Icon,
313
+ onFilter
314
+ })] })
297
315
  ]
298
316
  });
299
317
  }
318
+ var sameFieldFilter = (a, b) => a.operator === b.operator && a.path.length === b.path.length && a.path.every((part, index) => part === b.path[index]) && require_power_log_field_filters.jsonValuesEqual(a.value, b.value);
319
+ var fieldFilterLabel = (filter) => `${filter.path.reduce((path, part) => `${path}[${typeof part === "number" ? part : JSON.stringify(part)}]`, "$")} ${filter.operator === "eq" ? "=" : "!="} ${JSON.stringify(filter.value)}`;
320
+ function FieldFilters({ filters, sql = false, disabled = false, onRemove }) {
321
+ if (!filters.length) return null;
322
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
323
+ className: "power-logs-field-filters",
324
+ "aria-label": sql ? "SQL JSON filters" : "Live JSON filters",
325
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
326
+ className: "power-logs-muted",
327
+ children: [sql ? "Result filters" : "JSON filters", " · AND"]
328
+ }), filters.map((filter, index) => {
329
+ const label = `${filter.column ? `${filter.column} ` : ""}${fieldFilterLabel(filter)}`;
330
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
331
+ type: "button",
332
+ disabled,
333
+ title: label,
334
+ "aria-label": `Remove JSON filter ${label}`,
335
+ onClick: () => onRemove(index),
336
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", { children: label }), /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
337
+ material: "close",
338
+ size: 14
339
+ })]
340
+ }, index);
341
+ })]
342
+ });
343
+ }
300
344
  function Workspace({ clusterId, contextId }) {
301
345
  const client = require__global_external_react.useMemo(() => require_power_logs_client.createPowerLogsClient({
302
346
  clusterId,
303
347
  contextId
304
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]);
305
353
  const [target, setTarget] = require__global_external_react.useState(initialTarget);
306
354
  const [records, setRecords] = require__global_external_react.useState([]);
307
355
  const [sources, setSources] = require__global_external_react.useState([]);
308
356
  const [notes, setNotes] = require__global_external_react.useState([]);
309
357
  const [status, setStatus] = require__global_external_react.useState("Choose a resource to read its logs");
310
358
  const [error, setError] = require__global_external_react.useState("");
359
+ const [sqlError, setSqlError] = require__global_external_react.useState("");
311
360
  const [tab, setTab] = require__global_external_react.useState("live");
312
361
  const [text, setText] = require__global_external_react.useState("");
313
362
  const [level, setLevel] = require__global_external_react.useState("");
314
363
  const [source, setSource] = require__global_external_react.useState(initialSource);
364
+ const [fieldFilters, setFieldFilters] = require__global_external_react.useState([]);
365
+ const matchFields = require__global_external_react.useMemo(() => require_power_log_field_filters.compileJsonFilters(fieldFilters), [fieldFilters]);
366
+ const parsedFields = require__global_external_react.useRef(/* @__PURE__ */ new WeakMap());
367
+ const [sqlFieldFilters, setSqlFieldFilters] = require__global_external_react.useState();
315
368
  const preferences = require_logs_preferences_store.LogsPreferencesStore.getInstanceOrCreate();
316
369
  const limits = require_power_logs_limits.normalizePowerLogsLimits(preferences.limits);
317
370
  require__global_external_react.useEffect(() => {
318
371
  require_power_logs_limits.applyPowerLogsLimits(preferences.limits);
319
372
  }, [preferences.limits]);
320
373
  const [compact, setCompact] = require__global_external_react.useState(preferences.compactRows === true);
374
+ const [hideResourceName, setHideResourceName] = require__global_external_react.useState(preferences.hideResourceNameColumn === true);
321
375
  const [selected, setSelected] = require__global_external_react.useState();
322
376
  const [selectedSqlRow, setSelectedSqlRow] = require__global_external_react.useState();
323
377
  const [liveCoverage, setLiveCoverage] = require__global_external_react.useState();
@@ -325,16 +379,35 @@ function Workspace({ clusterId, contextId }) {
325
379
  const [follow, setFollow] = require__global_external_react.useState(true);
326
380
  const [busy, setBusy] = require__global_external_react.useState(() => Boolean(initialTarget()));
327
381
  const [captures, setCaptures] = require__global_external_react.useState([]);
328
- const [captureId, setCaptureId] = require__global_external_react.useState("");
382
+ const [captureId, updateCaptureId] = require__global_external_react.useState("");
329
383
  const captureSelection = require__global_external_react.useRef(captureId);
330
384
  captureSelection.current = captureId;
385
+ const setCaptureId = (id, snapshot = captures.find((item) => item.id === id)) => {
386
+ if (id !== captureSelection.current) drafts.flush();
387
+ captureSelection.current = id;
388
+ const query = snapshot ? drafts.read(snapshot, examples[0][1]) : examples[0][1];
389
+ sqlDraft.current = query;
390
+ updateSql(query);
391
+ updateCaptureId(id);
392
+ };
331
393
  const deleting = require__global_external_react.useRef(/* @__PURE__ */ new Set());
332
394
  const [deletingCount, setDeletingCount] = require__global_external_react.useState(0);
333
395
  const [capturing, setCapturing] = require__global_external_react.useState();
334
396
  const [capturePending, setCapturePending] = require__global_external_react.useState(false);
335
397
  const [captureMinutes, setCaptureMinutes] = require__global_external_react.useState(15);
336
398
  const [previous, setPrevious] = require__global_external_react.useState(false);
337
- const [sql, setSql] = require__global_external_react.useState(examples[0][1]);
399
+ const [sql, updateSql] = require__global_external_react.useState(examples[0][1]);
400
+ const sqlDraft = require__global_external_react.useRef(sql);
401
+ sqlDraft.current = sql;
402
+ const setSql = (query) => {
403
+ sqlDraft.current = query;
404
+ updateSql(query);
405
+ if (captureSelection.current) drafts.set(captureSelection.current, query);
406
+ };
407
+ const captureKeys = require__global_external_react.useRef(/* @__PURE__ */ new Map());
408
+ require__global_external_react.useEffect(() => {
409
+ setSqlFieldFilters((current) => current && (current.rendered !== sql || current.captureId !== captureId) ? void 0 : current);
410
+ }, [sql, captureId]);
338
411
  const [result, setResult] = require__global_external_react.useState();
339
412
  const [resultEvidence, setResultEvidence] = require__global_external_react.useState();
340
413
  const [resultPage, setResultPage] = require__global_external_react.useState(0);
@@ -479,6 +552,7 @@ function Workspace({ clusterId, contextId }) {
479
552
  };
480
553
  require__global_external_react.useEffect(() => {
481
554
  setText("");
555
+ setFieldFilters([]);
482
556
  setSources([]);
483
557
  setTab("live");
484
558
  readHistory();
@@ -609,11 +683,11 @@ function Workspace({ clusterId, contextId }) {
609
683
  if (current) try {
610
684
  const saved = await client.finish(current.id, partial || controller.signal.aborted, [...new Set(captureNotes)].slice(0, 20));
611
685
  if (alive.current) {
612
- setCaptureId(saved.id);
686
+ setCaptureId(saved.id, saved);
613
687
  setCaptures((items) => [saved, ...items.filter((item) => item.id !== saved.id)]);
614
688
  setTab("captures");
615
689
  setSql(examples[0][1]);
616
- runQuery(saved, examples[0][1]);
690
+ applyPreset("Recent", saved, true);
617
691
  }
618
692
  } catch {
619
693
  if (alive.current) setError("The temporary capture could not be finalized");
@@ -637,6 +711,7 @@ function Workspace({ clusterId, contextId }) {
637
711
  setError("");
638
712
  setResult(void 0);
639
713
  setSelectedSqlRow(void 0);
714
+ setSqlError("");
640
715
  setResultEvidence({
641
716
  capture: captured,
642
717
  sql: query
@@ -644,10 +719,24 @@ function Workspace({ clusterId, contextId }) {
644
719
  setResultPage(0);
645
720
  try {
646
721
  const value = await client.query(captured.id, id, query);
647
- if (alive.current && queryRef.current === id) if (value.error) setError(value.error);
648
- 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);
649
730
  } catch {
650
- 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
+ }
651
740
  } finally {
652
741
  if (queryRef.current === id) {
653
742
  if (alive.current) setQueryId("");
@@ -655,11 +744,123 @@ function Workspace({ clusterId, contextId }) {
655
744
  }
656
745
  }
657
746
  };
658
- const filtered = require__global_external_react.useMemo(() => records.filter((record) => (!text || record.message.toLocaleLowerCase().includes(text.toLocaleLowerCase())) && (!level || record.level === level) && (!source || (source.startsWith("container:") ? record.container === source.slice(10) : sourceKey(record) === source))), [
747
+ const applyPreset = async (title, selectedCapture, execute = false) => {
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
+ }
757
+ if (queryRef.current || !selectedCapture && captureController.current || !captured || captured.state === "collecting" || deleting.current.has(captured.id)) return;
758
+ const draft = execute ? examples[0][1] : sqlDraft.current;
759
+ const id = (0, node_crypto.randomUUID)();
760
+ queryRef.current = id;
761
+ setQueryId(id);
762
+ setSqlError("");
763
+ let prepared;
764
+ try {
765
+ let keys = captureKeys.current.get(captured.id);
766
+ if (!keys) {
767
+ const schema = await client.query(captured.id, id, require_power_log_json_preset.JSON_KEY_DISCOVERY_SQL);
768
+ if (schema.error) throw new Error(schema.error);
769
+ const keyColumn = schema.columns.indexOf("key");
770
+ keys = schema.truncated ? ["\0"] : keyColumn < 0 ? [] : schema.rows.map((row) => row[keyColumn]).filter((key) => typeof key === "string");
771
+ captureKeys.current.set(captured.id, keys);
772
+ if (captureKeys.current.size > 10) captureKeys.current.delete(captureKeys.current.keys().next().value);
773
+ }
774
+ if (!alive.current || queryRef.current !== id || captureSelection.current !== captured.id) return;
775
+ if (sqlDraft.current !== draft) {
776
+ setSqlError("SQL draft changed while detecting JSON fields. Select the preset again to replace it.");
777
+ return;
778
+ }
779
+ prepared = require_power_log_json_preset.buildJsonProjectionSql(keys);
780
+ setSql(prepared);
781
+ setSelectedSqlRow(void 0);
782
+ } catch {
783
+ if (alive.current) setSqlError("Could not detect JSON fields for this snapshot. Try the preset again.");
784
+ } finally {
785
+ if (queryRef.current === id) {
786
+ queryRef.current = "";
787
+ if (alive.current) setQueryId("");
788
+ }
789
+ }
790
+ if (prepared && execute && alive.current) runQuery(captured, prepared);
791
+ };
792
+ const addLiveFilter = (filter) => {
793
+ if (!require_power_log_field_filters.jsonValuesEqual(filter.value, filter.value)) {
794
+ setError("This JSON value is too large or unsupported for filtering");
795
+ return;
796
+ }
797
+ if (fieldFilters.some((current) => sameFieldFilter(current, filter))) return;
798
+ if (fieldFilters.length >= 32) {
799
+ setError("Up to 32 JSON filters can be combined");
800
+ return;
801
+ }
802
+ setFieldFilters([...fieldFilters, filter]);
803
+ };
804
+ const activeSqlFilters = sqlFieldFilters?.rendered === sql && sqlFieldFilters.captureId === captureId ? sqlFieldFilters : void 0;
805
+ const sqlFilteringDisabled = Boolean(queryId || capturePending || deleting.current.has(captureId) || !captures.some((capture) => capture.id === captureId && capture.state !== "collecting"));
806
+ const applySqlFilters = (filters) => {
807
+ if (queryRef.current || captureController.current || sqlFilteringDisabled) return;
808
+ if (!resultEvidence || resultEvidence.sql !== sql) {
809
+ setSqlError("Run the edited SQL query before filtering its results");
810
+ return;
811
+ }
812
+ try {
813
+ const base = activeSqlFilters?.base ?? sql;
814
+ const query = require_power_log_json_filter_sql.buildJsonFilterSql(base, filters);
815
+ setSqlFieldFilters({
816
+ base,
817
+ rendered: query,
818
+ captureId,
819
+ filters
820
+ });
821
+ setSql(query);
822
+ runQuery(void 0, query);
823
+ } catch {
824
+ setSqlError("This JSON filter cannot be added to the query. Check the field path and value size.");
825
+ }
826
+ };
827
+ const addSqlFilter = (column, filter) => {
828
+ const columnNames = result?.columns.map((name) => name.replace(/[A-Z]/g, (letter) => letter.toLowerCase())) ?? [];
829
+ if (!result?.columns.includes(column) || new Set(columnNames).size !== columnNames.length) {
830
+ setSqlError("JSON filters require unique result column names, ignoring case");
831
+ return;
832
+ }
833
+ const filters = activeSqlFilters?.filters ?? [];
834
+ if (filters.some((current) => current.column === column && sameFieldFilter(current, filter))) return;
835
+ if (filters.length >= 32) {
836
+ setSqlError("Up to 32 JSON filters can be combined");
837
+ return;
838
+ }
839
+ applySqlFilters([...filters, {
840
+ ...filter,
841
+ column
842
+ }]);
843
+ };
844
+ const matchesRecordFields = (record) => {
845
+ if (!fieldFilters.length) return true;
846
+ if (!parsedFields.current.has(record)) {
847
+ let fields;
848
+ try {
849
+ fields = JSON.parse(record.fieldsJson ?? "null");
850
+ } catch {
851
+ fields = void 0;
852
+ }
853
+ parsedFields.current.set(record, fields);
854
+ }
855
+ return matchFields(parsedFields.current.get(record));
856
+ };
857
+ const filtered = require__global_external_react.useMemo(() => records.filter((record) => (!text || record.message.toLocaleLowerCase().includes(text.toLocaleLowerCase())) && (!level || record.level === level) && (!source || (source.startsWith("container:") ? record.container === source.slice(10) : sourceKey(record) === source)) && matchesRecordFields(record)), [
659
858
  records,
660
859
  text,
661
860
  level,
662
- source
861
+ source,
862
+ fieldFilters,
863
+ matchFields
663
864
  ]);
664
865
  const chosenCapture = captures.find((item) => item.id === captureId);
665
866
  const collectionNotes = tab === "live" ? [.../* @__PURE__ */ new Set([...notes, ...live ? liveCoverage?.notes ?? [] : []])] : [];
@@ -674,12 +875,12 @@ function Workspace({ clusterId, contextId }) {
674
875
  const sql = event.detail?.sql;
675
876
  if (typeof sql !== "string" || !sql.trim() || sql.length > 65536) return;
676
877
  setTab("captures");
677
- setSql(sql);
678
878
  setSelectedSqlRow(void 0);
679
879
  if (!captureId) {
680
880
  const latest = captures.find((item) => item.state !== "collecting" && item.target.uid === target?.uid);
681
881
  if (latest) setCaptureId(latest.id);
682
882
  }
883
+ setSql(sql);
683
884
  requestAnimationFrame(() => document.getElementById("power-logs-sql")?.focus());
684
885
  };
685
886
  window.addEventListener("power-logs-sql-draft", insert);
@@ -690,12 +891,19 @@ function Workspace({ clusterId, contextId }) {
690
891
  target?.uid
691
892
  ]);
692
893
  const attachAi = () => {
693
- const resource = tab === "captures" ? result && resultEvidence ? resultEvidence.capture.target : chosenCapture?.target ?? target : target;
694
- const summary = tab === "captures" && result && resultEvidence ? require_power_logs_ai.powerLogQueryEvidence(resultEvidence.capture, resultEvidence.sql, selectedSqlRow ? {
695
- ...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,
696
904
  rows: [selectedSqlRow.row],
697
905
  truncated: false
698
- } : 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({
699
907
  target: resource,
700
908
  ...tab === "captures" ? {
701
909
  sql,
@@ -708,6 +916,7 @@ function Workspace({ clusterId, contextId }) {
708
916
  });
709
917
  if (!require_ai_diagnostic_handoff.explainDiagnosticFinding({
710
918
  clusterId,
919
+ source: "logs",
711
920
  title: tab === "captures" ? selectedSqlRow ? "SQL row" : "SQL query" : selected ? "Log line" : "Power Logs",
712
921
  summary: summary.slice(0, 6e3),
713
922
  ...resource ? { resource: {
@@ -722,7 +931,9 @@ function Workspace({ clusterId, contextId }) {
722
931
  deleting.current.add(id);
723
932
  setDeletingCount(deleting.current.size);
724
933
  try {
934
+ await drafts.flush();
725
935
  await client.delete(id);
936
+ drafts.forget(id);
726
937
  if (id === captureSelection.current) {
727
938
  setCaptureId("");
728
939
  setResult(void 0);
@@ -753,6 +964,7 @@ function Workspace({ clusterId, contextId }) {
753
964
  className: "power-logs",
754
965
  "data-testid": "power-logs-page",
755
966
  "data-compact": compact || void 0,
967
+ "data-hide-resource-name": hideResourceName || void 0,
756
968
  children: [
757
969
  /* @__PURE__ */ require_jsx_runtime.jsx("style", { children: require_power_logs_css.powerLogsStyles }),
758
970
  /* @__PURE__ */ require_jsx_runtime.jsxs("header", { children: [
@@ -793,11 +1005,7 @@ function Workspace({ clusterId, contextId }) {
793
1005
  onClick: () => {
794
1006
  setTab("captures");
795
1007
  const latest = captures.find((item) => item.state !== "collecting" && (!target || item.target.uid === target.uid));
796
- if (!captureId && latest && !capturePending) {
797
- setCaptureId(latest.id);
798
- setSql(examples[0][1]);
799
- runQuery(latest, examples[0][1]);
800
- }
1008
+ if (!captureId && latest && !capturePending) setCaptureId(latest.id);
801
1009
  refreshCaptures();
802
1010
  },
803
1011
  children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
@@ -854,6 +1062,14 @@ function Workspace({ clusterId, contextId }) {
854
1062
  preferences.compactRows = value;
855
1063
  },
856
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"
857
1073
  })
858
1074
  ] })]
859
1075
  }),
@@ -1036,6 +1252,10 @@ function Workspace({ clusterId, contextId }) {
1036
1252
  })
1037
1253
  ]
1038
1254
  }),
1255
+ /* @__PURE__ */ require_jsx_runtime.jsx(FieldFilters, {
1256
+ filters: fieldFilters,
1257
+ onRemove: (index) => setFieldFilters(fieldFilters.filter((_, item) => item !== index))
1258
+ }),
1039
1259
  /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1040
1260
  className: "power-logs-muted power-logs-status",
1041
1261
  role: "status",
@@ -1064,97 +1284,101 @@ function Workspace({ clusterId, contextId }) {
1064
1284
  compact
1065
1285
  }), selected && /* @__PURE__ */ require_jsx_runtime.jsx(LogDetails, {
1066
1286
  record: selected,
1067
- onClose: () => setSelected(void 0)
1287
+ onClose: () => setSelected(void 0),
1288
+ onFilter: addLiveFilter
1068
1289
  })]
1069
1290
  })
1070
1291
  ] }) : /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
1071
1292
  /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1072
1293
  className: "power-logs-toolbar power-logs-snapshot-bar",
1073
- children: [/* @__PURE__ */ require_jsx_runtime.jsx(LogSelect, {
1074
- label: "Capture snapshot",
1075
- onKeyDown: (event) => {
1076
- if (event.key !== "Delete" || !event.altKey || (event.target.value ?? "").length > 0) return;
1077
- const button = document.querySelector(".Select__menu .Select__option--is-focused [data-capture-delete]");
1078
- if (button && !button.disabled) {
1079
- event.preventDefault();
1080
- event.stopPropagation();
1081
- deleteCapture(button.dataset.captureDelete);
1082
- }
1083
- },
1084
- renderOption: (option, context) => {
1085
- const item = captures.find((item) => item.id === option.value);
1086
- if (context !== "menu" || !item) return option.label;
1087
- return /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1088
- className: "power-logs-snapshot-option",
1089
- title: option.label,
1090
- children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1091
- className: "power-logs-option-text",
1092
- children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: item.target.name }), /* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
1093
- require_date_time.formatDateTime(item.createdAt),
1094
- " · ",
1095
- item.rows.toLocaleString(),
1096
- " lines ·",
1097
- " ",
1098
- item.state[0].toUpperCase(),
1099
- item.state.slice(1)
1100
- ] })]
1101
- }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
1102
- type: "button",
1103
- "aria-label": `Delete snapshot ${item.target.name}`,
1104
- title: "Delete snapshot (Alt+Delete)",
1105
- "data-capture-delete": item.id,
1106
- onKeyDown: (event) => event.stopPropagation(),
1107
- disabled: !!queryId || capturePending || deletingCount > 0 || item.state === "collecting",
1108
- onMouseDown: (event) => {
1109
- event.preventDefault();
1110
- event.stopPropagation();
1111
- },
1112
- onClick: (event) => {
1113
- event.preventDefault();
1114
- event.stopPropagation();
1115
- deleteCapture(item.id);
1116
- },
1117
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Icon, {
1118
- material: "delete_outline",
1119
- size: 16,
1120
- tooltip: "Delete snapshot",
1121
- tooltipOverrideDisabled: true
1122
- })
1123
- })]
1124
- });
1125
- },
1126
- value: captureId,
1127
- disabled: !!queryId || capturePending || deletingCount > 0,
1128
- onChange: (value) => {
1129
- setCaptureId(value);
1130
- setResult(void 0);
1131
- setResultEvidence(void 0);
1132
- setSelectedSqlRow(void 0);
1133
- const snapshot = captures.find((item) => item.id === value);
1134
- if (snapshot) {
1135
- setSql(examples[0][1]);
1136
- runQuery(snapshot, examples[0][1]);
1137
- }
1138
- },
1139
- options: [{
1140
- value: "",
1141
- label: "Choose a snapshot"
1142
- }, ...captures.map((item) => ({
1143
- value: item.id,
1144
- label: `${item.target.name} · ${require_date_time.formatDateTime(item.createdAt)} · ${item.rows.toLocaleString()} lines · ${item.state[0].toUpperCase()}${item.state.slice(1)}`
1145
- }))]
1146
- }), /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
1147
- type: "button",
1148
- disabled: !captureId || !!queryId || deleting.current.has(captureId),
1149
- title: "Export JSON Lines (NDJSON)",
1150
- onClick: () => {
1151
- client.export(captureId).catch(() => setError("Could not export capture"));
1152
- },
1153
- children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1154
- material: "download",
1155
- size: 16
1156
- }), " Export JSON"]
1157
- })]
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
+ ]
1158
1382
  }),
1159
1383
  !!chosenCapture?.notes.length && /* @__PURE__ */ require_jsx_runtime.jsxs("details", {
1160
1384
  className: "power-logs-snapshot-notes power-logs-muted",
@@ -1169,6 +1393,7 @@ function Workspace({ clusterId, contextId }) {
1169
1393
  children: [
1170
1394
  /* @__PURE__ */ require_jsx_runtime.jsx("label", {
1171
1395
  htmlFor: "power-logs-sql",
1396
+ className: "power-logs-control-label",
1172
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)`,
1173
1398
  children: "SQL query"
1174
1399
  }),
@@ -1193,26 +1418,35 @@ function Workspace({ clusterId, contextId }) {
1193
1418
  }),
1194
1419
  examples.map(([title, query]) => /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
1195
1420
  type: "button",
1196
- onClick: () => setSql(query),
1421
+ disabled: !!queryId || capturePending,
1422
+ onClick: () => title === "JSON" || title === "Recent" ? void applyPreset(title) : setSql(query),
1197
1423
  children: [
1198
1424
  /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1199
1425
  material: title === "Recent" ? "history" : title === "Errors" ? "error_outline" : title === "Timeline" ? "timeline" : "data_object",
1200
1426
  size: 16
1201
1427
  }),
1202
1428
  " ",
1203
- "SQL: ",
1429
+ "SQL · ",
1204
1430
  title
1205
1431
  ]
1206
1432
  }, title))
1207
1433
  ]
1208
1434
  }),
1209
- /* @__PURE__ */ require_jsx_runtime.jsx("textarea", {
1210
- rows: 2,
1435
+ /* @__PURE__ */ require_jsx_runtime.jsx(FieldFilters, {
1436
+ sql: true,
1437
+ filters: activeSqlFilters?.filters ?? [],
1438
+ disabled: sqlFilteringDisabled,
1439
+ onRemove: (index) => applySqlFilters((activeSqlFilters?.filters ?? []).filter((_, item) => item !== index))
1440
+ }),
1441
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_sql_editor.SqlEditor, {
1211
1442
  id: "power-logs-sql",
1212
1443
  value: sql,
1213
- maxLength: 65536,
1214
- onChange: (event) => setSql(event.target.value),
1215
- spellCheck: false
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
1216
1450
  }),
1217
1451
  result ? /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
1218
1452
  /* @__PURE__ */ require_jsx_runtime.jsx("div", {
@@ -1259,22 +1493,25 @@ function Workspace({ clusterId, contextId }) {
1259
1493
  },
1260
1494
  children: row.map((cell, j) => /* @__PURE__ */ require_jsx_runtime.jsx("td", {
1261
1495
  "data-column": result.columns[j],
1262
- "data-level": result.columns[j] === "level" ? String(cell) : void 0,
1263
- children: /* @__PURE__ */ require_jsx_runtime.jsx("span", {
1264
- className: "power-logs-cell",
1265
- title: cell === null ? "NULL" : String(cell),
1266
- children: cell === null ? "NULL" : String(cell)
1496
+ "data-level": require_power_log_sql_level.sqlCellLevel(result.columns[j], cell),
1497
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_json_cell.LogJsonCell, {
1498
+ value: cell === null ? null : String(cell),
1499
+ column: result.columns[j],
1500
+ rawValue: cell,
1501
+ Icon,
1502
+ onFilter: sqlFilteringDisabled ? void 0 : (filter) => addSqlFilter(result.columns[j], filter)
1267
1503
  })
1268
1504
  }, j))
1269
1505
  }, i)) })
1270
1506
  ]
1271
1507
  }), result.rows.length === 0 && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
1272
1508
  className: "power-logs-empty",
1273
- 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."
1274
1510
  })]
1275
1511
  }), selectedSqlRow && /* @__PURE__ */ require_jsx_runtime.jsx(LogDetails, {
1276
1512
  record: selectedSqlRow.record,
1277
1513
  jsonColumn: selectedSqlRow.jsonColumn,
1514
+ onFilter: !sqlFilteringDisabled && selectedSqlRow.jsonColumn ? (filter) => addSqlFilter(selectedSqlRow.jsonColumn, filter) : void 0,
1278
1515
  onClose: () => setSelectedSqlRow(void 0)
1279
1516
  })]
1280
1517
  }),