@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
@@ -3,38 +3,163 @@ const require__global_external_mobx_react = require("../_virtual/_global-externa
3
3
  const require_power_logs = require("../src/common/power-logs.js");
4
4
  const require_power_logs_limits = require("../src/common/power-logs-limits.js");
5
5
  const require_logs_preferences_store = require("../src/common/store/logs-preferences-store.js");
6
- const require__global_external_react = require("../_virtual/_global-external_react.js");
7
6
  const require_jsx_runtime = require("../_virtual/_global-external_react/jsx-runtime.js");
8
7
  //#region src/renderer/preferences/logs-preferences.tsx
9
- var LogLimitInput = require__global_external_mobx_react.observer(function LogLimitInput({ field, preferences }) {
10
- const scale = field.scale ?? 1;
11
- const value = preferences.limits[field.key];
12
- const [draft, setDraft] = require__global_external_react.useState(String(value / scale));
13
- require__global_external_react.useEffect(() => setDraft(String(value / scale)), [value, scale]);
14
- const commit = () => {
15
- const next = draft.trim() === "" ? value : Number(draft) * scale;
16
- preferences.limits = require_power_logs_limits.normalizePowerLogsLimits({
17
- ...preferences.limits,
18
- [field.key]: Number.isFinite(next) ? next : value
19
- });
20
- require_power_logs_limits.applyPowerLogsLimits(preferences.limits);
21
- setDraft(String(preferences.limits[field.key] / scale));
22
- };
23
- return /* @__PURE__ */ require_jsx_runtime.jsxs("label", {
24
- className: "power-log-limit",
25
- children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", { children: field.label }), /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
26
- className: "power-log-limit-value",
27
- children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Input, {
28
- "aria-label": `${field.group}: ${field.label}`,
29
- type: "number",
30
- value: draft,
31
- min: field.min / scale,
32
- max: field.max / scale,
33
- step: "any",
34
- onChange: setDraft,
35
- onBlur: commit,
36
- onSubmit: commit
37
- }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: field.unit })]
8
+ var MiB = 1024 * 1024;
9
+ var settings = [
10
+ {
11
+ key: "liveRows",
12
+ label: "Live buffer",
13
+ hint: "Maximum rows and memory kept in the live log view.",
14
+ presets: [
15
+ 1e3,
16
+ 5e3,
17
+ 1e4,
18
+ 5e4,
19
+ 1e5
20
+ ].map((liveRows, index) => ({
21
+ label: `${liveRows.toLocaleString("en-US")} rows / ${[
22
+ 2,
23
+ 8,
24
+ 16,
25
+ 80,
26
+ 160
27
+ ][index]} MiB`,
28
+ limits: {
29
+ liveRows,
30
+ liveBytes: [
31
+ 2,
32
+ 8,
33
+ 16,
34
+ 80,
35
+ 160
36
+ ][index] * MiB
37
+ }
38
+ })),
39
+ describe: (limits) => `${limits.liveRows.toLocaleString("en-US")} rows / ${limits.liveBytes / MiB} MiB`
40
+ },
41
+ {
42
+ key: "historyLines",
43
+ label: "History per container",
44
+ hint: "Maximum recent lines and data fetched for each container.",
45
+ presets: [
46
+ 500,
47
+ 1e3,
48
+ 5e3
49
+ ].map((historyLines, index) => ({
50
+ label: `${historyLines.toLocaleString("en-US")} lines / ${[
51
+ .5,
52
+ 1,
53
+ 5
54
+ ][index]} MiB`,
55
+ limits: {
56
+ historyLines,
57
+ historyBytes: [
58
+ .5,
59
+ 1,
60
+ 5
61
+ ][index] * MiB
62
+ }
63
+ })),
64
+ describe: (limits) => `${limits.historyLines.toLocaleString("en-US")} lines / ${limits.historyBytes / MiB} MiB`
65
+ },
66
+ {
67
+ key: "captureBytes",
68
+ label: "Capture size",
69
+ hint: "Maximum data stored in each temporary capture.",
70
+ presets: [
71
+ 64,
72
+ 128,
73
+ 256,
74
+ 512
75
+ ].map((size) => ({
76
+ label: `${size} MiB`,
77
+ limits: { captureBytes: size * MiB }
78
+ })),
79
+ describe: (limits) => `${limits.captureBytes / MiB} MiB`
80
+ },
81
+ {
82
+ key: "captures",
83
+ label: "Saved captures",
84
+ hint: "Maximum number of temporary captures retained.",
85
+ presets: [
86
+ 1,
87
+ 3,
88
+ 5,
89
+ 10
90
+ ].map((captures) => ({
91
+ label: `${captures} ${captures === 1 ? "capture" : "captures"}`,
92
+ limits: { captures }
93
+ })),
94
+ describe: (limits) => `${limits.captures} captures`
95
+ },
96
+ {
97
+ key: "ttlMs",
98
+ label: "Retention",
99
+ hint: "How long temporary captures remain available.",
100
+ presets: [
101
+ 30,
102
+ 60,
103
+ 360,
104
+ 1440,
105
+ 4320
106
+ ].map((minutes) => ({
107
+ label: minutes >= 1440 ? `${minutes / 1440} ${minutes === 1440 ? "day" : "days"}` : minutes < 60 ? `${minutes} minutes` : `${minutes / 60} ${minutes === 60 ? "hour" : "hours"}`,
108
+ limits: { ttlMs: minutes * 6e4 }
109
+ })),
110
+ describe: (limits) => `${limits.ttlMs / 6e4} minutes`
111
+ }
112
+ ];
113
+ var LogLimitSelect = require__global_external_mobx_react.observer(function LogLimitSelect({ setting, preferences }) {
114
+ const selected = setting.presets.findIndex((preset) => Object.entries(preset.limits).every(([key, value]) => preferences.limits[key] === value));
115
+ const options = setting.presets.map((preset, index) => ({
116
+ value: String(index),
117
+ label: preset.label
118
+ }));
119
+ if (selected === -1) options.push({
120
+ value: "custom",
121
+ label: `Custom: ${setting.describe(preferences.limits)}`
122
+ });
123
+ const id = `freelens-power-logs-${setting.key}`;
124
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
125
+ className: "power-settings-row",
126
+ style: {
127
+ display: "flex",
128
+ alignItems: "center",
129
+ justifyContent: "space-between",
130
+ gap: 20
131
+ },
132
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("label", {
133
+ id: `${id}-label`,
134
+ htmlFor: id,
135
+ children: setting.label
136
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("div", {
137
+ className: "power-settings-hint",
138
+ children: setting.hint
139
+ })] }), /* @__PURE__ */ require_jsx_runtime.jsx("div", {
140
+ style: {
141
+ width: 260,
142
+ maxWidth: "100%",
143
+ flexShrink: 0
144
+ },
145
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Select, {
146
+ id,
147
+ "aria-labelledby": `${id}-label`,
148
+ options,
149
+ value: selected === -1 ? "custom" : String(selected),
150
+ isSearchable: false,
151
+ isClearable: false,
152
+ onChange: (option) => {
153
+ if (!option || option.value === "custom") return;
154
+ const preset = setting.presets[Number(option.value)];
155
+ if (!preset) return;
156
+ preferences.limits = {
157
+ ...preferences.limits,
158
+ ...preset.limits
159
+ };
160
+ require_power_logs_limits.applyPowerLogsLimits(preferences.limits);
161
+ }
162
+ })
38
163
  })]
39
164
  });
40
165
  });
@@ -56,18 +181,14 @@ var LogsPreferences = require__global_external_mobx_react.observer(() => {
56
181
  className: "power-settings-hint",
57
182
  children: "Colored workload logs and temporary SQLite captures for SQL analysis. Native logs return when disabled. Temporary captures follow the retention settings below and are deleted when Power Logs stops."
58
183
  })] }),
59
- /* @__PURE__ */ require_jsx_runtime.jsx("style", { children: `.power-log-limit-groups{display:grid;gap:20px}.power-log-limit-groups fieldset{min-width:0;border:1px solid var(--borderFaintColor);border-radius:6px;padding:16px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px 20px}.power-log-limit-groups legend{padding:0 6px;color:var(--textColorAccent)}.power-log-limit{display:flex;flex-direction:column;gap:6px;min-width:0}.power-log-limit-value{display:flex;align-items:center;gap:8px;color:var(--textColorSecondary);font-size:12px}.power-log-limit-value .Input{min-width:0;flex:1}.power-log-limit-value input{width:100%;min-width:0}@container power-preferences(max-width:480px){.power-log-limit-groups fieldset{grid-template-columns:1fr}}` }),
60
184
  /* @__PURE__ */ require_jsx_runtime.jsx("div", {
61
185
  className: "power-settings-hint",
62
186
  children: "Changes apply to new requests, captures and SQL queries. Existing captures retain their capacity and expiry."
63
187
  }),
64
- /* @__PURE__ */ require_jsx_runtime.jsx("div", {
65
- className: "power-log-limit-groups",
66
- children: [...new Set(require_power_logs_limits.LOG_LIMIT_FIELDS.map((field) => field.group))].map((group) => /* @__PURE__ */ require_jsx_runtime.jsxs("fieldset", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("legend", { children: group }), require_power_logs_limits.LOG_LIMIT_FIELDS.filter((field) => field.group === group).map((field) => /* @__PURE__ */ require_jsx_runtime.jsx(LogLimitInput, {
67
- field,
68
- preferences
69
- }, field.key))] }, group))
70
- }),
188
+ settings.map((setting) => /* @__PURE__ */ require_jsx_runtime.jsx(LogLimitSelect, {
189
+ setting,
190
+ preferences
191
+ }, setting.key)),
71
192
  /* @__PURE__ */ require_jsx_runtime.jsx("div", {
72
193
  style: {
73
194
  display: "flex",
@@ -1 +1 @@
1
- {"version":3,"file":"logs-preferences.js","names":[],"sources":["../../../src/renderer/preferences/logs-preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useState } from \"react\";\nimport { POWER_LOGS_DEFAULT_LIMITS } from \"../../common/power-logs\";\nimport {\n applyPowerLogsLimits,\n LOG_LIMIT_FIELDS,\n type LogLimitField,\n normalizePowerLogsLimits,\n} from \"../../common/power-logs-limits\";\nimport { LogsPreferencesStore } from \"../../common/store/logs-preferences-store\";\n\nconst LogLimitInput = observer(function LogLimitInput({\n field,\n preferences,\n}: {\n field: LogLimitField;\n preferences: LogsPreferencesStore;\n}) {\n const scale = field.scale ?? 1;\n const value = preferences.limits[field.key];\n const [draft, setDraft] = useState(String(value / scale));\n useEffect(() => setDraft(String(value / scale)), [value, scale]);\n const commit = () => {\n const next = draft.trim() === \"\" ? value : Number(draft) * scale;\n preferences.limits = normalizePowerLogsLimits({\n ...preferences.limits,\n [field.key]: Number.isFinite(next) ? next : value,\n });\n applyPowerLogsLimits(preferences.limits);\n setDraft(String(preferences.limits[field.key] / scale));\n };\n return (\n <label className=\"power-log-limit\">\n <span>{field.label}</span>\n <span className=\"power-log-limit-value\">\n <Renderer.Component.Input\n aria-label={`${field.group}: ${field.label}`}\n type=\"number\"\n value={draft}\n min={field.min / scale}\n max={field.max / scale}\n step=\"any\"\n onChange={setDraft}\n onBlur={commit}\n onSubmit={commit}\n />\n <span>{field.unit}</span>\n </span>\n </label>\n );\n});\n\nexport const LogsPreferences = observer(() => {\n const preferences = LogsPreferencesStore.getInstanceOrCreate<LogsPreferencesStore>();\n return (\n <div style={{ display: \"grid\", gap: 20 }}>\n <div>\n <Renderer.Component.Switch\n checked={preferences.enabled}\n onChange={(value) => {\n preferences.enabled = value;\n }}\n >\n Use Power Logs\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">\n Colored workload logs and temporary SQLite captures for SQL analysis. Native logs return when disabled.\n Temporary captures follow the retention settings below and are deleted when Power Logs stops.\n </div>\n </div>\n <style>{`.power-log-limit-groups{display:grid;gap:20px}.power-log-limit-groups fieldset{min-width:0;border:1px solid var(--borderFaintColor);border-radius:6px;padding:16px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px 20px}.power-log-limit-groups legend{padding:0 6px;color:var(--textColorAccent)}.power-log-limit{display:flex;flex-direction:column;gap:6px;min-width:0}.power-log-limit-value{display:flex;align-items:center;gap:8px;color:var(--textColorSecondary);font-size:12px}.power-log-limit-value .Input{min-width:0;flex:1}.power-log-limit-value input{width:100%;min-width:0}@container power-preferences(max-width:480px){.power-log-limit-groups fieldset{grid-template-columns:1fr}}`}</style>\n <div className=\"power-settings-hint\">\n Changes apply to new requests, captures and SQL queries. Existing captures retain their capacity and expiry.\n </div>\n <div className=\"power-log-limit-groups\">\n {[...new Set(LOG_LIMIT_FIELDS.map((field) => field.group))].map((group) => (\n <fieldset key={group}>\n <legend>{group}</legend>\n {LOG_LIMIT_FIELDS.filter((field) => field.group === group).map((field) => (\n <LogLimitInput key={field.key} field={field} preferences={preferences} />\n ))}\n </fieldset>\n ))}\n </div>\n <div style={{ display: \"flex\", justifyContent: \"flex-end\" }}>\n <Renderer.Component.Button\n onClick={() => {\n preferences.limits = { ...POWER_LOGS_DEFAULT_LIMITS };\n applyPowerLogsLimits(preferences.limits);\n }}\n >\n <Renderer.Component.Icon material=\"restore\" size={16} /> Reset log limits\n </Renderer.Component.Button>\n </div>\n </div>\n );\n});\n"],"mappings":";;;;;;;;AAYA,IAAM,gBAAgB,oCAAA,SAAS,SAAS,cAAc,EACpD,OACA,eAIC;CACD,MAAM,QAAQ,MAAM,SAAS;CAC7B,MAAM,QAAQ,YAAY,OAAO,MAAM;CACvC,MAAM,CAAC,OAAO,YAAY,+BAAA,SAAS,OAAO,QAAQ,KAAK,CAAC;CACxD,+BAAA,gBAAgB,SAAS,OAAO,QAAQ,KAAK,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC;CAC/D,MAAM,eAAe;EACnB,MAAM,OAAO,MAAM,KAAK,MAAM,KAAK,QAAQ,OAAO,KAAK,IAAI;EAC3D,YAAY,SAAS,0BAAA,yBAAyB;GAC5C,GAAG,YAAY;IACd,MAAM,MAAM,OAAO,SAAS,IAAI,IAAI,OAAO;EAC9C,CAAC;EACD,0BAAA,qBAAqB,YAAY,MAAM;EACvC,SAAS,OAAO,YAAY,OAAO,MAAM,OAAO,KAAK,CAAC;CACxD;CACA,OACE,oCAAA,KAAC,SAAD;EAAO,WAAU;YAAjB,CACE,oCAAA,IAAC,QAAD,EAAA,UAAO,MAAM,MAAY,CAAA,GACzB,oCAAA,KAAC,QAAD;GAAM,WAAU;aAAhB,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,OAApB;IACE,cAAY,GAAG,MAAM,MAAM,IAAI,MAAM;IACrC,MAAK;IACL,OAAO;IACP,KAAK,MAAM,MAAM;IACjB,KAAK,MAAM,MAAM;IACjB,MAAK;IACL,UAAU;IACV,QAAQ;IACR,UAAU;GACX,CAAA,GACD,oCAAA,IAAC,QAAD,EAAA,UAAO,MAAM,KAAW,CAAA,CACpB;IACD;;AAEX,CAAC;AAED,IAAa,kBAAkB,oCAAA,eAAe;CAC5C,MAAM,cAAc,+BAAA,qBAAqB,oBAA0C;CACnF,OACE,oCAAA,KAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,KAAK;EAAG;YAAvC;GACE,oCAAA,KAAC,OAAD,EAAA,UAAA,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,QAApB;IACE,SAAS,YAAY;IACrB,WAAW,UAAU;KACnB,YAAY,UAAU;IACxB;cACD;GAE0B,CAAA,GAC3B,oCAAA,IAAC,OAAD;IAAK,WAAU;cAAsB;GAGhC,CAAA,CACF,EAAA,CAAA;GACL,oCAAA,IAAC,SAAD,EAAA,UAAQ,8rBAAqsB,CAAA;GAC7sB,oCAAA,IAAC,OAAD;IAAK,WAAU;cAAsB;GAEhC,CAAA;GACL,oCAAA,IAAC,OAAD;IAAK,WAAU;cACZ,CAAC,GAAG,IAAI,IAAI,0BAAA,iBAAiB,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,UAC/D,oCAAA,KAAC,YAAD,EAAA,UAAA,CACE,oCAAA,IAAC,UAAD,EAAA,UAAS,MAAc,CAAA,GACtB,0BAAA,iBAAiB,QAAQ,UAAU,MAAM,UAAU,KAAK,CAAC,CAAC,KAAK,UAC9D,oCAAA,IAAC,eAAD;KAAsC;KAAoB;IAAc,GAApD,MAAM,GAA8C,CACzE,CACO,EAAA,GALK,KAKL,CACX;GACE,CAAA;GACL,oCAAA,IAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,gBAAgB;IAAW;cACxD,oCAAA,KAAC,mBAAA,SAAS,UAAU,QAApB;KACE,eAAe;MACb,YAAY,SAAS,EAAE,GAAG,mBAAA,0BAA0B;MACpD,0BAAA,qBAAqB,YAAY,MAAM;KACzC;eAJF,CAME,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;MAAyB,UAAS;MAAU,MAAM;KAAK,CAAA,GAAC,mBAC/B;;GACxB,CAAA;EACF;;AAET,CAAC"}
1
+ {"version":3,"file":"logs-preferences.js","names":[],"sources":["../../../src/renderer/preferences/logs-preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { observer } from \"mobx-react\";\nimport { POWER_LOGS_DEFAULT_LIMITS, type PowerLogsLimits } from \"../../common/power-logs\";\nimport { applyPowerLogsLimits } from \"../../common/power-logs-limits\";\nimport { LogsPreferencesStore } from \"../../common/store/logs-preferences-store\";\n\nconst MiB = 1024 * 1024;\ninterface LimitPreset {\n label: string;\n limits: Partial<PowerLogsLimits>;\n}\ninterface LimitSetting {\n key: keyof PowerLogsLimits;\n label: string;\n hint: string;\n presets: LimitPreset[];\n describe(limits: PowerLogsLimits): string;\n}\nconst settings: LimitSetting[] = [\n {\n key: \"liveRows\",\n label: \"Live buffer\",\n hint: \"Maximum rows and memory kept in the live log view.\",\n presets: [1000, 5000, 10000, 50000, 100000].map((liveRows, index) => ({\n label: `${liveRows.toLocaleString(\"en-US\")} rows / ${[2, 8, 16, 80, 160][index]} MiB`,\n limits: { liveRows, liveBytes: [2, 8, 16, 80, 160][index] * MiB },\n })),\n describe: (limits) => `${limits.liveRows.toLocaleString(\"en-US\")} rows / ${limits.liveBytes / MiB} MiB`,\n },\n {\n key: \"historyLines\",\n label: \"History per container\",\n hint: \"Maximum recent lines and data fetched for each container.\",\n presets: [500, 1000, 5000].map((historyLines, index) => ({\n label: `${historyLines.toLocaleString(\"en-US\")} lines / ${[0.5, 1, 5][index]} MiB`,\n limits: { historyLines, historyBytes: [0.5, 1, 5][index] * MiB },\n })),\n describe: (limits) => `${limits.historyLines.toLocaleString(\"en-US\")} lines / ${limits.historyBytes / MiB} MiB`,\n },\n {\n key: \"captureBytes\",\n label: \"Capture size\",\n hint: \"Maximum data stored in each temporary capture.\",\n presets: [64, 128, 256, 512].map((size) => ({\n label: `${size} MiB`,\n limits: { captureBytes: size * MiB },\n })),\n describe: (limits) => `${limits.captureBytes / MiB} MiB`,\n },\n {\n key: \"captures\",\n label: \"Saved captures\",\n hint: \"Maximum number of temporary captures retained.\",\n presets: [1, 3, 5, 10].map((captures) => ({\n label: `${captures} ${captures === 1 ? \"capture\" : \"captures\"}`,\n limits: { captures },\n })),\n describe: (limits) => `${limits.captures} captures`,\n },\n {\n key: \"ttlMs\",\n label: \"Retention\",\n hint: \"How long temporary captures remain available.\",\n presets: [30, 60, 360, 1440, 4320].map((minutes) => ({\n label:\n minutes >= 1440\n ? `${minutes / 1440} ${minutes === 1440 ? \"day\" : \"days\"}`\n : minutes < 60\n ? `${minutes} minutes`\n : `${minutes / 60} ${minutes === 60 ? \"hour\" : \"hours\"}`,\n limits: { ttlMs: minutes * 60000 },\n })),\n describe: (limits) => `${limits.ttlMs / 60000} minutes`,\n },\n];\n\nconst LogLimitSelect = observer(function LogLimitSelect({\n setting,\n preferences,\n}: {\n setting: LimitSetting;\n preferences: LogsPreferencesStore;\n}) {\n const selected = setting.presets.findIndex((preset) =>\n Object.entries(preset.limits).every(([key, value]) => preferences.limits[key as keyof PowerLogsLimits] === value),\n );\n const options = setting.presets.map((preset, index) => ({ value: String(index), label: preset.label }));\n if (selected === -1) options.push({ value: \"custom\", label: `Custom: ${setting.describe(preferences.limits)}` });\n const id = `freelens-power-logs-${setting.key}`;\n return (\n <div\n className=\"power-settings-row\"\n style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", gap: 20 }}\n >\n <div>\n <label id={`${id}-label`} htmlFor={id}>\n {setting.label}\n </label>\n <div className=\"power-settings-hint\">{setting.hint}</div>\n </div>\n <div style={{ width: 260, maxWidth: \"100%\", flexShrink: 0 }}>\n <Renderer.Component.Select\n id={id}\n aria-labelledby={`${id}-label`}\n options={options}\n value={selected === -1 ? \"custom\" : String(selected)}\n isSearchable={false}\n isClearable={false}\n onChange={(option) => {\n if (!option || option.value === \"custom\") return;\n const preset = setting.presets[Number(option.value)];\n if (!preset) return;\n preferences.limits = { ...preferences.limits, ...preset.limits };\n applyPowerLogsLimits(preferences.limits);\n }}\n />\n </div>\n </div>\n );\n});\n\nexport const LogsPreferences = observer(() => {\n const preferences = LogsPreferencesStore.getInstanceOrCreate<LogsPreferencesStore>();\n return (\n <div style={{ display: \"grid\", gap: 20 }}>\n <div>\n <Renderer.Component.Switch\n checked={preferences.enabled}\n onChange={(value) => {\n preferences.enabled = value;\n }}\n >\n Use Power Logs\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">\n Colored workload logs and temporary SQLite captures for SQL analysis. Native logs return when disabled.\n Temporary captures follow the retention settings below and are deleted when Power Logs stops.\n </div>\n </div>\n <div className=\"power-settings-hint\">\n Changes apply to new requests, captures and SQL queries. Existing captures retain their capacity and expiry.\n </div>\n {settings.map((setting) => (\n <LogLimitSelect key={setting.key} setting={setting} preferences={preferences} />\n ))}\n <div style={{ display: \"flex\", justifyContent: \"flex-end\" }}>\n <Renderer.Component.Button\n onClick={() => {\n preferences.limits = { ...POWER_LOGS_DEFAULT_LIMITS };\n applyPowerLogsLimits(preferences.limits);\n }}\n >\n <Renderer.Component.Icon material=\"restore\" size={16} /> Reset log limits\n </Renderer.Component.Button>\n </div>\n </div>\n );\n});\n"],"mappings":"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"}
@@ -9,6 +9,7 @@ const require_monitoring_preferences = require("./monitoring-preferences.js");
9
9
  const require_node_shell_preferences = require("./node-shell-preferences.js");
10
10
  const require_power_preferences = require("./power-preferences.js");
11
11
  const require_read_only_preferences = require("./read-only-preferences.js");
12
+ const require_section_navigation = require("./section-navigation.js");
12
13
  const require_settings_export = require("./settings-export.js");
13
14
  //#region src/renderer/preferences/preferences.tsx
14
15
  var styles = `
@@ -128,15 +129,7 @@ var PowerPreferences = () => /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
128
129
  title: `Jump to ${title} settings`,
129
130
  onClick: (event) => {
130
131
  event.preventDefault();
131
- const section = document.getElementById(`freelens-power-${id}-section`);
132
- requestAnimationFrame(() => {
133
- if (!section?.isConnected) return;
134
- section.focus({ preventScroll: true });
135
- section.scrollIntoView({
136
- block: "start",
137
- behavior: "instant"
138
- });
139
- });
132
+ require_section_navigation.focusPreferencesSection(id);
140
133
  },
141
134
  children: [
142
135
  /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Icon, {
@@ -1 +1 @@
1
- {"version":3,"file":"preferences.js","names":[],"sources":["../../../src/renderer/preferences/preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { EXTENSION_ID } from \"../../common/extension-info\";\nimport { AiPreferences } from \"./ai-preferences\";\nimport { AppearancePreferences } from \"./appearance-preferences\";\nimport { DashboardPreferences } from \"./dashboard-preferences\";\nimport { LogsPreferences } from \"./logs-preferences\";\nimport { MonitoringPreferences } from \"./monitoring-preferences\";\nimport { NodeShellPreferences } from \"./node-shell-preferences\";\nimport { PowerPreferencesToggle } from \"./power-preferences\";\nimport { ReadOnlyPreferences } from \"./read-only-preferences\";\nimport { SettingsExport } from \"./settings-export\";\n\nconst styles = `\n .power-preferences-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; }\n .power-preferences-header-actions { display: inline-flex; margin-left: auto; flex-shrink: 0; }\n #freelens-power-settings > .SubTitle,\n #freelens-power-settings > .hint,\n #freelens-power-settings + hr { display: none; }\n .freelens-power-preferences { display: grid; gap: 24px; max-width: 760px; container: power-preferences / inline-size; }\n .freelens-power-preferences .power-preferences-section-links {\n display:flex; align-items:center; width:100%; gap:8px; overflow-x:auto;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link {\n display: inline-flex; align-items: center; justify-content: center; gap: 6px;\n flex:1 0 auto; width:auto; min-width:max-content; min-height:32px; padding:4px 2px;\n border: 0; border-radius: 4px; background: transparent; box-shadow: none;\n color: var(--textColorSecondary); text-transform: none; white-space: nowrap; cursor: pointer;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link:hover {\n color: var(--primary); background: transparent; box-shadow: none;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link::before {\n content: none;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link:focus-visible {\n color: var(--primary); background: transparent; box-shadow: none;\n outline: 2px solid var(--primary); outline-offset: 2px;\n }\n @container power-preferences (max-width:660px) { .power-preferences-section-links { font-size:12px; } }\n .freelens-power-preferences section[id] { scroll-margin-top: 16px; }\n .freelens-power-preferences section {\n padding: 24px; border: 1px solid var(--borderFaintColor);\n border-radius: 12px; background: var(--contentColor);\n }\n .freelens-power-preferences button, .freelens-power-preferences .Button { transition: color 160ms ease, background-color 160ms ease, border-color 160ms ease; }\n :root .freelens-power-preferences button.power-settings-icon-action,\n :root .power-preferences-header-actions button.power-settings-icon-action {\n display: inline-flex; align-items: center; justify-content: center;\n width: 32px; height: 32px; min-width: 32px; padding: 0; flex-shrink: 0;\n border: 0; border-radius: 6px; background: transparent; color: var(--textColorSecondary); cursor: pointer;\n transition: color 160ms ease, background-color 160ms ease;\n }\n :root .freelens-power-preferences button.power-settings-icon-action:not(:disabled):hover,\n :root .power-preferences-header-actions button.power-settings-icon-action:not(:disabled):hover {\n color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent);\n }\n :root .freelens-power-preferences button.power-settings-icon-action:focus-visible,\n :root .power-preferences-header-actions button.power-settings-icon-action:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }\n :root .freelens-power-preferences button.power-settings-icon-action:disabled,\n :root .power-preferences-header-actions button.power-settings-icon-action:disabled { opacity: 0.4; cursor: default; }\n .freelens-power-preferences h3 {\n display: flex; align-items: center; gap: 10px; margin: 0 0 24px; font-size: 16px; font-weight: 600; color: var(--textColorAccent);\n }\n .freelens-power-preferences label[data-testid=\"switch\"] {\n display: flex; width: 100%; align-items: center; justify-content: space-between;\n }\n .freelens-power-preferences .power-settings-hint {\n margin-top: 6px; font-size: 12px; line-height: 1.5; color: var(--textColorSecondary);\n }\n @media (max-width: 760px) {\n .freelens-power-preferences section { padding: 18px; }\n .freelens-power-preferences .power-settings-row { flex-wrap: wrap; }\n }\n`;\n\nconst sectionLinks = [\n { id: \"safety\", title: \"Safety\", icon: \"lock_outline\" },\n { id: \"appearance\", title: \"Appearance\", icon: \"palette\" },\n { id: \"monitoring\", title: \"Monitoring\", icon: \"show_chart\" },\n { id: \"logs\", title: \"Logs\", icon: \"subject\" },\n { id: \"shell\", title: \"Shell\", icon: \"vpn_key\" },\n { id: \"ai\", title: \"AI\", icon: \"chat_bubble_outline\" },\n { id: \"dashboard\", title: \"Dashboard\", icon: \"dashboard\" },\n];\n\nexport const PowerPreferences = () => (\n <div className=\"freelens-power-preferences\">\n <style>{styles}</style>\n <SettingsExport />\n <section aria-label=\"Freelens Power\">\n <PowerPreferencesToggle />\n </section>\n <nav className=\"power-preferences-section-links\" aria-label=\"Settings sections\">\n {sectionLinks.map(({ id, title, icon }) => (\n <Renderer.Component.Button\n key={id}\n className=\"power-settings-section-link\"\n aria-label={title}\n title={`Jump to ${title} settings`}\n onClick={(event) => {\n event.preventDefault();\n const section = document.getElementById(`freelens-power-${id}-section`);\n requestAnimationFrame(() => {\n if (!section?.isConnected) return;\n section.focus({ preventScroll: true });\n section.scrollIntoView({ block: \"start\", behavior: \"instant\" });\n });\n }}\n >\n <Renderer.Component.Icon material={icon} size={16} /> {title}\n </Renderer.Component.Button>\n ))}\n </nav>\n <section id=\"freelens-power-safety-section\" tabIndex={-1} aria-labelledby=\"freelens-power-safety-title\">\n <h3 id=\"freelens-power-safety-title\">\n <Renderer.Component.Icon material=\"lock_outline\" size={20} /> Read-only mode\n </h3>\n <ReadOnlyPreferences />\n </section>\n <section id=\"freelens-power-appearance-section\" tabIndex={-1} aria-labelledby=\"freelens-power-appearance-title\">\n <h3 id=\"freelens-power-appearance-title\">\n <Renderer.Component.Icon material=\"palette\" size={20} /> Appearance\n </h3>\n <AppearancePreferences />\n <div style={{ display: \"flex\", justifyContent: \"flex-end\", marginTop: 20 }}>\n <Renderer.Component.Button\n style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6 }}\n onClick={() => Renderer.Navigation.navigate(`/extension/${EXTENSION_ID}/cluster-icons`)}\n title=\"Create matching icons for your saved clusters\"\n >\n <Renderer.Component.Icon material=\"palette\" size={16} /> Cluster icons\n </Renderer.Component.Button>\n </div>\n </section>\n <section id=\"freelens-power-monitoring-section\" tabIndex={-1} aria-labelledby=\"freelens-power-monitoring-title\">\n <h3 id=\"freelens-power-monitoring-title\">\n <Renderer.Component.Icon material=\"show_chart\" size={20} /> Monitoring\n </h3>\n <MonitoringPreferences />\n </section>\n <section id=\"freelens-power-logs-section\" tabIndex={-1} aria-labelledby=\"freelens-power-logs-title\">\n <h3 id=\"freelens-power-logs-title\">\n <Renderer.Component.Icon material=\"subject\" size={20} /> Power Logs\n </h3>\n <LogsPreferences />\n </section>\n <section id=\"freelens-power-shell-section\" tabIndex={-1} aria-labelledby=\"freelens-power-shell-title\">\n <h3 id=\"freelens-power-shell-title\">\n <Renderer.Component.Icon material=\"vpn_key\" size={20} /> Secure Node Shell\n </h3>\n <NodeShellPreferences />\n </section>\n <section id=\"freelens-power-ai-section\" tabIndex={-1} aria-labelledby=\"freelens-power-ai-title\">\n <h3 id=\"freelens-power-ai-title\">\n <Renderer.Component.Icon material=\"chat_bubble_outline\" size={20} /> AI assistant\n </h3>\n <AiPreferences />\n </section>\n <section id=\"freelens-power-dashboard-section\" tabIndex={-1} aria-labelledby=\"freelens-power-dashboard-title\">\n <h3 id=\"freelens-power-dashboard-title\">\n <Renderer.Component.Icon material=\"dashboard\" size={20} /> Dashboard\n </h3>\n <DashboardPreferences />\n </section>\n </div>\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"preferences.js","names":[],"sources":["../../../src/renderer/preferences/preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { EXTENSION_ID } from \"../../common/extension-info\";\nimport { AiPreferences } from \"./ai-preferences\";\nimport { AppearancePreferences } from \"./appearance-preferences\";\nimport { DashboardPreferences } from \"./dashboard-preferences\";\nimport { LogsPreferences } from \"./logs-preferences\";\nimport { MonitoringPreferences } from \"./monitoring-preferences\";\nimport { NodeShellPreferences } from \"./node-shell-preferences\";\nimport { PowerPreferencesToggle } from \"./power-preferences\";\nimport { ReadOnlyPreferences } from \"./read-only-preferences\";\nimport { focusPreferencesSection } from \"./section-navigation\";\nimport { SettingsExport } from \"./settings-export\";\n\nconst styles = `\n .power-preferences-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; }\n .power-preferences-header-actions { display: inline-flex; margin-left: auto; flex-shrink: 0; }\n #freelens-power-settings > .SubTitle,\n #freelens-power-settings > .hint,\n #freelens-power-settings + hr { display: none; }\n .freelens-power-preferences { display: grid; gap: 24px; max-width: 760px; container: power-preferences / inline-size; }\n .freelens-power-preferences .power-preferences-section-links {\n display:flex; align-items:center; width:100%; gap:8px; overflow-x:auto;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link {\n display: inline-flex; align-items: center; justify-content: center; gap: 6px;\n flex:1 0 auto; width:auto; min-width:max-content; min-height:32px; padding:4px 2px;\n border: 0; border-radius: 4px; background: transparent; box-shadow: none;\n color: var(--textColorSecondary); text-transform: none; white-space: nowrap; cursor: pointer;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link:hover {\n color: var(--primary); background: transparent; box-shadow: none;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link::before {\n content: none;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link:focus-visible {\n color: var(--primary); background: transparent; box-shadow: none;\n outline: 2px solid var(--primary); outline-offset: 2px;\n }\n @container power-preferences (max-width:660px) { .power-preferences-section-links { font-size:12px; } }\n .freelens-power-preferences section[id] { scroll-margin-top: 16px; }\n .freelens-power-preferences section {\n padding: 24px; border: 1px solid var(--borderFaintColor);\n border-radius: 12px; background: var(--contentColor);\n }\n .freelens-power-preferences button, .freelens-power-preferences .Button { transition: color 160ms ease, background-color 160ms ease, border-color 160ms ease; }\n :root .freelens-power-preferences button.power-settings-icon-action,\n :root .power-preferences-header-actions button.power-settings-icon-action {\n display: inline-flex; align-items: center; justify-content: center;\n width: 32px; height: 32px; min-width: 32px; padding: 0; flex-shrink: 0;\n border: 0; border-radius: 6px; background: transparent; color: var(--textColorSecondary); cursor: pointer;\n transition: color 160ms ease, background-color 160ms ease;\n }\n :root .freelens-power-preferences button.power-settings-icon-action:not(:disabled):hover,\n :root .power-preferences-header-actions button.power-settings-icon-action:not(:disabled):hover {\n color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent);\n }\n :root .freelens-power-preferences button.power-settings-icon-action:focus-visible,\n :root .power-preferences-header-actions button.power-settings-icon-action:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }\n :root .freelens-power-preferences button.power-settings-icon-action:disabled,\n :root .power-preferences-header-actions button.power-settings-icon-action:disabled { opacity: 0.4; cursor: default; }\n .freelens-power-preferences h3 {\n display: flex; align-items: center; gap: 10px; margin: 0 0 24px; font-size: 16px; font-weight: 600; color: var(--textColorAccent);\n }\n .freelens-power-preferences label[data-testid=\"switch\"] {\n display: flex; width: 100%; align-items: center; justify-content: space-between;\n }\n .freelens-power-preferences .power-settings-hint {\n margin-top: 6px; font-size: 12px; line-height: 1.5; color: var(--textColorSecondary);\n }\n @media (max-width: 760px) {\n .freelens-power-preferences section { padding: 18px; }\n .freelens-power-preferences .power-settings-row { flex-wrap: wrap; }\n }\n`;\n\nconst sectionLinks = [\n { id: \"safety\", title: \"Safety\", icon: \"lock_outline\" },\n { id: \"appearance\", title: \"Appearance\", icon: \"palette\" },\n { id: \"monitoring\", title: \"Monitoring\", icon: \"show_chart\" },\n { id: \"logs\", title: \"Logs\", icon: \"subject\" },\n { id: \"shell\", title: \"Shell\", icon: \"vpn_key\" },\n { id: \"ai\", title: \"AI\", icon: \"chat_bubble_outline\" },\n { id: \"dashboard\", title: \"Dashboard\", icon: \"dashboard\" },\n];\n\nexport const PowerPreferences = () => (\n <div className=\"freelens-power-preferences\">\n <style>{styles}</style>\n <SettingsExport />\n <section aria-label=\"Freelens Power\">\n <PowerPreferencesToggle />\n </section>\n <nav className=\"power-preferences-section-links\" aria-label=\"Settings sections\">\n {sectionLinks.map(({ id, title, icon }) => (\n <Renderer.Component.Button\n key={id}\n className=\"power-settings-section-link\"\n aria-label={title}\n title={`Jump to ${title} settings`}\n onClick={(event) => {\n event.preventDefault();\n focusPreferencesSection(id);\n }}\n >\n <Renderer.Component.Icon material={icon} size={16} /> {title}\n </Renderer.Component.Button>\n ))}\n </nav>\n <section id=\"freelens-power-safety-section\" tabIndex={-1} aria-labelledby=\"freelens-power-safety-title\">\n <h3 id=\"freelens-power-safety-title\">\n <Renderer.Component.Icon material=\"lock_outline\" size={20} /> Read-only mode\n </h3>\n <ReadOnlyPreferences />\n </section>\n <section id=\"freelens-power-appearance-section\" tabIndex={-1} aria-labelledby=\"freelens-power-appearance-title\">\n <h3 id=\"freelens-power-appearance-title\">\n <Renderer.Component.Icon material=\"palette\" size={20} /> Appearance\n </h3>\n <AppearancePreferences />\n <div style={{ display: \"flex\", justifyContent: \"flex-end\", marginTop: 20 }}>\n <Renderer.Component.Button\n style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6 }}\n onClick={() => Renderer.Navigation.navigate(`/extension/${EXTENSION_ID}/cluster-icons`)}\n title=\"Create matching icons for your saved clusters\"\n >\n <Renderer.Component.Icon material=\"palette\" size={16} /> Cluster icons\n </Renderer.Component.Button>\n </div>\n </section>\n <section id=\"freelens-power-monitoring-section\" tabIndex={-1} aria-labelledby=\"freelens-power-monitoring-title\">\n <h3 id=\"freelens-power-monitoring-title\">\n <Renderer.Component.Icon material=\"show_chart\" size={20} /> Monitoring\n </h3>\n <MonitoringPreferences />\n </section>\n <section id=\"freelens-power-logs-section\" tabIndex={-1} aria-labelledby=\"freelens-power-logs-title\">\n <h3 id=\"freelens-power-logs-title\">\n <Renderer.Component.Icon material=\"subject\" size={20} /> Power Logs\n </h3>\n <LogsPreferences />\n </section>\n <section id=\"freelens-power-shell-section\" tabIndex={-1} aria-labelledby=\"freelens-power-shell-title\">\n <h3 id=\"freelens-power-shell-title\">\n <Renderer.Component.Icon material=\"vpn_key\" size={20} /> Secure Node Shell\n </h3>\n <NodeShellPreferences />\n </section>\n <section id=\"freelens-power-ai-section\" tabIndex={-1} aria-labelledby=\"freelens-power-ai-title\">\n <h3 id=\"freelens-power-ai-title\">\n <Renderer.Component.Icon material=\"chat_bubble_outline\" size={20} /> AI assistant\n </h3>\n <AiPreferences />\n </section>\n <section id=\"freelens-power-dashboard-section\" tabIndex={-1} aria-labelledby=\"freelens-power-dashboard-title\">\n <h3 id=\"freelens-power-dashboard-title\">\n <Renderer.Component.Icon material=\"dashboard\" size={20} /> Dashboard\n </h3>\n <DashboardPreferences />\n </section>\n </div>\n);\n"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ //#region src/renderer/preferences/section-navigation.ts
2
+ function focusPreferencesSection(id) {
3
+ const section = document.getElementById(`freelens-power-${id}-section`);
4
+ if (!section) return false;
5
+ requestAnimationFrame(() => {
6
+ if (!section.isConnected) return;
7
+ section.focus({ preventScroll: true });
8
+ section.scrollIntoView({
9
+ block: "start",
10
+ behavior: "instant"
11
+ });
12
+ });
13
+ return true;
14
+ }
15
+ var cancelPendingNavigation;
16
+ function openPreferencesSection(id, navigate) {
17
+ cancelPendingNavigation?.();
18
+ const observer = new MutationObserver(() => {
19
+ if (focusPreferencesSection(id)) cleanup();
20
+ });
21
+ const timeout = window.setTimeout(cleanup, 1e4);
22
+ function cleanup() {
23
+ observer.disconnect();
24
+ window.clearTimeout(timeout);
25
+ if (cancelPendingNavigation === cleanup) cancelPendingNavigation = void 0;
26
+ }
27
+ cancelPendingNavigation = cleanup;
28
+ observer.observe(document.body, {
29
+ childList: true,
30
+ subtree: true
31
+ });
32
+ try {
33
+ navigate();
34
+ if (focusPreferencesSection(id)) cleanup();
35
+ } catch (error) {
36
+ cleanup();
37
+ throw error;
38
+ }
39
+ }
40
+ //#endregion
41
+ exports.focusPreferencesSection = focusPreferencesSection;
42
+ exports.openPreferencesSection = openPreferencesSection;
43
+
44
+ //# sourceMappingURL=section-navigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"section-navigation.js","names":[],"sources":["../../../src/renderer/preferences/section-navigation.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\nexport function focusPreferencesSection(id: string): boolean {\n const section = document.getElementById(`freelens-power-${id}-section`);\n if (!section) return false;\n requestAnimationFrame(() => {\n if (!section.isConnected) return;\n section.focus({ preventScroll: true });\n section.scrollIntoView({ block: \"start\", behavior: \"instant\" });\n });\n return true;\n}\n\nlet cancelPendingNavigation: (() => void) | undefined;\n\nexport function openPreferencesSection(id: string, navigate: () => void): void {\n cancelPendingNavigation?.();\n const observer = new MutationObserver(() => {\n if (focusPreferencesSection(id)) cleanup();\n });\n const timeout = window.setTimeout(cleanup, 10_000);\n function cleanup() {\n observer.disconnect();\n window.clearTimeout(timeout);\n if (cancelPendingNavigation === cleanup) cancelPendingNavigation = undefined;\n }\n cancelPendingNavigation = cleanup;\n observer.observe(document.body, { childList: true, subtree: true });\n try {\n navigate();\n if (focusPreferencesSection(id)) cleanup();\n } catch (error) {\n cleanup();\n throw error;\n }\n}\n"],"mappings":";AAEA,SAAgB,wBAAwB,IAAqB;CAC3D,MAAM,UAAU,SAAS,eAAe,kBAAkB,GAAG,SAAS;CACtE,IAAI,CAAC,SAAS,OAAO;CACrB,4BAA4B;EAC1B,IAAI,CAAC,QAAQ,aAAa;EAC1B,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;EACrC,QAAQ,eAAe;GAAE,OAAO;GAAS,UAAU;EAAU,CAAC;CAChE,CAAC;CACD,OAAO;AACT;AAEA,IAAI;AAEJ,SAAgB,uBAAuB,IAAY,UAA4B;CAC7E,0BAA0B;CAC1B,MAAM,WAAW,IAAI,uBAAuB;EAC1C,IAAI,wBAAwB,EAAE,GAAG,QAAQ;CAC3C,CAAC;CACD,MAAM,UAAU,OAAO,WAAW,SAAS,GAAM;CACjD,SAAS,UAAU;EACjB,SAAS,WAAW;EACpB,OAAO,aAAa,OAAO;EAC3B,IAAI,4BAA4B,SAAS,0BAA0B,KAAA;CACrE;CACA,0BAA0B;CAC1B,SAAS,QAAQ,SAAS,MAAM;EAAE,WAAW;EAAM,SAAS;CAAK,CAAC;CAClE,IAAI;EACF,SAAS;EACT,IAAI,wBAAwB,EAAE,GAAG,QAAQ;CAC3C,SAAS,OAAO;EACd,QAAQ;EACR,MAAM;CACR;AACF"}
@@ -1,3 +1,4 @@
1
+ const require_resource_search_kind_priority = require("./src/common/resource-search-kind-priority.js");
1
2
  const require_resource_search_distance = require("./resource-search-distance.js");
2
3
  //#region src/renderer/resource-search-model.ts
3
4
  var RESOURCE_SEARCH_MAX_DOCUMENT_CHARS = 4096;
@@ -16,7 +17,7 @@ function rankResources(resources, tokens) {
16
17
  const namespace = (resource.namespace ?? "").toLocaleLowerCase();
17
18
  return total + (name === token ? 0 : name.startsWith(token) ? 1 : kind === token ? 2 : name.includes(token) ? 3 : kind.includes(token) ? 4 : namespace.includes(token) ? 5 : 6);
18
19
  }, 0);
19
- return resources.sort((a, b) => score(a) - score(b) || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));
20
+ return resources.sort((a, b) => score(a) - score(b) || require_resource_search_kind_priority.resourceKindPriority(a.kind) - require_resource_search_kind_priority.resourceKindPriority(b.kind) || a.kind.localeCompare(b.kind) || (a.group ?? "").localeCompare(b.group ?? "") || a.name.localeCompare(b.name));
20
21
  }
21
22
  function createResourceSearchIndex(resources) {
22
23
  const documents = resources;
@@ -1 +1 @@
1
- {"version":3,"file":"resource-search-model.js","names":[],"sources":["../../src/renderer/resource-search-model.ts"],"sourcesContent":["import { boundedNameDistance } from \"./resource-search-distance\";\n\nimport type { SearchField } from \"./resource-search-fields\";\nexport interface SearchResource {\n kind: string;\n aliases?: string[];\n name: string;\n namespace?: string;\n labels: Record<string, string>;\n path: string;\n fields?: SearchField[];\n}\n\nexport const RESOURCE_SEARCH_MAX_DOCUMENT_CHARS = 4096;\nexport const queryTokens = (query: string) =>\n query.slice(0, 300).toLocaleLowerCase().trim().split(/\\s+/).filter(Boolean);\nconst metadataText = (resource: SearchResource) =>\n [\n resource.kind,\n ...(resource.aliases ?? []),\n resource.name,\n resource.namespace ?? \"\",\n ...Object.entries(resource.labels).map(([key, value]) => `${key}=${value}`),\n ].join(\" \");\nfunction rankResources(resources: SearchResource[], tokens: string[]) {\n const score = (resource: SearchResource) =>\n tokens.reduce((total, token) => {\n const name = resource.name.toLocaleLowerCase();\n const kind = resource.kind.toLocaleLowerCase();\n const namespace = (resource.namespace ?? \"\").toLocaleLowerCase();\n return (\n total +\n (name === token\n ? 0\n : name.startsWith(token)\n ? 1\n : kind === token\n ? 2\n : name.includes(token)\n ? 3\n : kind.includes(token)\n ? 4\n : namespace.includes(token)\n ? 5\n : 6)\n );\n }, 0);\n\n return resources.sort((a, b) => score(a) - score(b) || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));\n}\n\nexport function createResourceSearchIndex(resources: SearchResource[]) {\n const documents = resources;\n const vocabulary = new Map<string, Set<number>>();\n const names = new Map<number, Map<string, Set<number>>>();\n let lastQuery: string | undefined;\n let lastResults: SearchResource[] = [];\n let similarPaths = new Set<string>();\n let partsPaths = new Set<string>();\n const nameParts = new Map<number, string[]>();\n const partVocabulary = new Map<string, Set<number>>();\n const snippets = new Map<string, SearchField[]>();\n const metadata = new Map<string, string>();\n documents.forEach((resource, id) => {\n const parts = resource.name.toLocaleLowerCase().split(/[-._]/).filter(Boolean).slice(0, 16);\n nameParts.set(id, parts);\n for (const part of parts) {\n let ids = partVocabulary.get(part);\n if (!ids) {\n ids = new Set();\n partVocabulary.set(part, ids);\n }\n ids.add(id);\n }\n for (const name of new Set(\n [resource.name, ...resource.name.split(/[-.]/)].map((name) => name.toLocaleLowerCase()),\n )) {\n if (name.length < 3 || name.length > 64) continue;\n let bucket = names.get(name.length);\n if (!bucket) {\n bucket = new Map();\n names.set(name.length, bucket);\n }\n let ids = bucket.get(name);\n if (!ids) {\n ids = new Set();\n bucket.set(name, ids);\n }\n ids.add(id);\n }\n const identity = metadataText(resource).slice(0, RESOURCE_SEARCH_MAX_DOCUMENT_CHARS);\n metadata.set(resource.path, identity.toLocaleLowerCase());\n let remaining = RESOURCE_SEARCH_MAX_DOCUMENT_CHARS - identity.length;\n const fields: SearchField[] = [];\n for (const field of resource.fields ?? []) {\n if (remaining <= 1) break;\n const text = field.text.slice(0, remaining - 1);\n fields.push({ label: field.label, text });\n remaining -= text.length + 1;\n }\n snippets.set(resource.path, fields);\n for (const word of new Set(\n [identity, ...fields.map((field) => field.text)].join(\" \").toLocaleLowerCase().split(/\\s+/),\n )) {\n if (!word) continue;\n let ids = vocabulary.get(word);\n if (!ids) {\n ids = new Set();\n vocabulary.set(word, ids);\n }\n ids.add(id);\n }\n });\n return {\n search(query: string): SearchResource[] {\n query = query.slice(0, 300);\n if (lastQuery === query) return lastResults;\n lastQuery = query;\n similarPaths = new Set();\n partsPaths = new Set();\n lastResults = [];\n const tokens = queryTokens(query);\n const penalties = new Map<number, number>();\n let candidates: Set<number> | undefined;\n for (const token of tokens) {\n const found = new Set<number>();\n for (const [word, ids] of vocabulary) if (word.includes(token)) for (const id of ids) found.add(id);\n if (/^[a-z0-9]+(?:[-._][a-z0-9]+)+$/.test(token)) {\n const parts = token.split(/[-._]/);\n if (parts.length <= 16) {\n let possible: Set<number> | undefined;\n for (const part of parts) {\n const ids = partVocabulary.get(part) ?? new Set<number>();\n possible = possible === undefined ? ids : new Set([...possible].filter((id) => ids.has(id)));\n }\n for (const id of possible ?? []) {\n if (found.has(id)) continue;\n let offset = 0;\n for (const part of nameParts.get(id) ?? []) if (part === parts[offset]) offset++;\n if (offset === parts.length) {\n found.add(id);\n penalties.set(id, (penalties.get(id) ?? 0) + 1);\n partsPaths.add(documents[id].path);\n }\n }\n }\n }\n const exact = new Set(found);\n const limit = token.length >= 7 && token.length <= 64 ? 2 : token.length >= 4 && token.length <= 6 ? 1 : 0;\n if (limit) {\n const distances = new Map<number, number>();\n for (let length = token.length - limit; length <= token.length + limit; length++) {\n for (const [name, ids] of names.get(length) ?? []) {\n const distance = boundedNameDistance(token, name, limit);\n if (distance > limit) continue;\n for (const id of ids)\n if (!exact.has(id)) distances.set(id, Math.min(distances.get(id) ?? Infinity, distance));\n }\n }\n for (const [id, distance] of distances) {\n found.add(id);\n penalties.set(id, (penalties.get(id) ?? 0) + 10 * distance);\n similarPaths.add(documents[id].path);\n }\n }\n candidates = candidates === undefined ? found : new Set([...candidates].filter((id) => found.has(id)));\n if (!candidates.size) return [];\n }\n lastResults = rankResources(\n candidates === undefined ? [...documents] : [...candidates].map((id) => documents[id]),\n tokens,\n );\n const penaltyByPath = new Map([...penalties].map(([id, penalty]) => [documents[id].path, penalty]));\n lastResults.sort((left, right) => (penaltyByPath.get(left.path) ?? 0) - (penaltyByPath.get(right.path) ?? 0));\n return lastResults;\n },\n nameParts(resource: SearchResource): boolean {\n return partsPaths.has(resource.path);\n },\n similarName(resource: SearchResource): boolean {\n return similarPaths.has(resource.path);\n },\n snippet(resource: SearchResource, query: string): SearchField | undefined {\n const tokens = queryTokens(query);\n const identity = metadata.get(resource.path) ?? \"\";\n const bodyTokens = tokens.filter((token) => !identity.includes(token));\n if (!bodyTokens.length) return;\n const field = snippets\n .get(resource.path)\n ?.find((item) => bodyTokens.some((token) => item.text.toLocaleLowerCase().includes(token)));\n if (!field) return;\n const offset = Math.max(\n 0,\n Math.min(\n ...bodyTokens.map((token) => field.text.toLocaleLowerCase().indexOf(token)).filter((index) => index >= 0),\n ) - 35,\n );\n return {\n label: field.label,\n text:\n (offset ? \"…\" : \"\") + field.text.slice(offset, offset + 150) + (field.text.length > offset + 150 ? \"…\" : \"\"),\n };\n },\n };\n}\n\nexport function matchResources(resources: SearchResource[], query: string): SearchResource[] {\n return createResourceSearchIndex(resources).search(query);\n}\n\nexport function createDoubleShift() {\n let last = 0;\n let held = false;\n const otherKeys = new Set<string>();\n return (\n event: Pick<KeyboardEvent, \"key\" | \"type\" | \"repeat\" | \"altKey\" | \"ctrlKey\" | \"metaKey\"> &\n Partial<Pick<KeyboardEvent, \"code\">>,\n now: number,\n ) => {\n const physicalKey = event.code || event.key;\n if (event.type === \"keyup\") {\n if (event.key === \"Shift\") held = false;\n else otherKeys.delete(physicalKey);\n if (event.key === \"Meta\") {\n otherKeys.clear();\n held = false;\n last = 0;\n }\n return false;\n }\n if (event.key !== \"Shift\") otherKeys.add(physicalKey);\n if (event.key !== \"Shift\" || otherKeys.size > 0 || event.altKey || event.ctrlKey || event.metaKey) {\n last = 0;\n return false;\n }\n if (event.repeat || held) return false;\n held = true;\n const hit = last > 0 && now - last <= 400;\n last = hit ? 0 : now;\n return hit;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resource-search-model.js","names":[],"sources":["../../src/renderer/resource-search-model.ts"],"sourcesContent":["import { resourceKindPriority } from \"../common/resource-search-kind-priority\";\nimport { boundedNameDistance } from \"./resource-search-distance\";\n\nimport type { SearchField } from \"./resource-search-fields\";\nexport interface SearchResource {\n kind: string;\n group?: string;\n aliases?: string[];\n name: string;\n namespace?: string;\n labels: Record<string, string>;\n path: string;\n fields?: SearchField[];\n}\n\nexport const RESOURCE_SEARCH_MAX_DOCUMENT_CHARS = 4096;\nexport const queryTokens = (query: string) =>\n query.slice(0, 300).toLocaleLowerCase().trim().split(/\\s+/).filter(Boolean);\nconst metadataText = (resource: SearchResource) =>\n [\n resource.kind,\n ...(resource.aliases ?? []),\n resource.name,\n resource.namespace ?? \"\",\n ...Object.entries(resource.labels).map(([key, value]) => `${key}=${value}`),\n ].join(\" \");\nfunction rankResources(resources: SearchResource[], tokens: string[]) {\n const score = (resource: SearchResource) =>\n tokens.reduce((total, token) => {\n const name = resource.name.toLocaleLowerCase();\n const kind = resource.kind.toLocaleLowerCase();\n const namespace = (resource.namespace ?? \"\").toLocaleLowerCase();\n return (\n total +\n (name === token\n ? 0\n : name.startsWith(token)\n ? 1\n : kind === token\n ? 2\n : name.includes(token)\n ? 3\n : kind.includes(token)\n ? 4\n : namespace.includes(token)\n ? 5\n : 6)\n );\n }, 0);\n\n return resources.sort(\n (a, b) =>\n score(a) - score(b) ||\n resourceKindPriority(a.kind) - resourceKindPriority(b.kind) ||\n a.kind.localeCompare(b.kind) ||\n (a.group ?? \"\").localeCompare(b.group ?? \"\") ||\n a.name.localeCompare(b.name),\n );\n}\n\nexport function createResourceSearchIndex(resources: SearchResource[]) {\n const documents = resources;\n const vocabulary = new Map<string, Set<number>>();\n const names = new Map<number, Map<string, Set<number>>>();\n let lastQuery: string | undefined;\n let lastResults: SearchResource[] = [];\n let similarPaths = new Set<string>();\n let partsPaths = new Set<string>();\n const nameParts = new Map<number, string[]>();\n const partVocabulary = new Map<string, Set<number>>();\n const snippets = new Map<string, SearchField[]>();\n const metadata = new Map<string, string>();\n documents.forEach((resource, id) => {\n const parts = resource.name.toLocaleLowerCase().split(/[-._]/).filter(Boolean).slice(0, 16);\n nameParts.set(id, parts);\n for (const part of parts) {\n let ids = partVocabulary.get(part);\n if (!ids) {\n ids = new Set();\n partVocabulary.set(part, ids);\n }\n ids.add(id);\n }\n for (const name of new Set(\n [resource.name, ...resource.name.split(/[-.]/)].map((name) => name.toLocaleLowerCase()),\n )) {\n if (name.length < 3 || name.length > 64) continue;\n let bucket = names.get(name.length);\n if (!bucket) {\n bucket = new Map();\n names.set(name.length, bucket);\n }\n let ids = bucket.get(name);\n if (!ids) {\n ids = new Set();\n bucket.set(name, ids);\n }\n ids.add(id);\n }\n const identity = metadataText(resource).slice(0, RESOURCE_SEARCH_MAX_DOCUMENT_CHARS);\n metadata.set(resource.path, identity.toLocaleLowerCase());\n let remaining = RESOURCE_SEARCH_MAX_DOCUMENT_CHARS - identity.length;\n const fields: SearchField[] = [];\n for (const field of resource.fields ?? []) {\n if (remaining <= 1) break;\n const text = field.text.slice(0, remaining - 1);\n fields.push({ label: field.label, text });\n remaining -= text.length + 1;\n }\n snippets.set(resource.path, fields);\n for (const word of new Set(\n [identity, ...fields.map((field) => field.text)].join(\" \").toLocaleLowerCase().split(/\\s+/),\n )) {\n if (!word) continue;\n let ids = vocabulary.get(word);\n if (!ids) {\n ids = new Set();\n vocabulary.set(word, ids);\n }\n ids.add(id);\n }\n });\n return {\n search(query: string): SearchResource[] {\n query = query.slice(0, 300);\n if (lastQuery === query) return lastResults;\n lastQuery = query;\n similarPaths = new Set();\n partsPaths = new Set();\n lastResults = [];\n const tokens = queryTokens(query);\n const penalties = new Map<number, number>();\n let candidates: Set<number> | undefined;\n for (const token of tokens) {\n const found = new Set<number>();\n for (const [word, ids] of vocabulary) if (word.includes(token)) for (const id of ids) found.add(id);\n if (/^[a-z0-9]+(?:[-._][a-z0-9]+)+$/.test(token)) {\n const parts = token.split(/[-._]/);\n if (parts.length <= 16) {\n let possible: Set<number> | undefined;\n for (const part of parts) {\n const ids = partVocabulary.get(part) ?? new Set<number>();\n possible = possible === undefined ? ids : new Set([...possible].filter((id) => ids.has(id)));\n }\n for (const id of possible ?? []) {\n if (found.has(id)) continue;\n let offset = 0;\n for (const part of nameParts.get(id) ?? []) if (part === parts[offset]) offset++;\n if (offset === parts.length) {\n found.add(id);\n penalties.set(id, (penalties.get(id) ?? 0) + 1);\n partsPaths.add(documents[id].path);\n }\n }\n }\n }\n const exact = new Set(found);\n const limit = token.length >= 7 && token.length <= 64 ? 2 : token.length >= 4 && token.length <= 6 ? 1 : 0;\n if (limit) {\n const distances = new Map<number, number>();\n for (let length = token.length - limit; length <= token.length + limit; length++) {\n for (const [name, ids] of names.get(length) ?? []) {\n const distance = boundedNameDistance(token, name, limit);\n if (distance > limit) continue;\n for (const id of ids)\n if (!exact.has(id)) distances.set(id, Math.min(distances.get(id) ?? Infinity, distance));\n }\n }\n for (const [id, distance] of distances) {\n found.add(id);\n penalties.set(id, (penalties.get(id) ?? 0) + 10 * distance);\n similarPaths.add(documents[id].path);\n }\n }\n candidates = candidates === undefined ? found : new Set([...candidates].filter((id) => found.has(id)));\n if (!candidates.size) return [];\n }\n lastResults = rankResources(\n candidates === undefined ? [...documents] : [...candidates].map((id) => documents[id]),\n tokens,\n );\n const penaltyByPath = new Map([...penalties].map(([id, penalty]) => [documents[id].path, penalty]));\n lastResults.sort((left, right) => (penaltyByPath.get(left.path) ?? 0) - (penaltyByPath.get(right.path) ?? 0));\n return lastResults;\n },\n nameParts(resource: SearchResource): boolean {\n return partsPaths.has(resource.path);\n },\n similarName(resource: SearchResource): boolean {\n return similarPaths.has(resource.path);\n },\n snippet(resource: SearchResource, query: string): SearchField | undefined {\n const tokens = queryTokens(query);\n const identity = metadata.get(resource.path) ?? \"\";\n const bodyTokens = tokens.filter((token) => !identity.includes(token));\n if (!bodyTokens.length) return;\n const field = snippets\n .get(resource.path)\n ?.find((item) => bodyTokens.some((token) => item.text.toLocaleLowerCase().includes(token)));\n if (!field) return;\n const offset = Math.max(\n 0,\n Math.min(\n ...bodyTokens.map((token) => field.text.toLocaleLowerCase().indexOf(token)).filter((index) => index >= 0),\n ) - 35,\n );\n return {\n label: field.label,\n text:\n (offset ? \"…\" : \"\") + field.text.slice(offset, offset + 150) + (field.text.length > offset + 150 ? \"…\" : \"\"),\n };\n },\n };\n}\n\nexport function matchResources(resources: SearchResource[], query: string): SearchResource[] {\n return createResourceSearchIndex(resources).search(query);\n}\n\nexport function createDoubleShift() {\n let last = 0;\n let held = false;\n const otherKeys = new Set<string>();\n return (\n event: Pick<KeyboardEvent, \"key\" | \"type\" | \"repeat\" | \"altKey\" | \"ctrlKey\" | \"metaKey\"> &\n Partial<Pick<KeyboardEvent, \"code\">>,\n now: number,\n ) => {\n const physicalKey = event.code || event.key;\n if (event.type === \"keyup\") {\n if (event.key === \"Shift\") held = false;\n else otherKeys.delete(physicalKey);\n if (event.key === \"Meta\") {\n otherKeys.clear();\n held = false;\n last = 0;\n }\n return false;\n }\n if (event.key !== \"Shift\") otherKeys.add(physicalKey);\n if (event.key !== \"Shift\" || otherKeys.size > 0 || event.altKey || event.ctrlKey || event.metaKey) {\n last = 0;\n return false;\n }\n if (event.repeat || held) return false;\n held = true;\n const hit = last > 0 && now - last <= 400;\n last = hit ? 0 : now;\n return hit;\n };\n}\n"],"mappings":"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"}
@@ -156,7 +156,7 @@ function createSearchSynchronizer(options) {
156
156
  seen.add(token);
157
157
  } while (token);
158
158
  } catch {
159
- status.message = "Namespace discovery denied or incomplete; add a known namespace to continue";
159
+ status.message = "Namespace discovery denied or incomplete; search is limited to accessible namespaces";
160
160
  }
161
161
  fallbackNamespaces = namespaces;
162
162
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resource-search-sync.js","names":[],"sources":["../../src/renderer/resource-search-sync.ts"],"sourcesContent":["import {\n collectionPath,\n discoverSearchCollections,\n type MetadataPage,\n projectMetadataPage,\n type SearchCollection,\n type SearchTransport,\n searchErrorStatus,\n} from \"./resource-search-discovery\";\n\nimport type { SearchDocument } from \"../common/resource-search\";\n\nexport interface SearchSink {\n begin(scope: string, scanId: string): Promise<unknown>;\n append(scope: string, scanId: string, documents: SearchDocument[]): Promise<unknown>;\n complete(scope: string, scanId: string): Promise<unknown>;\n invalidate(scope: string): Promise<unknown>;\n reconcile?(resources: string[]): Promise<unknown>;\n}\nexport interface SearchCoverage {\n scanned: number;\n discovered: number;\n denied: number;\n unavailable: number;\n unsupported: number;\n indexing: boolean;\n message?: string;\n kinds?: string[];\n namespaces?: string[];\n completedAt?: number;\n storageFull?: boolean;\n}\nexport const SEARCH_STORAGE_FULL_MESSAGE = \"Search storage is full; clear the local search cache to continue\";\nexport const isSearchStorageFull = (error: unknown): boolean =>\n error instanceof Error && error.message.endsWith(SEARCH_STORAGE_FULL_MESSAGE);\nconst metadataAccept = { Accept: \"application/json;as=PartialObjectMetadataList;g=meta.k8s.io;v=v1\" };\nexport function createSearchSynchronizer(options: {\n transport: SearchTransport;\n sink: SearchSink;\n signal: AbortSignal;\n onStatus(status: SearchCoverage): void;\n namespaces?: () => string[];\n delay?: (milliseconds: number) => Promise<void>;\n refreshMs?: number;\n}) {\n const { transport, sink, signal } = options;\n const status: SearchCoverage = {\n scanned: 0,\n discovered: 0,\n denied: 0,\n unavailable: 0,\n unsupported: 0,\n indexing: true,\n };\n const emit = () => options.onStatus({ ...status });\n const delay =\n options.delay ??\n ((milliseconds: number) =>\n new Promise<void>((resolve) => {\n if (signal.aborted) {\n resolve();\n return;\n }\n const done = () => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", done);\n resolve();\n };\n const timer = setTimeout(done, milliseconds);\n signal.addEventListener(\"abort\", done, { once: true });\n }));\n let sequence = 0;\n let nextRequest = 0;\n const request = async <T>(\n path: string,\n query?: Record<string, string | number>,\n headers?: Record<string, string>,\n ): Promise<T> => {\n await delay(Math.max(0, nextRequest - Date.now()));\n if (signal.aborted) throw new Error(\"Search synchronization canceled\");\n nextRequest = Date.now() + 500;\n for (let attempt = 0; ; attempt++) {\n try {\n return await transport.get<T>(path, query, headers);\n } catch (error) {\n const code = searchErrorStatus(error);\n if (signal.aborted || attempt >= 3 || (code !== 429 && code < 500)) throw error;\n const retry = (error as { headers?: { get?(name: string): string | null }; retryAfter?: string }).retryAfter;\n const retryMilliseconds = retry\n ? /^\\d+$/.test(retry)\n ? Number(retry) * 1000\n : Date.parse(retry) - Date.now()\n : NaN;\n await delay(\n Number.isFinite(retryMilliseconds)\n ? Math.min(86400000, Math.max(0, retryMilliseconds))\n : Math.min(30000, 1000 * 2 ** attempt) + Math.random() * 250,\n );\n }\n }\n };\n const limited: SearchTransport = { get: request };\n const scan = async (type: SearchCollection, namespace?: string): Promise<void> => {\n const scope = `${type.group}/${type.resource}/${namespace ?? \"*\"}`;\n for (let restart = 0; restart < 2; restart++) {\n const scanId = `${Date.now()}-${++sequence}`;\n await sink.begin(scope, scanId);\n let continuation = \"\";\n let pageSize = 250;\n const seen = new Set<string>();\n try {\n do {\n let page: MetadataPage;\n for (;;) {\n const query = { limit: pageSize, ...(continuation ? { continue: continuation } : {}) };\n try {\n try {\n page = await request<MetadataPage>(collectionPath(type, namespace), query, metadataAccept);\n } catch (error) {\n if (searchErrorStatus(error) !== 406 || !type.group) throw error;\n page = await request<MetadataPage>(collectionPath(type, namespace), query, {\n Accept: \"application/json\",\n });\n }\n break;\n } catch (error) {\n if (!(error as { oversized?: boolean }).oversized || pageSize === 1) throw error;\n pageSize = Math.max(1, Math.floor(pageSize / 2));\n }\n }\n if (signal.aborted) return;\n const documents = projectMetadataPage(type, page);\n status.namespaces = [\n ...new Set([\n ...(status.namespaces ?? []),\n ...documents.flatMap((document) => (document.namespace ? [document.namespace] : [])),\n ]),\n ].sort();\n await sink.append(scope, scanId, documents);\n continuation = page.metadata?.continue ?? \"\";\n if (continuation && seen.has(continuation)) throw new Error(\"Repeated inventory continuation\");\n seen.add(continuation);\n } while (continuation);\n if (!signal.aborted) await sink.complete(scope, scanId);\n return;\n } catch (error) {\n const code = searchErrorStatus(error);\n if (code === 410 && restart === 0) continue;\n if (code === 403) await sink.invalidate(scope);\n throw error;\n }\n }\n };\n return {\n async run(): Promise<void> {\n try {\n while (!signal.aborted) {\n status.indexing = true;\n status.scanned = 0;\n status.denied = 0;\n status.unavailable = 0;\n status.unsupported = 0;\n status.message = undefined;\n emit();\n let discoveryFailures = 0;\n const collections = await discoverSearchCollections(limited, () => {\n discoveryFailures++;\n status.unavailable++;\n emit();\n });\n if (signal.aborted) return;\n if (discoveryFailures === 0)\n await sink.reconcile?.(collections.map((type) => `${type.group}/${type.resource}`));\n else status.message = \"API discovery incomplete; cached resource types may be stale\";\n status.kinds = [...new Set(collections.map((type) => type.kind))].sort();\n status.discovered = collections.length;\n emit();\n let fallbackNamespaces: string[] | undefined;\n for (const type of collections) {\n if (signal.aborted) return;\n try {\n await scan(type);\n } catch (error) {\n if (signal.aborted) return;\n if (isSearchStorageFull(error)) throw error;\n if (searchErrorStatus(error) === 403) {\n status.denied++;\n if (type.namespaced) {\n let namespaces = fallbackNamespaces ?? options.namespaces?.() ?? [];\n if (fallbackNamespaces === undefined) {\n try {\n const namespaceType: SearchCollection = {\n group: \"\",\n version: \"v1\",\n resource: \"namespaces\",\n kind: \"Namespace\",\n namespaced: false,\n aliases: [],\n };\n let token = \"\";\n let limit = 250;\n const seen = new Set<string>();\n do {\n let page: MetadataPage;\n for (;;) {\n try {\n page = await request<MetadataPage>(\n collectionPath(namespaceType),\n { limit, ...(token ? { continue: token } : {}) },\n metadataAccept,\n );\n break;\n } catch (error) {\n if (!(error as { oversized?: boolean }).oversized || limit === 1) throw error;\n limit = Math.max(1, Math.floor(limit / 2));\n }\n }\n namespaces = [\n ...new Set([\n ...namespaces,\n ...(page.items ?? []).flatMap((item) => (item.metadata?.name ? [item.metadata.name] : [])),\n ]),\n ];\n token = page.metadata?.continue ?? \"\";\n if (token && seen.has(token)) throw new Error(\"Repeated namespace continuation\");\n seen.add(token);\n } while (token);\n } catch {\n status.message = \"Namespace discovery denied or incomplete; add a known namespace to continue\";\n }\n fallbackNamespaces = namespaces;\n }\n for (const namespace of namespaces) {\n if (signal.aborted) return;\n try {\n await scan(type, namespace);\n } catch (failure) {\n if (isSearchStorageFull(failure)) throw failure;\n if (searchErrorStatus(failure) === 403) status.denied++;\n else status.unavailable++;\n }\n }\n }\n } else if (searchErrorStatus(error) === 406) status.unsupported++;\n else status.unavailable++;\n }\n status.scanned++;\n emit();\n }\n status.indexing = false;\n status.completedAt = Date.now();\n emit();\n await delay(options.refreshMs ?? 10 * 60 * 1000);\n }\n } catch (error) {\n if (!isSearchStorageFull(error)) throw error;\n status.indexing = false;\n status.storageFull = true;\n status.message = SEARCH_STORAGE_FULL_MESSAGE;\n emit();\n }\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resource-search-sync.js","names":[],"sources":["../../src/renderer/resource-search-sync.ts"],"sourcesContent":["import {\n collectionPath,\n discoverSearchCollections,\n type MetadataPage,\n projectMetadataPage,\n type SearchCollection,\n type SearchTransport,\n searchErrorStatus,\n} from \"./resource-search-discovery\";\n\nimport type { SearchDocument } from \"../common/resource-search\";\n\nexport interface SearchSink {\n begin(scope: string, scanId: string): Promise<unknown>;\n append(scope: string, scanId: string, documents: SearchDocument[]): Promise<unknown>;\n complete(scope: string, scanId: string): Promise<unknown>;\n invalidate(scope: string): Promise<unknown>;\n reconcile?(resources: string[]): Promise<unknown>;\n}\nexport interface SearchCoverage {\n scanned: number;\n discovered: number;\n denied: number;\n unavailable: number;\n unsupported: number;\n indexing: boolean;\n message?: string;\n kinds?: string[];\n namespaces?: string[];\n completedAt?: number;\n storageFull?: boolean;\n}\nexport const SEARCH_STORAGE_FULL_MESSAGE = \"Search storage is full; clear the local search cache to continue\";\nexport const isSearchStorageFull = (error: unknown): boolean =>\n error instanceof Error && error.message.endsWith(SEARCH_STORAGE_FULL_MESSAGE);\nconst metadataAccept = { Accept: \"application/json;as=PartialObjectMetadataList;g=meta.k8s.io;v=v1\" };\nexport function createSearchSynchronizer(options: {\n transport: SearchTransport;\n sink: SearchSink;\n signal: AbortSignal;\n onStatus(status: SearchCoverage): void;\n namespaces?: () => string[];\n delay?: (milliseconds: number) => Promise<void>;\n refreshMs?: number;\n}) {\n const { transport, sink, signal } = options;\n const status: SearchCoverage = {\n scanned: 0,\n discovered: 0,\n denied: 0,\n unavailable: 0,\n unsupported: 0,\n indexing: true,\n };\n const emit = () => options.onStatus({ ...status });\n const delay =\n options.delay ??\n ((milliseconds: number) =>\n new Promise<void>((resolve) => {\n if (signal.aborted) {\n resolve();\n return;\n }\n const done = () => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", done);\n resolve();\n };\n const timer = setTimeout(done, milliseconds);\n signal.addEventListener(\"abort\", done, { once: true });\n }));\n let sequence = 0;\n let nextRequest = 0;\n const request = async <T>(\n path: string,\n query?: Record<string, string | number>,\n headers?: Record<string, string>,\n ): Promise<T> => {\n await delay(Math.max(0, nextRequest - Date.now()));\n if (signal.aborted) throw new Error(\"Search synchronization canceled\");\n nextRequest = Date.now() + 500;\n for (let attempt = 0; ; attempt++) {\n try {\n return await transport.get<T>(path, query, headers);\n } catch (error) {\n const code = searchErrorStatus(error);\n if (signal.aborted || attempt >= 3 || (code !== 429 && code < 500)) throw error;\n const retry = (error as { headers?: { get?(name: string): string | null }; retryAfter?: string }).retryAfter;\n const retryMilliseconds = retry\n ? /^\\d+$/.test(retry)\n ? Number(retry) * 1000\n : Date.parse(retry) - Date.now()\n : NaN;\n await delay(\n Number.isFinite(retryMilliseconds)\n ? Math.min(86400000, Math.max(0, retryMilliseconds))\n : Math.min(30000, 1000 * 2 ** attempt) + Math.random() * 250,\n );\n }\n }\n };\n const limited: SearchTransport = { get: request };\n const scan = async (type: SearchCollection, namespace?: string): Promise<void> => {\n const scope = `${type.group}/${type.resource}/${namespace ?? \"*\"}`;\n for (let restart = 0; restart < 2; restart++) {\n const scanId = `${Date.now()}-${++sequence}`;\n await sink.begin(scope, scanId);\n let continuation = \"\";\n let pageSize = 250;\n const seen = new Set<string>();\n try {\n do {\n let page: MetadataPage;\n for (;;) {\n const query = { limit: pageSize, ...(continuation ? { continue: continuation } : {}) };\n try {\n try {\n page = await request<MetadataPage>(collectionPath(type, namespace), query, metadataAccept);\n } catch (error) {\n if (searchErrorStatus(error) !== 406 || !type.group) throw error;\n page = await request<MetadataPage>(collectionPath(type, namespace), query, {\n Accept: \"application/json\",\n });\n }\n break;\n } catch (error) {\n if (!(error as { oversized?: boolean }).oversized || pageSize === 1) throw error;\n pageSize = Math.max(1, Math.floor(pageSize / 2));\n }\n }\n if (signal.aborted) return;\n const documents = projectMetadataPage(type, page);\n status.namespaces = [\n ...new Set([\n ...(status.namespaces ?? []),\n ...documents.flatMap((document) => (document.namespace ? [document.namespace] : [])),\n ]),\n ].sort();\n await sink.append(scope, scanId, documents);\n continuation = page.metadata?.continue ?? \"\";\n if (continuation && seen.has(continuation)) throw new Error(\"Repeated inventory continuation\");\n seen.add(continuation);\n } while (continuation);\n if (!signal.aborted) await sink.complete(scope, scanId);\n return;\n } catch (error) {\n const code = searchErrorStatus(error);\n if (code === 410 && restart === 0) continue;\n if (code === 403) await sink.invalidate(scope);\n throw error;\n }\n }\n };\n return {\n async run(): Promise<void> {\n try {\n while (!signal.aborted) {\n status.indexing = true;\n status.scanned = 0;\n status.denied = 0;\n status.unavailable = 0;\n status.unsupported = 0;\n status.message = undefined;\n emit();\n let discoveryFailures = 0;\n const collections = await discoverSearchCollections(limited, () => {\n discoveryFailures++;\n status.unavailable++;\n emit();\n });\n if (signal.aborted) return;\n if (discoveryFailures === 0)\n await sink.reconcile?.(collections.map((type) => `${type.group}/${type.resource}`));\n else status.message = \"API discovery incomplete; cached resource types may be stale\";\n status.kinds = [...new Set(collections.map((type) => type.kind))].sort();\n status.discovered = collections.length;\n emit();\n let fallbackNamespaces: string[] | undefined;\n for (const type of collections) {\n if (signal.aborted) return;\n try {\n await scan(type);\n } catch (error) {\n if (signal.aborted) return;\n if (isSearchStorageFull(error)) throw error;\n if (searchErrorStatus(error) === 403) {\n status.denied++;\n if (type.namespaced) {\n let namespaces = fallbackNamespaces ?? options.namespaces?.() ?? [];\n if (fallbackNamespaces === undefined) {\n try {\n const namespaceType: SearchCollection = {\n group: \"\",\n version: \"v1\",\n resource: \"namespaces\",\n kind: \"Namespace\",\n namespaced: false,\n aliases: [],\n };\n let token = \"\";\n let limit = 250;\n const seen = new Set<string>();\n do {\n let page: MetadataPage;\n for (;;) {\n try {\n page = await request<MetadataPage>(\n collectionPath(namespaceType),\n { limit, ...(token ? { continue: token } : {}) },\n metadataAccept,\n );\n break;\n } catch (error) {\n if (!(error as { oversized?: boolean }).oversized || limit === 1) throw error;\n limit = Math.max(1, Math.floor(limit / 2));\n }\n }\n namespaces = [\n ...new Set([\n ...namespaces,\n ...(page.items ?? []).flatMap((item) => (item.metadata?.name ? [item.metadata.name] : [])),\n ]),\n ];\n token = page.metadata?.continue ?? \"\";\n if (token && seen.has(token)) throw new Error(\"Repeated namespace continuation\");\n seen.add(token);\n } while (token);\n } catch {\n status.message =\n \"Namespace discovery denied or incomplete; search is limited to accessible namespaces\";\n }\n fallbackNamespaces = namespaces;\n }\n for (const namespace of namespaces) {\n if (signal.aborted) return;\n try {\n await scan(type, namespace);\n } catch (failure) {\n if (isSearchStorageFull(failure)) throw failure;\n if (searchErrorStatus(failure) === 403) status.denied++;\n else status.unavailable++;\n }\n }\n }\n } else if (searchErrorStatus(error) === 406) status.unsupported++;\n else status.unavailable++;\n }\n status.scanned++;\n emit();\n }\n status.indexing = false;\n status.completedAt = Date.now();\n emit();\n await delay(options.refreshMs ?? 10 * 60 * 1000);\n }\n } catch (error) {\n if (!isSearchStorageFull(error)) throw error;\n status.indexing = false;\n status.storageFull = true;\n status.message = SEARCH_STORAGE_FULL_MESSAGE;\n emit();\n }\n },\n };\n}\n"],"mappings":"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"}