dsh-ci-diet 0.2.0 → 0.3.1

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 (126) hide show
  1. package/README.ko.md +39 -10
  2. package/README.md +50 -14
  3. package/client/index.d.ts +3 -0
  4. package/client/index.js +1035 -0
  5. package/dist/adapters/github/budget.d.ts +1 -1
  6. package/dist/adapters/github/budget.js +4 -2
  7. package/dist/adapters/github/budget.js.map +1 -1
  8. package/dist/adapters/github/client.d.ts +2 -0
  9. package/dist/adapters/github/client.js.map +1 -1
  10. package/dist/adapters/github/errors.d.ts +1 -1
  11. package/dist/adapters/github/errors.js +5 -3
  12. package/dist/adapters/github/errors.js.map +1 -1
  13. package/dist/adapters/github/transport.d.ts +10 -3
  14. package/dist/adapters/github/transport.js +115 -21
  15. package/dist/adapters/github/transport.js.map +1 -1
  16. package/dist/cli/index.d.ts +25 -0
  17. package/dist/cli/index.js +334 -233
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/core/analysis/coverage.js +2 -2
  20. package/dist/core/analysis/coverage.js.map +1 -1
  21. package/dist/core/config/load.d.ts +13 -0
  22. package/dist/core/config/load.js +71 -0
  23. package/dist/core/config/load.js.map +1 -0
  24. package/dist/core/config/plugin-settings.d.ts +37 -0
  25. package/dist/core/config/plugin-settings.js +75 -0
  26. package/dist/core/config/plugin-settings.js.map +1 -0
  27. package/dist/core/history/sync.d.ts +8 -3
  28. package/dist/core/history/sync.js +163 -45
  29. package/dist/core/history/sync.js.map +1 -1
  30. package/dist/core/patch/apply.d.ts +28 -7
  31. package/dist/core/patch/apply.js +341 -284
  32. package/dist/core/patch/apply.js.map +1 -1
  33. package/dist/core/patch/approval.d.ts +5 -0
  34. package/dist/core/patch/approval.js +26 -1
  35. package/dist/core/patch/approval.js.map +1 -1
  36. package/dist/core/patch/attestation.js +7 -3
  37. package/dist/core/patch/attestation.js.map +1 -1
  38. package/dist/core/patch/dependencies.d.ts +1 -0
  39. package/dist/core/patch/dependencies.js +176 -132
  40. package/dist/core/patch/dependencies.js.map +1 -1
  41. package/dist/core/patch/diff.d.ts +11 -0
  42. package/dist/core/patch/diff.js +76 -0
  43. package/dist/core/patch/diff.js.map +1 -0
  44. package/dist/core/patch/edits.d.ts +1 -1
  45. package/dist/core/patch/edits.js +7 -4
  46. package/dist/core/patch/edits.js.map +1 -1
  47. package/dist/core/patch/eligibility.d.ts +3 -2
  48. package/dist/core/patch/eligibility.js +22 -19
  49. package/dist/core/patch/eligibility.js.map +1 -1
  50. package/dist/core/patch/locks.d.ts +50 -0
  51. package/dist/core/patch/locks.js +196 -0
  52. package/dist/core/patch/locks.js.map +1 -0
  53. package/dist/core/patch/plan.d.ts +1 -2
  54. package/dist/core/patch/plan.js +2 -2
  55. package/dist/core/patch/plan.js.map +1 -1
  56. package/dist/core/patch/request-plan.d.ts +51 -0
  57. package/dist/core/patch/request-plan.js +122 -0
  58. package/dist/core/patch/request-plan.js.map +1 -0
  59. package/dist/core/patch/store.d.ts +123 -5
  60. package/dist/core/patch/store.js +722 -80
  61. package/dist/core/patch/store.js.map +1 -1
  62. package/dist/core/patch/verify.d.ts +4 -3
  63. package/dist/core/patch/verify.js +6 -4
  64. package/dist/core/patch/verify.js.map +1 -1
  65. package/dist/core/policy/constants.d.ts +34 -0
  66. package/dist/core/policy/constants.js +35 -0
  67. package/dist/core/policy/constants.js.map +1 -0
  68. package/dist/core/rules/cd001.js +107 -42
  69. package/dist/core/rules/cd001.js.map +1 -1
  70. package/dist/core/rules/cd003.js +42 -14
  71. package/dist/core/rules/cd003.js.map +1 -1
  72. package/dist/core/rules/cd005.d.ts +0 -5
  73. package/dist/core/rules/cd005.js +156 -68
  74. package/dist/core/rules/cd005.js.map +1 -1
  75. package/dist/core/rules/cd008.d.ts +1 -2
  76. package/dist/core/rules/cd008.js +2 -2
  77. package/dist/core/rules/cd008.js.map +1 -1
  78. package/dist/core/savings/report-metrics.js +5 -1
  79. package/dist/core/savings/report-metrics.js.map +1 -1
  80. package/dist/core/scan/binding.d.ts +34 -2
  81. package/dist/core/scan/binding.js +58 -17
  82. package/dist/core/scan/binding.js.map +1 -1
  83. package/dist/core/scan/discover.d.ts +2 -0
  84. package/dist/core/scan/discover.js +7 -6
  85. package/dist/core/scan/discover.js.map +1 -1
  86. package/dist/core/scan/scan.d.ts +4 -0
  87. package/dist/core/scan/scan.js +8 -2
  88. package/dist/core/scan/scan.js.map +1 -1
  89. package/dist/core/support/clock.d.ts +34 -0
  90. package/dist/core/support/clock.js +34 -0
  91. package/dist/core/support/clock.js.map +1 -0
  92. package/dist/core/support/concurrency.d.ts +11 -0
  93. package/dist/core/support/concurrency.js +47 -0
  94. package/dist/core/support/concurrency.js.map +1 -0
  95. package/dist/core/support/deadline.d.ts +17 -0
  96. package/dist/core/support/deadline.js +45 -0
  97. package/dist/core/support/deadline.js.map +1 -0
  98. package/dist/core/support/filesystem.d.ts +19 -0
  99. package/dist/core/support/filesystem.js +8 -0
  100. package/dist/core/support/filesystem.js.map +1 -0
  101. package/dist/core/version.d.ts +1 -1
  102. package/dist/core/version.js +1 -1
  103. package/dist/core/workflow/jobkey.js +89 -35
  104. package/dist/core/workflow/jobkey.js.map +1 -1
  105. package/dist/core/workflow/matrix.d.ts +4 -2
  106. package/dist/core/workflow/matrix.js +90 -25
  107. package/dist/core/workflow/matrix.js.map +1 -1
  108. package/dist/core/workflow/parse.js +14 -11
  109. package/dist/core/workflow/parse.js.map +1 -1
  110. package/dist/dsh/host/plugin.d.ts +122 -5
  111. package/dist/dsh/host/plugin.js +199 -138
  112. package/dist/dsh/host/plugin.js.map +1 -1
  113. package/dist/shared/i18n/en.d.ts +14 -3
  114. package/dist/shared/i18n/en.js +14 -3
  115. package/dist/shared/i18n/en.js.map +1 -1
  116. package/dist/shared/i18n/es.js +14 -3
  117. package/dist/shared/i18n/es.js.map +1 -1
  118. package/dist/shared/i18n/fr.js +14 -3
  119. package/dist/shared/i18n/fr.js.map +1 -1
  120. package/dist/shared/i18n/hi.js +14 -3
  121. package/dist/shared/i18n/hi.js.map +1 -1
  122. package/dist/shared/i18n/ko.js +14 -3
  123. package/dist/shared/i18n/ko.js.map +1 -1
  124. package/dist/shared/i18n/zh-Hans.js +14 -3
  125. package/dist/shared/i18n/zh-Hans.js.map +1 -1
  126. package/package.json +15 -3
@@ -0,0 +1,1035 @@
1
+ /* Built by scripts/build-client.mjs. Source: src/client. */
2
+ window.__ModuleLoader__.load({
3
+ id: "dsh-ci-diet",
4
+ factory(require) {
5
+ var __factories = Object.create(null);
6
+ var __cache = Object.create(null);
7
+ function __define(name, factory) { __factories[name] = factory; }
8
+ function __load(name) {
9
+ if (__cache[name] !== undefined) return __cache[name].exports;
10
+ var module = { exports: {} };
11
+ __cache[name] = module;
12
+ function __local(id) {
13
+ if (id.charAt(0) !== ".") return require(id);
14
+ var relative = id.slice(2);
15
+ var suffix = ".js";
16
+ if (relative.length >= suffix.length && relative.slice(-suffix.length) === suffix) relative = relative.slice(0, -suffix.length);
17
+ return __load(relative);
18
+ }
19
+ __factories[name](module, module.exports, __local);
20
+ return module.exports;
21
+ }
22
+ __define("fields", function (module, exports, require) {
23
+ "use strict";
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.SETTINGS_GROUPS = exports.SETTINGS_FIELDS = exports.RULE_CHOICES = void 0;
26
+ exports.collectSettingsEdits = collectSettingsEdits;
27
+ /** Analysis rules a human can enable, in contract order. */
28
+ exports.RULE_CHOICES = ["CD001", "CD002", "CD003", "CD004", "CD005", "CD008"];
29
+ exports.SETTINGS_FIELDS = [
30
+ { key: "networkEnabled", group: "general", kind: "switch" },
31
+ { key: "stateDir", group: "general", kind: "text" },
32
+ { key: "rules", group: "rules", kind: "rules" },
33
+ { key: "historyLookbackDays", group: "history", kind: "number", min: 1, max: 30, integer: true },
34
+ { key: "historyMaxRuns", group: "history", kind: "number", min: 1, integer: true },
35
+ { key: "historyMaxRequests", group: "history", kind: "number", min: 1, integer: true },
36
+ { key: "historyConcurrentRequests", group: "history", kind: "number", min: 1, max: 4, integer: true },
37
+ { key: "historyRequestTimeoutMs", group: "history", kind: "number", min: 1, integer: true },
38
+ { key: "storageStoreMaxBytes", group: "storage", kind: "number", min: 0, integer: true },
39
+ { key: "storageWorkflowMaxBytes", group: "storage", kind: "number", min: 0, integer: true },
40
+ { key: "storageCacheMaxBytes", group: "storage", kind: "number", min: 0, integer: true },
41
+ { key: "storageCacheDays", group: "storage", kind: "number", min: 0, integer: true },
42
+ { key: "storageReportDays", group: "storage", kind: "number", min: 0, integer: true },
43
+ { key: "storagePlanDays", group: "storage", kind: "number", min: 0, integer: true },
44
+ { key: "billingEnabled", group: "billing", kind: "switch" },
45
+ { key: "billingIncludedMinutes", group: "billing", kind: "number", min: 0, integer: true },
46
+ { key: "billingAccountMinutesUsed", group: "billing", kind: "number", min: 0, integer: true },
47
+ ];
48
+ exports.SETTINGS_GROUPS = ["general", "rules", "history", "storage", "billing"];
49
+ /** Validate a staged draft exactly like the page and produce one atomic edit batch. */
50
+ function collectSettingsEdits(draft, message, resets = []) {
51
+ const ops = [];
52
+ const errors = {};
53
+ for (const field of exports.SETTINGS_FIELDS) {
54
+ const key = field.key;
55
+ // A reset stays in the draft so it is saved with the same pinned revision
56
+ // as every other edit instead of writing to the host immediately.
57
+ if (resets.includes(key)) {
58
+ ops.push({ op: "unset", path: [key] });
59
+ continue;
60
+ }
61
+ if (!Object.prototype.hasOwnProperty.call(draft, key))
62
+ continue;
63
+ const raw = draft[key];
64
+ if (field.kind === "rules") {
65
+ const selected = Array.isArray(raw) ? raw.filter((id) => typeof id === "string") : [];
66
+ const unknown = selected.filter((id) => !exports.RULE_CHOICES.includes(id));
67
+ if (unknown.length > 0) {
68
+ errors[key] = message("page.unknownRule", { rule: unknown[0] });
69
+ continue;
70
+ }
71
+ if (selected.length === 0) {
72
+ errors[key] = message("page.noRules");
73
+ continue;
74
+ }
75
+ ops.push({ op: "set", path: [key], value: exports.RULE_CHOICES.filter((id) => selected.includes(id)) });
76
+ continue;
77
+ }
78
+ if (field.kind === "switch") {
79
+ ops.push({ op: "set", path: [key], value: raw === true });
80
+ continue;
81
+ }
82
+ if (field.kind === "text") {
83
+ ops.push({ op: "set", path: [key], value: String(raw) });
84
+ continue;
85
+ }
86
+ const text = String(raw).trim();
87
+ const value = Number(text);
88
+ if (text === "" || !Number.isFinite(value)) {
89
+ errors[key] = message("page.invalidNumber");
90
+ continue;
91
+ }
92
+ if (field.integer === true && !Number.isInteger(value)) {
93
+ errors[key] = message("page.invalidInteger");
94
+ continue;
95
+ }
96
+ if (field.min !== undefined && value < field.min) {
97
+ errors[key] = message("page.tooSmall", { min: field.min });
98
+ continue;
99
+ }
100
+ if (field.max !== undefined && value > field.max) {
101
+ errors[key] = message("page.tooLarge", { max: field.max });
102
+ continue;
103
+ }
104
+ ops.push({ op: "set", path: [key], value });
105
+ }
106
+ return { ops, errors };
107
+ }
108
+
109
+ });
110
+
111
+ __define("i18n", function (module, exports, require) {
112
+ "use strict";
113
+ Object.defineProperty(exports, "__esModule", { value: true });
114
+ exports.CLIENT_LANGUAGES = exports.DICTIONARIES = exports.NS = void 0;
115
+ /** Client dictionaries for the CI Diet settings page (ADR-19: six languages). */
116
+ exports.NS = "dsh-ci-diet";
117
+ const EN = {
118
+ nav: "CI Diet",
119
+ "page.title": "CI Diet",
120
+ "page.intro": "Configure the analysis engine used by CI Diet's DSH tools.",
121
+ "page.scope": "These values pass the same contract validation and shipped defaults as ci-diet --config. The CLI keeps its own config file and flags.",
122
+ "page.applies": "Changes apply to the next tool call in this profile; no restart.",
123
+ "page.scopeLabel": "Scope",
124
+ "page.appliesLabel": "Applies",
125
+ "page.loading": "Loading settings…",
126
+ "page.unavailable": "These settings are not available for this installation. Use the plugin entry under Settings → Plugins instead.",
127
+ "page.readOnly": "This profile is read-only, so settings cannot be saved here.",
128
+ "page.save": "Save changes",
129
+ "page.saving": "Saving…",
130
+ "page.discard": "Discard changes",
131
+ "page.saved": "Saved. The next tool call uses these values.",
132
+ "page.conflict": "Another screen saved this configuration while you were editing. Discard your draft to load the new values.",
133
+ "page.overridden": "Changed",
134
+ "page.reset": "Reset",
135
+ "page.invalidNumber": "Enter a number.",
136
+ "page.invalidInteger": "Enter a whole number.",
137
+ "page.tooSmall": "Minimum is {min}.",
138
+ "page.noRules": "Keep at least one analysis rule enabled.",
139
+ "page.unknownRule": "Unknown analysis rule: {rule}.",
140
+ "group.general.title": "Connection",
141
+ "group.general.desc": "Network access and where CI Diet keeps its local state.",
142
+ "group.rules.title": "Analysis rules",
143
+ "group.rules.desc": "Rules used by ci_diet_scan and ci_diet_sync. At least one stays enabled. CD003 and CD005 are off by default (ADR-13).",
144
+ "group.history.title": "History collection",
145
+ "group.history.desc": "Caps applied by ci_diet_sync when it reads GitHub Actions runs, jobs, and files.",
146
+ "group.storage.title": "Storage",
147
+ "group.storage.desc": "Limits for the local JSON store. A size of 0 disables that cap.",
148
+ "group.billing.title": "Cost estimate",
149
+ "group.billing.desc": "Marginal-cost estimate only; actual billing is never queried. Included and used minutes come from your own account.",
150
+ "field.networkEnabled": "Allow network access",
151
+ "field.stateDir": "State directory",
152
+ "field.rules": "Enabled rules",
153
+ "field.ruleCD001": "CD001: concurrency and required checks",
154
+ "field.ruleCD002": "CD002: job timeout candidates",
155
+ "field.ruleCD003": "CD003: docs-only pull requests",
156
+ "field.ruleCD004": "CD004: matrix size review",
157
+ "field.ruleCD005": "CD005: job timeout evidence",
158
+ "field.ruleCD008": "CD008: artifact retention",
159
+ "field.historyLookbackDays": "Lookback (days)",
160
+ "field.historyMaxRuns": "Maximum runs",
161
+ "field.historyMaxRequests": "Maximum requests",
162
+ "field.historyConcurrentRequests": "Concurrent requests",
163
+ "field.historyRequestTimeoutMs": "Request timeout (ms)",
164
+ "field.storageStoreMaxBytes": "Store size (bytes)",
165
+ "field.storageWorkflowMaxBytes": "Workflow file size (bytes)",
166
+ "field.storageCacheMaxBytes": "Cache size (bytes)",
167
+ "field.storageCacheDays": "Cache retention (days)",
168
+ "field.storageReportDays": "Report retention (days)",
169
+ "field.storagePlanDays": "Plan retention (days)",
170
+ "field.billingEnabled": "Use an account allowance",
171
+ "field.billingIncludedMinutes": "Included minutes",
172
+ "field.billingAccountMinutesUsed": "Minutes used this cycle",
173
+ };
174
+ const KO = {
175
+ nav: "CI Diet",
176
+ "page.title": "CI Diet",
177
+ "page.intro": "CI Diet의 DSH 도구가 사용하는 분석 엔진을 설정합니다.",
178
+ "page.scope": "이 값은 ci-diet --config와 같은 계약 검증과 기본값을 사용합니다. CLI는 자체 설정 파일과 플래그를 따로 사용합니다.",
179
+ "page.applies": "변경 사항은 이 프로필의 다음 도구 호출에 적용되며 재시작이 필요 없습니다.",
180
+ "page.scopeLabel": "적용 범위",
181
+ "page.appliesLabel": "적용 시점",
182
+ "page.loading": "설정을 불러오는 중…",
183
+ "page.unavailable": "이 설치에서는 설정을 사용할 수 없습니다. 설정 → 플러그인의 플러그인 항목을 사용하세요.",
184
+ "page.readOnly": "이 프로필은 읽기 전용이라 설정을 저장할 수 없습니다.",
185
+ "page.save": "변경 저장",
186
+ "page.saving": "저장 중…",
187
+ "page.discard": "변경 취소",
188
+ "page.saved": "저장했습니다. 다음 도구 호출부터 적용됩니다.",
189
+ "page.conflict": "편집하는 동안 다른 화면에서 이 설정을 저장했습니다. 초안을 버리고 새 값을 불러오세요.",
190
+ "page.overridden": "변경됨",
191
+ "page.reset": "초기화",
192
+ "page.invalidNumber": "숫자를 입력하세요.",
193
+ "page.invalidInteger": "정수를 입력하세요.",
194
+ "page.tooSmall": "최솟값은 {min}입니다.",
195
+ "page.noRules": "분석 규칙을 하나 이상 켜 두세요.",
196
+ "page.unknownRule": "알 수 없는 분석 규칙: {rule}.",
197
+ "group.general.title": "연결",
198
+ "group.general.desc": "네트워크 접근과 CI Diet의 로컬 상태 저장 위치입니다.",
199
+ "group.rules.title": "분석 규칙",
200
+ "group.rules.desc": "ci_diet_scan과 ci_diet_sync가 사용하는 규칙입니다. 하나 이상은 켜져 있어야 합니다. CD003과 CD005는 기본으로 꺼져 있습니다(ADR-13).",
201
+ "group.history.title": "이력 수집",
202
+ "group.history.desc": "ci_diet_sync가 GitHub Actions 실행·작업·파일을 읽을 때 적용하는 상한입니다.",
203
+ "group.storage.title": "저장 공간",
204
+ "group.storage.desc": "로컬 JSON 저장소의 한도입니다. 크기 0은 해당 한도를 끕니다.",
205
+ "group.billing.title": "비용 추정",
206
+ "group.billing.desc": "한계 비용 추정에만 쓰며 실제 청구액은 조회하지 않습니다. 포함·사용 분은 사용자 계정 기준입니다.",
207
+ "field.networkEnabled": "네트워크 접근 허용",
208
+ "field.stateDir": "상태 디렉터리",
209
+ "field.rules": "사용할 규칙",
210
+ "field.ruleCD001": "CD001: 동시성과 필수 검사",
211
+ "field.ruleCD002": "CD002: 작업 타임아웃 후보",
212
+ "field.ruleCD003": "CD003: 문서만 바꾼 풀 리퀘스트",
213
+ "field.ruleCD004": "CD004: 매트릭스 크기 검토",
214
+ "field.ruleCD005": "CD005: 작업 타임아웃 근거",
215
+ "field.ruleCD008": "CD008: 아티팩트 보존 기간",
216
+ "field.historyLookbackDays": "조회 기간(일)",
217
+ "field.historyMaxRuns": "최대 실행 수",
218
+ "field.historyMaxRequests": "최대 요청 수",
219
+ "field.historyConcurrentRequests": "동시 요청 수",
220
+ "field.historyRequestTimeoutMs": "요청 타임아웃(ms)",
221
+ "field.storageStoreMaxBytes": "저장소 크기(바이트)",
222
+ "field.storageWorkflowMaxBytes": "워크플로 파일 크기(바이트)",
223
+ "field.storageCacheMaxBytes": "캐시 크기(바이트)",
224
+ "field.storageCacheDays": "캐시 보존(일)",
225
+ "field.storageReportDays": "보고서 보존(일)",
226
+ "field.storagePlanDays": "계획 보존(일)",
227
+ "field.billingEnabled": "계정 한도 사용",
228
+ "field.billingIncludedMinutes": "포함 분",
229
+ "field.billingAccountMinutesUsed": "이번 주기 사용 분",
230
+ };
231
+ const ZH = {
232
+ nav: "CI Diet",
233
+ "page.title": "CI Diet",
234
+ "page.intro": "配置 CI Diet 的 DSH 工具使用的分析引擎。",
235
+ "page.scope": "这些值与 ci-diet --config 使用相同的契约校验和内置默认值。CLI 仍使用自己的配置文件与参数。",
236
+ "page.applies": "更改会在本配置档案的下一次工具调用生效,无需重启。",
237
+ "page.scopeLabel": "适用范围",
238
+ "page.appliesLabel": "生效时机",
239
+ "page.loading": "正在加载设置…",
240
+ "page.unavailable": "此安装无法使用该设置页。请改用 设置 → 插件 中的插件条目。",
241
+ "page.readOnly": "此配置档案为只读,无法在此保存设置。",
242
+ "page.save": "保存更改",
243
+ "page.saving": "正在保存…",
244
+ "page.discard": "放弃更改",
245
+ "page.saved": "已保存。下一次工具调用将使用这些值。",
246
+ "page.conflict": "编辑期间另一个界面保存了此配置。请放弃草稿以加载新值。",
247
+ "page.overridden": "已更改",
248
+ "page.reset": "重置",
249
+ "page.invalidNumber": "请输入数字。",
250
+ "page.invalidInteger": "请输入整数。",
251
+ "page.tooSmall": "最小值为 {min}。",
252
+ "page.noRules": "请至少保留一条分析规则。",
253
+ "page.unknownRule": "未知的分析规则:{rule}。",
254
+ "group.general.title": "连接",
255
+ "group.general.desc": "网络访问以及 CI Diet 本地状态的存放位置。",
256
+ "group.rules.title": "分析规则",
257
+ "group.rules.desc": "ci_diet_scan 与 ci_diet_sync 使用的规则。至少保留一条启用。CD003 与 CD005 默认关闭(ADR-13)。",
258
+ "group.history.title": "历史收集",
259
+ "group.history.desc": "ci_diet_sync 读取 GitHub Actions 运行、作业与文件时应用的上限。",
260
+ "group.storage.title": "存储空间",
261
+ "group.storage.desc": "本地 JSON 存储的限制。大小为 0 表示不限制该项。",
262
+ "group.billing.title": "费用估算",
263
+ "group.billing.desc": "仅用于边际成本估算,不会查询实际账单。包含分钟数与已用分钟数来自你的账户。",
264
+ "field.networkEnabled": "允许网络访问",
265
+ "field.stateDir": "状态目录",
266
+ "field.rules": "启用的规则",
267
+ "field.ruleCD001": "CD001:并发与必需检查",
268
+ "field.ruleCD002": "CD002:作业超时候选",
269
+ "field.ruleCD003": "CD003:仅文档变更的拉取请求",
270
+ "field.ruleCD004": "CD004:矩阵规模审查",
271
+ "field.ruleCD005": "CD005:作业超时证据",
272
+ "field.ruleCD008": "CD008:构件保留期限",
273
+ "field.historyLookbackDays": "回溯天数",
274
+ "field.historyMaxRuns": "最大运行数",
275
+ "field.historyMaxRequests": "最大请求数",
276
+ "field.historyConcurrentRequests": "并发请求数",
277
+ "field.historyRequestTimeoutMs": "请求超时(毫秒)",
278
+ "field.storageStoreMaxBytes": "存储大小(字节)",
279
+ "field.storageWorkflowMaxBytes": "工作流文件大小(字节)",
280
+ "field.storageCacheMaxBytes": "缓存大小(字节)",
281
+ "field.storageCacheDays": "缓存保留(天)",
282
+ "field.storageReportDays": "报告保留(天)",
283
+ "field.storagePlanDays": "计划保留(天)",
284
+ "field.billingEnabled": "使用账户额度",
285
+ "field.billingIncludedMinutes": "包含分钟数",
286
+ "field.billingAccountMinutesUsed": "本周期已用分钟数",
287
+ };
288
+ const HI = {
289
+ nav: "CI Diet",
290
+ "page.title": "CI Diet",
291
+ "page.intro": "CI Diet के DSH टूल्स द्वारा उपयोग किए जाने वाले विश्लेषण इंजन को कॉन्फ़िगर करें।",
292
+ "page.scope": "ये मान ci-diet --config जैसी ही अनुबंध-सत्यता और डिफ़ॉल्ट का उपयोग करते हैं। CLI अपनी अलग कॉन्फ़िग फ़ाइल और फ़्लैग रखता है।",
293
+ "page.applies": "बदलाव इस प्रोफ़ाइल में अगली टूल कॉल पर लागू होते हैं; पुनः आरंभ की आवश्यकता नहीं।",
294
+ "page.scopeLabel": "दायरा",
295
+ "page.appliesLabel": "कब लागू होगा",
296
+ "page.loading": "सेटिंग्स लोड हो रही हैं…",
297
+ "page.unavailable": "इस इंस्टॉलेशन के लिए ये सेटिंग्स उपलब्ध नहीं हैं। सेटिंग्स → प्लगइन में प्लगइन प्रविष्टि का उपयोग करें।",
298
+ "page.readOnly": "यह प्रोफ़ाइल केवल-पढ़ने योग्य है, इसलिए यहाँ सेटिंग्स सहेजी नहीं जा सकतीं।",
299
+ "page.save": "बदलाव सहेजें",
300
+ "page.saving": "सहेजा जा रहा है…",
301
+ "page.discard": "परिवर्तन छोड़ें",
302
+ "page.saved": "सहेजा गया। अगली टूल कॉल इन मानों का उपयोग करेगी।",
303
+ "page.conflict": "संपादन के दौरान किसी अन्य स्क्रीन ने यह कॉन्फ़िगरेशन सहेजा। नए मान लोड करने के लिए अपना ड्राफ़्ट छोड़ें।",
304
+ "page.overridden": "बदला गया",
305
+ "page.reset": "रीसेट",
306
+ "page.invalidNumber": "संख्या दर्ज करें।",
307
+ "page.invalidInteger": "पूर्णांक दर्ज करें।",
308
+ "page.tooSmall": "न्यूनतम {min} है।",
309
+ "page.noRules": "कम से कम एक विश्लेषण नियम सक्षम रखें।",
310
+ "page.unknownRule": "अज्ञात विश्लेषण नियम: {rule}।",
311
+ "group.general.title": "कनेक्शन",
312
+ "group.general.desc": "नेटवर्क पहुँच और CI Diet अपना स्थानीय स्टेट कहाँ रखता है।",
313
+ "group.rules.title": "विश्लेषण नियम",
314
+ "group.rules.desc": "ci_diet_scan और ci_diet_sync द्वारा उपयोग किए जाने वाले नियम। कम से कम एक सक्षम रहना चाहिए। CD003 और CD005 डिफ़ॉल्ट रूप से बंद हैं (ADR-13)।",
315
+ "group.history.title": "इतिहास संग्रह",
316
+ "group.history.desc": "ci_diet_sync द्वारा GitHub Actions रन, जॉब और फ़ाइलें पढ़ते समय लागू सीमाएँ।",
317
+ "group.storage.title": "संग्रहण",
318
+ "group.storage.desc": "स्थानीय JSON स्टोर की सीमाएँ। आकार 0 उस सीमा को बंद करता है।",
319
+ "group.billing.title": "लागत अनुमान",
320
+ "group.billing.desc": "केवल सीमांत लागत अनुमान; वास्तविक बिल कभी नहीं पूछा जाता। शामिल और उपयोग किए गए मिनट आपके खाते से आते हैं।",
321
+ "field.networkEnabled": "नेटवर्क पहुँच की अनुमति दें",
322
+ "field.stateDir": "स्थिति निर्देशिका",
323
+ "field.rules": "सक्षम नियम",
324
+ "field.ruleCD001": "CD001: समवर्तीता और आवश्यक जाँचें",
325
+ "field.ruleCD002": "CD002: जॉब टाइमआउट उम्मीदवार",
326
+ "field.ruleCD003": "CD003: केवल दस्तावेज़ वाले पुल अनुरोध",
327
+ "field.ruleCD004": "CD004: मैट्रिक्स आकार समीक्षा",
328
+ "field.ruleCD005": "CD005: जॉब टाइमआउट प्रमाण",
329
+ "field.ruleCD008": "CD008: आर्टिफ़ैक्ट प्रतिधारण",
330
+ "field.historyLookbackDays": "पीछे देखने के दिन",
331
+ "field.historyMaxRuns": "अधिकतम रन",
332
+ "field.historyMaxRequests": "अधिकतम अनुरोध",
333
+ "field.historyConcurrentRequests": "समवर्ती अनुरोध",
334
+ "field.historyRequestTimeoutMs": "अनुरोध टाइमआउट (ms)",
335
+ "field.storageStoreMaxBytes": "स्टोर आकार (बाइट)",
336
+ "field.storageWorkflowMaxBytes": "वर्कफ़्लो फ़ाइल आकार (बाइट)",
337
+ "field.storageCacheMaxBytes": "कैश आकार (बाइट)",
338
+ "field.storageCacheDays": "कैश प्रतिधारण (दिन)",
339
+ "field.storageReportDays": "रिपोर्ट प्रतिधारण (दिन)",
340
+ "field.storagePlanDays": "योजना प्रतिधारण (दिन)",
341
+ "field.billingEnabled": "खाता भत्ता उपयोग करें",
342
+ "field.billingIncludedMinutes": "शामिल मिनट",
343
+ "field.billingAccountMinutesUsed": "इस चक्र में उपयोग किए मिनट",
344
+ };
345
+ const ES = {
346
+ nav: "CI Diet",
347
+ "page.title": "CI Diet",
348
+ "page.intro": "Configura el motor de análisis que usan las herramientas DSH de CI Diet.",
349
+ "page.scope": "Estos valores pasan la misma validación del contrato y los mismos valores predeterminados que ci-diet --config. La CLI conserva su propio archivo y sus opciones.",
350
+ "page.applies": "Los cambios se aplican en la siguiente llamada a una herramienta de este perfil; no hace falta reiniciar.",
351
+ "page.scopeLabel": "Ámbito",
352
+ "page.appliesLabel": "Cuándo se aplica",
353
+ "page.loading": "Cargando ajustes…",
354
+ "page.unavailable": "Estos ajustes no están disponibles en esta instalación. Usa la entrada del plugin en Ajustes → Plugins.",
355
+ "page.readOnly": "Este perfil es de solo lectura, así que no se pueden guardar ajustes aquí.",
356
+ "page.save": "Guardar cambios",
357
+ "page.saving": "Guardando…",
358
+ "page.discard": "Descartar cambios",
359
+ "page.saved": "Guardado. La siguiente llamada a una herramienta usará estos valores.",
360
+ "page.conflict": "Otra pantalla guardó esta configuración mientras editabas. Descarta el borrador para cargar los valores nuevos.",
361
+ "page.overridden": "Cambiado",
362
+ "page.reset": "Restablecer",
363
+ "page.invalidNumber": "Introduce un número.",
364
+ "page.invalidInteger": "Introduce un número entero.",
365
+ "page.tooSmall": "El mínimo es {min}.",
366
+ "page.noRules": "Mantén al menos una regla de análisis activa.",
367
+ "page.unknownRule": "Regla de análisis desconocida: {rule}.",
368
+ "group.general.title": "Conexión",
369
+ "group.general.desc": "Acceso a la red y dónde guarda CI Diet su estado local.",
370
+ "group.rules.title": "Reglas de análisis",
371
+ "group.rules.desc": "Reglas que usan ci_diet_scan y ci_diet_sync. Al menos una debe quedar activa. CD003 y CD005 están desactivadas por defecto (ADR-13).",
372
+ "group.history.title": "Recopilación de historial",
373
+ "group.history.desc": "Límites que aplica ci_diet_sync al leer ejecuciones, trabajos y archivos de GitHub Actions.",
374
+ "group.storage.title": "Almacenamiento",
375
+ "group.storage.desc": "Límites del almacén JSON local. Un tamaño de 0 desactiva ese límite.",
376
+ "group.billing.title": "Estimación de coste",
377
+ "group.billing.desc": "Solo estimación de coste marginal; nunca se consulta la facturación real. Los minutos incluidos y usados provienen de tu cuenta.",
378
+ "field.networkEnabled": "Permitir acceso a la red",
379
+ "field.stateDir": "Directorio de estado",
380
+ "field.rules": "Reglas activas",
381
+ "field.ruleCD001": "CD001: concurrencia y comprobaciones obligatorias",
382
+ "field.ruleCD002": "CD002: candidatos de tiempo de espera",
383
+ "field.ruleCD003": "CD003: pull requests solo de documentación",
384
+ "field.ruleCD004": "CD004: revisión del tamaño de la matriz",
385
+ "field.ruleCD005": "CD005: evidencia de tiempo de espera",
386
+ "field.ruleCD008": "CD008: retención de artefactos",
387
+ "field.historyLookbackDays": "Días de retrospectiva",
388
+ "field.historyMaxRuns": "Ejecuciones máximas",
389
+ "field.historyMaxRequests": "Solicitudes máximas",
390
+ "field.historyConcurrentRequests": "Solicitudes simultáneas",
391
+ "field.historyRequestTimeoutMs": "Tiempo de espera (ms)",
392
+ "field.storageStoreMaxBytes": "Tamaño del almacén (bytes)",
393
+ "field.storageWorkflowMaxBytes": "Tamaño del flujo (bytes)",
394
+ "field.storageCacheMaxBytes": "Tamaño de caché (bytes)",
395
+ "field.storageCacheDays": "Retención de caché (días)",
396
+ "field.storageReportDays": "Retención de informes (días)",
397
+ "field.storagePlanDays": "Retención de planes (días)",
398
+ "field.billingEnabled": "Usar la franquicia de la cuenta",
399
+ "field.billingIncludedMinutes": "Minutos incluidos",
400
+ "field.billingAccountMinutesUsed": "Minutos usados en el ciclo",
401
+ };
402
+ const FR = {
403
+ nav: "CI Diet",
404
+ "page.title": "CI Diet",
405
+ "page.intro": "Configurez le moteur d’analyse utilisé par les outils DSH de CI Diet.",
406
+ "page.scope": "Ces valeurs passent la même validation de contrat et les mêmes valeurs par défaut que ci-diet --config. La CLI garde son propre fichier et ses options.",
407
+ "page.applies": "Les modifications s’appliquent au prochain appel d’outil de ce profil ; aucun redémarrage n’est nécessaire.",
408
+ "page.scopeLabel": "Portée",
409
+ "page.appliesLabel": "Application",
410
+ "page.loading": "Chargement des réglages…",
411
+ "page.unavailable": "Ces réglages ne sont pas disponibles pour cette installation. Utilisez l’entrée du plugin dans Réglages → Plugins.",
412
+ "page.readOnly": "Ce profil est en lecture seule ; les réglages ne peuvent pas être enregistrés ici.",
413
+ "page.save": "Enregistrer",
414
+ "page.saving": "Enregistrement…",
415
+ "page.discard": "Annuler les modifications",
416
+ "page.saved": "Enregistré. Le prochain appel d’outil utilisera ces valeurs.",
417
+ "page.conflict": "Un autre écran a enregistré cette configuration pendant votre modification. Abandonnez le brouillon pour charger les nouvelles valeurs.",
418
+ "page.overridden": "Modifié",
419
+ "page.reset": "Réinitialiser",
420
+ "page.invalidNumber": "Saisissez un nombre.",
421
+ "page.invalidInteger": "Saisissez un nombre entier.",
422
+ "page.tooSmall": "Le minimum est {min}.",
423
+ "page.noRules": "Gardez au moins une règle d’analyse active.",
424
+ "page.unknownRule": "Règle d’analyse inconnue : {rule}.",
425
+ "group.general.title": "Connexion",
426
+ "group.general.desc": "Accès réseau et emplacement de l’état local de CI Diet.",
427
+ "group.rules.title": "Règles d’analyse",
428
+ "group.rules.desc": "Règles utilisées par ci_diet_scan et ci_diet_sync. Au moins une doit rester active. CD003 et CD005 sont désactivées par défaut (ADR-13).",
429
+ "group.history.title": "Collecte de l’historique",
430
+ "group.history.desc": "Limites appliquées par ci_diet_sync lors de la lecture des exécutions, tâches et fichiers GitHub Actions.",
431
+ "group.storage.title": "Stockage",
432
+ "group.storage.desc": "Limites du stockage JSON local. Une taille de 0 désactive cette limite.",
433
+ "group.billing.title": "Estimation du coût",
434
+ "group.billing.desc": "Estimation du coût marginal uniquement ; la facturation réelle n’est jamais interrogée. Les minutes incluses et utilisées viennent de votre compte.",
435
+ "field.networkEnabled": "Autoriser l’accès réseau",
436
+ "field.stateDir": "Répertoire d’état",
437
+ "field.rules": "Règles actives",
438
+ "field.ruleCD001": "CD001 : concurrence et vérifications requises",
439
+ "field.ruleCD002": "CD002 : candidats de délai de tâche",
440
+ "field.ruleCD003": "CD003 : pull requests documentaires",
441
+ "field.ruleCD004": "CD004 : revue de taille de matrice",
442
+ "field.ruleCD005": "CD005 : preuve de délai de tâche",
443
+ "field.ruleCD008": "CD008 : rétention des artefacts",
444
+ "field.historyLookbackDays": "Jours de rétrospective",
445
+ "field.historyMaxRuns": "Exécutions maximales",
446
+ "field.historyMaxRequests": "Requêtes maximales",
447
+ "field.historyConcurrentRequests": "Requêtes simultanées",
448
+ "field.historyRequestTimeoutMs": "Délai de requête (ms)",
449
+ "field.storageStoreMaxBytes": "Taille du stockage (octets)",
450
+ "field.storageWorkflowMaxBytes": "Taille du fichier de workflow (octets)",
451
+ "field.storageCacheMaxBytes": "Taille du cache (octets)",
452
+ "field.storageCacheDays": "Rétention du cache (jours)",
453
+ "field.storageReportDays": "Rétention des rapports (jours)",
454
+ "field.storagePlanDays": "Rétention des plans (jours)",
455
+ "field.billingEnabled": "Utiliser la franchise du compte",
456
+ "field.billingIncludedMinutes": "Minutes incluses",
457
+ "field.billingAccountMinutesUsed": "Minutes utilisées ce cycle",
458
+ };
459
+ /** Simplified Chinese ships under the host id "zh" and the BCP 47 tag. */
460
+ exports.DICTIONARIES = {
461
+ en: EN,
462
+ ko: KO,
463
+ zh: ZH,
464
+ "zh-Hans": ZH,
465
+ hi: HI,
466
+ es: ES,
467
+ fr: FR,
468
+ };
469
+ /** Languages a client may contribute when the host does not ship them. */
470
+ exports.CLIENT_LANGUAGES = [
471
+ { id: "ko", label: "한국어" },
472
+ { id: "hi", label: "हिन्दी" },
473
+ { id: "es", label: "Español" },
474
+ { id: "fr", label: "Français" },
475
+ ];
476
+
477
+ });
478
+
479
+ __define("styles", function (module, exports, require) {
480
+ "use strict";
481
+ Object.defineProperty(exports, "__esModule", { value: true });
482
+ exports.CSS_TEXT = exports.STYLE_ATTRIBUTE = void 0;
483
+ exports.installStyles = installStyles;
484
+ /** Scoped styles for the CI Diet settings section.
485
+ *
486
+ * Every colour comes from the DSH theme aliases the built-in settings screens
487
+ * use, so light and dark themes follow the host instead of a local fallback.
488
+ * The page owns its scrolling: only the settings body scrolls, and the action
489
+ * bar sits below that body as a separate footer inside the settings panel.
490
+ */
491
+ exports.STYLE_ATTRIBUTE = "data-dsh-ci-diet-settings";
492
+ exports.CSS_TEXT = [
493
+ "/* Scoped styles for the CI Diet settings section.",
494
+ " * Every colour comes from the DSH theme aliases the built-in settings screens",
495
+ " * use, so light and dark themes follow the host instead of a local fallback.",
496
+ " * Only the settings body scrolls; the action bar is a separate footer below",
497
+ " * it, inside the settings panel (.VOzbGW_options). */",
498
+ ".ci-diet-page {",
499
+ " box-sizing: border-box;",
500
+ " display: flex;",
501
+ " flex-direction: column;",
502
+ " width: 100%;",
503
+ " max-width: 720px;",
504
+ " height: 100%;",
505
+ " min-height: 0;",
506
+ " color: var(--dsw-alias-label-primary);",
507
+ "}",
508
+ ".ci-diet-body {",
509
+ " display: flex;",
510
+ " flex-direction: column;",
511
+ " flex: 1 1 auto;",
512
+ " gap: 16px;",
513
+ " min-height: 0;",
514
+ " overflow-y: auto;",
515
+ " overscroll-behavior: contain;",
516
+ " padding-bottom: 16px;",
517
+ "}",
518
+ ".ci-diet-title {",
519
+ " margin: 0;",
520
+ " font-size: 18px;",
521
+ " font-weight: 600;",
522
+ " line-height: 1.4;",
523
+ "}",
524
+ ".ci-diet-intro {",
525
+ " margin: 0;",
526
+ " color: var(--dsw-alias-label-tertiary);",
527
+ " font-size: 13px;",
528
+ " line-height: 1.6;",
529
+ "}",
530
+ ".ci-diet-meta {",
531
+ " display: grid;",
532
+ " grid-template-columns: max-content minmax(0, 1fr);",
533
+ " gap: 6px 14px;",
534
+ " box-sizing: border-box;",
535
+ " padding: 12px 14px;",
536
+ " border: 0.5px solid var(--dsw-alias-settings-card-stroke);",
537
+ " border-radius: var(--dsw-radius-xl);",
538
+ " background: var(--dsw-alias-settings-card-fill);",
539
+ " font-size: 13px;",
540
+ " line-height: 1.55;",
541
+ "}",
542
+ ".ci-diet-meta-key {",
543
+ " color: var(--dsw-alias-label-tertiary);",
544
+ " white-space: nowrap;",
545
+ "}",
546
+ ".ci-diet-group {",
547
+ " display: flex;",
548
+ " flex-direction: column;",
549
+ " gap: 4px;",
550
+ " box-sizing: border-box;",
551
+ " padding: 14px 16px;",
552
+ " border: 0.5px solid var(--dsw-alias-settings-card-stroke);",
553
+ " border-radius: var(--dsw-radius-xl);",
554
+ " background: var(--dsw-alias-settings-card-fill);",
555
+ "}",
556
+ ".ci-diet-group-head {",
557
+ " display: flex;",
558
+ " flex-direction: column;",
559
+ " gap: 3px;",
560
+ " padding-bottom: 6px;",
561
+ "}",
562
+ ".ci-diet-group-title {",
563
+ " margin: 0;",
564
+ " font-size: 15px;",
565
+ " font-weight: 600;",
566
+ " line-height: 1.45;",
567
+ "}",
568
+ ".ci-diet-group-desc {",
569
+ " margin: 0;",
570
+ " color: var(--dsw-alias-label-secondary);",
571
+ " font-size: 12px;",
572
+ " line-height: 1.5;",
573
+ "}",
574
+ ".ci-diet-row {",
575
+ " display: flex;",
576
+ " flex-wrap: wrap;",
577
+ " align-items: center;",
578
+ " justify-content: space-between;",
579
+ " gap: 8px 14px;",
580
+ " padding: 10px 0;",
581
+ " border-top: 0.5px solid var(--dsw-alias-border-l2);",
582
+ "}",
583
+ ".ci-diet-row-text {",
584
+ " display: flex;",
585
+ " flex-direction: column;",
586
+ " gap: 3px;",
587
+ " flex: 1 1 180px;",
588
+ " min-width: 0;",
589
+ "}",
590
+ ".ci-diet-label {",
591
+ " font-size: 13px;",
592
+ " font-weight: 500;",
593
+ " line-height: 1.5;",
594
+ " overflow-wrap: anywhere;",
595
+ "}",
596
+ ".ci-diet-badge {",
597
+ " display: flex;",
598
+ " flex-wrap: wrap;",
599
+ " align-items: center;",
600
+ " gap: 6px;",
601
+ " color: var(--dsw-alias-label-tertiary);",
602
+ " font-size: 11px;",
603
+ " line-height: 1.5;",
604
+ "}",
605
+ ".ci-diet-reset {",
606
+ " padding: 0;",
607
+ " border: 0;",
608
+ " background: none;",
609
+ " color: var(--dsw-alias-label-secondary);",
610
+ " font: inherit;",
611
+ " font-size: 11px;",
612
+ " cursor: pointer;",
613
+ "}",
614
+ ".ci-diet-reset:hover:not(:disabled) {",
615
+ " color: var(--dsw-alias-label-primary);",
616
+ " text-decoration: underline;",
617
+ "}",
618
+ ".ci-diet-reset:disabled {",
619
+ " cursor: default;",
620
+ " opacity: 0.5;",
621
+ "}",
622
+ ".ci-diet-input {",
623
+ " box-sizing: border-box;",
624
+ " flex: 0 1 180px;",
625
+ " width: 180px;",
626
+ " min-width: 0;",
627
+ " max-width: 100%;",
628
+ " height: 34px;",
629
+ " padding: 0 12px;",
630
+ " border: 0.5px solid var(--dsw-alias-border-l4);",
631
+ " border-radius: var(--dsw-radius-md);",
632
+ " background: var(--dsw-alias-bg-layer-3);",
633
+ " color: var(--dsw-alias-label-primary);",
634
+ " font: inherit;",
635
+ " font-size: 13px;",
636
+ " line-height: 1.5;",
637
+ "}",
638
+ ".ci-diet-input:focus-visible {",
639
+ " outline: none;",
640
+ " border-color: var(--dsw-alias-state-business-primary);",
641
+ "}",
642
+ ".ci-diet-input:disabled {",
643
+ " color: var(--dsw-alias-label-tertiary);",
644
+ " cursor: default;",
645
+ "}",
646
+ ".ci-diet-error {",
647
+ " color: var(--dsw-alias-state-error-primary);",
648
+ " font-size: 11.5px;",
649
+ " line-height: 1.5;",
650
+ "}",
651
+ ".ci-diet-rules {",
652
+ " display: flex;",
653
+ " flex: 0 1 180px;",
654
+ " flex-direction: column;",
655
+ " align-items: flex-start;",
656
+ " gap: 5px;",
657
+ "}",
658
+ ".ci-diet-rule {",
659
+ " display: flex;",
660
+ " align-items: center;",
661
+ " gap: 6px;",
662
+ " font-size: 12.5px;",
663
+ " line-height: 1.45;",
664
+ " color: var(--dsw-alias-label-primary);",
665
+ "}",
666
+ ".ci-diet-checkbox {",
667
+ " accent-color: var(--dsw-alias-brand-primary);",
668
+ " margin: 0;",
669
+ "}",
670
+ ".ci-diet-switch {",
671
+ " position: relative;",
672
+ " flex: 0 0 auto;",
673
+ " box-sizing: border-box;",
674
+ " width: 36px;",
675
+ " height: 20px;",
676
+ " padding: 2px;",
677
+ " border: 0;",
678
+ " border-radius: 999px;",
679
+ " background: var(--dsw-alias-border-l3);",
680
+ " cursor: pointer;",
681
+ "}",
682
+ ".ci-diet-switch[aria-checked='true'] {",
683
+ " background: var(--dsw-alias-brand-primary);",
684
+ "}",
685
+ ".ci-diet-switch:disabled {",
686
+ " cursor: default;",
687
+ " opacity: 0.5;",
688
+ "}",
689
+ ".ci-diet-switch:focus-visible {",
690
+ " outline: var(--dsw-focus-ring-width) solid var(--dsw-alias-state-business-primary);",
691
+ " outline-offset: 2px;",
692
+ "}",
693
+ ".ci-diet-thumb {",
694
+ " display: block;",
695
+ " width: 16px;",
696
+ " height: 16px;",
697
+ " border-radius: 50%;",
698
+ " background: var(--dsw-alias-label-primary-foreground);",
699
+ " transition: transform 120ms ease;",
700
+ "}",
701
+ ".ci-diet-switch[aria-checked='false'] .ci-diet-thumb {",
702
+ " background: var(--dsw-alias-switch-thumb);",
703
+ "}",
704
+ ".ci-diet-switch[aria-checked='true'] .ci-diet-thumb {",
705
+ " transform: translateX(16px);",
706
+ "}",
707
+ ".ci-diet-actions {",
708
+ " flex: 0 0 auto;",
709
+ " display: flex;",
710
+ " flex-wrap: wrap;",
711
+ " align-items: center;",
712
+ " gap: 8px;",
713
+ " box-sizing: border-box;",
714
+ " padding: 12px 0 0;",
715
+ " border-top: 0.5px solid var(--dsw-alias-border-l2);",
716
+ " background: var(--dsw-alias-bg-layer-2);",
717
+ "}",
718
+ ".ci-diet-save {",
719
+ " box-sizing: border-box;",
720
+ " display: inline-flex;",
721
+ " align-items: center;",
722
+ " justify-content: center;",
723
+ " height: 36px;",
724
+ " padding: 0 14px;",
725
+ " border: 0;",
726
+ " border-radius: var(--dsw-radius-md);",
727
+ " background: var(--dsw-alias-button-primary-fill);",
728
+ " color: var(--dsw-alias-label-primary-foreground);",
729
+ " font: inherit;",
730
+ " font-size: 14px;",
731
+ " line-height: 22px;",
732
+ " cursor: pointer;",
733
+ "}",
734
+ ".ci-diet-save:hover:not(:disabled) {",
735
+ " background: var(--dsw-alias-button-primary-hover);",
736
+ "}",
737
+ ".ci-diet-save:disabled {",
738
+ " cursor: default;",
739
+ " opacity: 0.4;",
740
+ "}",
741
+ ".ci-diet-discard {",
742
+ " box-sizing: border-box;",
743
+ " display: inline-flex;",
744
+ " align-items: center;",
745
+ " justify-content: center;",
746
+ " height: 36px;",
747
+ " padding: 0 14px;",
748
+ " border: 0.5px solid var(--dsw-alias-border-l3);",
749
+ " border-radius: var(--dsw-radius-md);",
750
+ " background: transparent;",
751
+ " color: var(--dsw-alias-label-primary);",
752
+ " font: inherit;",
753
+ " font-size: 14px;",
754
+ " line-height: 22px;",
755
+ " cursor: pointer;",
756
+ "}",
757
+ ".ci-diet-discard:hover:not(:disabled) {",
758
+ " background: var(--dsw-alias-interactive-bg-hover);",
759
+ "}",
760
+ ".ci-diet-discard:disabled {",
761
+ " cursor: default;",
762
+ " opacity: 0.4;",
763
+ "}",
764
+ ".ci-diet-status {",
765
+ " flex: 1 1 180px;",
766
+ " min-width: 0;",
767
+ " color: var(--dsw-alias-label-tertiary);",
768
+ " font-size: 12px;",
769
+ " line-height: 1.5;",
770
+ " overflow-wrap: anywhere;",
771
+ "}",
772
+ ".ci-diet-notice {",
773
+ " box-sizing: border-box;",
774
+ " padding: 16px;",
775
+ " border: 0.5px solid var(--dsw-alias-settings-card-stroke);",
776
+ " border-radius: var(--dsw-radius-xl);",
777
+ " background: var(--dsw-alias-settings-card-fill);",
778
+ " color: var(--dsw-alias-label-secondary);",
779
+ " line-height: 1.55;",
780
+ "}",
781
+ "@media (max-width: 640px) {",
782
+ " .ci-diet-meta {",
783
+ " grid-template-columns: minmax(0, 1fr);",
784
+ " gap: 2px 0;",
785
+ " }",
786
+ " .ci-diet-meta-key:not(:first-child) {",
787
+ " margin-top: 6px;",
788
+ " }",
789
+ "}"
790
+ ].join("\n");
791
+ function currentDocument() {
792
+ return typeof document === "undefined" ? undefined : document;
793
+ }
794
+ /** Install the section stylesheet once; the returned cleanup removes our own tag. */
795
+ function installStyles(doc = currentDocument()) {
796
+ if (doc === undefined || doc.head === undefined)
797
+ return () => { };
798
+ const existing = doc.querySelector("style[" + exports.STYLE_ATTRIBUTE + "]");
799
+ if (existing !== null) {
800
+ if (existing.textContent !== exports.CSS_TEXT)
801
+ existing.textContent = exports.CSS_TEXT;
802
+ return () => { };
803
+ }
804
+ const style = doc.createElement("style");
805
+ style.setAttribute(exports.STYLE_ATTRIBUTE, "");
806
+ style.textContent = exports.CSS_TEXT;
807
+ doc.head.appendChild(style);
808
+ return () => { style.remove(); };
809
+ }
810
+
811
+ });
812
+
813
+ __define("index", function (module, exports, require) {
814
+ "use strict";
815
+ Object.defineProperty(exports, "__esModule", { value: true });
816
+ exports.SettingsPage = SettingsPage;
817
+ exports.translateWith = translateWith;
818
+ const React = require("react");
819
+ const fields_ts_1 = require("./fields.js");
820
+ const i18n_ts_1 = require("./i18n.js");
821
+ const styles_ts_1 = require("./styles.js");
822
+ const FORM_NAMESPACE = "ciDiet.bootstrap";
823
+ const EN = i18n_ts_1.DICTIONARIES.en;
824
+ function translateWith(dict) {
825
+ return (key, params = {}) => {
826
+ const template = dict[key] ?? EN[key] ?? key;
827
+ return template.replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
828
+ };
829
+ }
830
+ function isRecord(value) {
831
+ return value !== null && typeof value === "object" && !Array.isArray(value);
832
+ }
833
+ function isOverridden(user, key) {
834
+ return isRecord(user) && Object.prototype.hasOwnProperty.call(user, key);
835
+ }
836
+ function switchControl(label, checked, disabled, onToggle) {
837
+ return React.createElement("button", {
838
+ type: "button",
839
+ role: "switch",
840
+ "aria-checked": checked,
841
+ "aria-label": label,
842
+ disabled,
843
+ onClick: onToggle,
844
+ className: "ci-diet-switch",
845
+ }, React.createElement("span", { className: "ci-diet-thumb" }));
846
+ }
847
+ function rulesControl(selected, disabled, labelFor, onToggle) {
848
+ return React.createElement("div", { className: "ci-diet-rules" }, fields_ts_1.RULE_CHOICES.map((id) => React.createElement("label", { key: id, className: "ci-diet-rule" }, React.createElement("input", {
849
+ type: "checkbox",
850
+ checked: selected.includes(id),
851
+ disabled,
852
+ onChange: () => onToggle(id),
853
+ "aria-label": labelFor(id),
854
+ className: "ci-diet-checkbox",
855
+ }), labelFor(id))));
856
+ }
857
+ function SettingsPage(props) {
858
+ const { form } = props;
859
+ const t = props.t ?? translateWith(EN);
860
+ const emptySnapshot = { status: "loading", writable: false };
861
+ const snapshot = form
862
+ ? React.useSyncExternalStore((listener) => form.subscribe(listener), () => form.getSnapshot(), () => form.getSnapshot())
863
+ : emptySnapshot;
864
+ const [draft, setDraft] = React.useState({});
865
+ const [resets, setResets] = React.useState([]);
866
+ const [errors, setErrors] = React.useState({});
867
+ const [saving, setSaving] = React.useState(false);
868
+ const [saved, setSaved] = React.useState(false);
869
+ const [failed, setFailed] = React.useState(false);
870
+ const [pinnedRevision, setPinnedRevision] = React.useState(null);
871
+ const [conflict, setConflict] = React.useState(false);
872
+ const disabled = !snapshot.writable || saving;
873
+ const edit = (key, value) => {
874
+ setPinnedRevision((current) => current ?? snapshot.revision ?? null);
875
+ setResets((current) => current.filter((item) => item !== key));
876
+ setDraft((current) => ({ ...current, [key]: value }));
877
+ setErrors((current) => {
878
+ const next = { ...current };
879
+ delete next[key];
880
+ return next;
881
+ });
882
+ setSaved(false);
883
+ setFailed(false);
884
+ };
885
+ // Staging the reset instead of writing it now keeps the pinned revision
886
+ // meaningful: the reset and every staged field land in one mutation.
887
+ const reset = (key) => {
888
+ if (form === undefined || disabled)
889
+ return;
890
+ setPinnedRevision((current) => current ?? snapshot.revision ?? null);
891
+ setResets((current) => (current.includes(key) ? current : [...current, key]));
892
+ setDraft((current) => {
893
+ const next = { ...current };
894
+ delete next[key];
895
+ return next;
896
+ });
897
+ setErrors((current) => {
898
+ const next = { ...current };
899
+ delete next[key];
900
+ return next;
901
+ });
902
+ setSaved(false);
903
+ setFailed(false);
904
+ setConflict(false);
905
+ };
906
+ const save = async () => {
907
+ if (form === undefined || disabled)
908
+ return;
909
+ const batch = (0, fields_ts_1.collectSettingsEdits)(draft, t, resets);
910
+ setErrors({ ...batch.errors });
911
+ if (Object.keys(batch.errors).length > 0)
912
+ return;
913
+ const ops = batch.ops.map((op) => (op.op === "set"
914
+ ? { op: "set", path: op.path, value: op.value }
915
+ : { op: "unset", path: op.path }));
916
+ if (ops.length === 0)
917
+ return;
918
+ setSaving(true);
919
+ setFailed(false);
920
+ setConflict(false);
921
+ try {
922
+ const accepted = await form.mutate(ops, pinnedRevision ?? snapshot.revision);
923
+ if (accepted) {
924
+ setDraft({});
925
+ setResets([]);
926
+ setSaved(true);
927
+ setPinnedRevision(null);
928
+ }
929
+ else {
930
+ setFailed(true);
931
+ setConflict(pinnedRevision !== null && snapshot.revision !== pinnedRevision);
932
+ }
933
+ }
934
+ catch {
935
+ setFailed(true);
936
+ }
937
+ finally {
938
+ setSaving(false);
939
+ }
940
+ };
941
+ if (snapshot.status === "loading")
942
+ return React.createElement("div", { className: "ci-diet-notice" }, t("page.loading"));
943
+ if (snapshot.status === "unavailable" || form === undefined)
944
+ return React.createElement("div", { className: "ci-diet-notice" }, t("page.unavailable"));
945
+ const readOnly = !snapshot.writable;
946
+ const body = fields_ts_1.SETTINGS_GROUPS.map((group) => {
947
+ const rows = fields_ts_1.SETTINGS_FIELDS.filter((field) => field.group === group).map((field) => {
948
+ const raw = draft[field.key] !== undefined ? draft[field.key] : snapshot.value?.[field.key];
949
+ const overridden = isOverridden(snapshot.user, field.key);
950
+ const label = t("field." + field.key);
951
+ const selected = Array.isArray(raw) ? raw.filter((id) => typeof id === "string") : [];
952
+ const control = field.kind === "switch"
953
+ ? switchControl(label, raw === true, disabled || readOnly, () => edit(field.key, raw !== true))
954
+ : field.kind === "rules"
955
+ ? rulesControl(selected, disabled || readOnly, (id) => t("field.rule" + id), (id) => {
956
+ const next = selected.includes(id) ? selected.filter((entry) => entry !== id) : [...selected, id];
957
+ edit(field.key, fields_ts_1.RULE_CHOICES.filter((entry) => next.includes(entry)));
958
+ })
959
+ : React.createElement("input", {
960
+ id: "ci-diet-" + field.key,
961
+ type: field.kind === "number" ? "number" : "text",
962
+ value: raw === undefined || raw === null ? "" : String(raw),
963
+ min: field.min,
964
+ max: field.max,
965
+ step: field.kind === "number" ? 1 : undefined,
966
+ disabled: disabled || readOnly,
967
+ onChange: (event) => edit(field.key, event.target.value),
968
+ className: "ci-diet-input",
969
+ "aria-label": label,
970
+ });
971
+ return React.createElement("div", { key: field.key, className: "ci-diet-row" }, React.createElement("div", { className: "ci-diet-row-text" }, React.createElement("label", { htmlFor: "ci-diet-" + field.key, className: "ci-diet-label" }, label), overridden
972
+ ? React.createElement("span", { className: "ci-diet-badge" }, t("page.overridden"), " · ", React.createElement("button", {
973
+ type: "button",
974
+ onClick: () => { reset(field.key); },
975
+ disabled: disabled || readOnly,
976
+ className: "ci-diet-reset",
977
+ }, t("page.reset")))
978
+ : null, errors[field.key] ? React.createElement("span", { className: "ci-diet-error" }, errors[field.key]) : null), control);
979
+ });
980
+ return React.createElement("section", { key: group, className: "ci-diet-group" }, React.createElement("div", { className: "ci-diet-group-head" }, React.createElement("h3", { className: "ci-diet-group-title" }, t("group." + group + ".title")), React.createElement("p", { className: "ci-diet-group-desc" }, t("group." + group + ".desc"))), rows);
981
+ });
982
+ return React.createElement("div", { className: "ci-diet-page" }, React.createElement("div", { className: "ci-diet-body" }, React.createElement("h2", { className: "ci-diet-title" }, t("page.title")), React.createElement("p", { className: "ci-diet-intro" }, t("page.intro")), React.createElement("div", { className: "ci-diet-meta" }, React.createElement("div", { className: "ci-diet-meta-key" }, t("page.scopeLabel")), React.createElement("div", null, t("page.scope")), React.createElement("div", { className: "ci-diet-meta-key" }, t("page.appliesLabel")), React.createElement("div", null, t("page.applies"))), readOnly ? React.createElement("div", { className: "ci-diet-notice" }, t("page.readOnly")) : null, ...body), React.createElement("div", { className: "ci-diet-actions" }, React.createElement("button", { type: "button", className: "ci-diet-save", disabled: disabled || readOnly, onClick: () => { void save(); } }, saving ? t("page.saving") : t("page.save")), React.createElement("button", {
983
+ type: "button",
984
+ className: "ci-diet-discard",
985
+ disabled: disabled || readOnly || (Object.keys(draft).length === 0 && resets.length === 0),
986
+ onClick: () => { setDraft({}); setResets([]); setErrors({}); setSaved(false); setFailed(false); setConflict(false); setPinnedRevision(null); },
987
+ }, t("page.discard")), failed ? React.createElement("span", { className: "ci-diet-status", role: "alert" }, t(conflict ? "page.conflict" : "page.saveFailed")) : null, !failed && saved ? React.createElement("span", { className: "ci-diet-status", role: "status" }, t("page.saved")) : null));
988
+ }
989
+ function registerLanguages(locale) {
990
+ if (locale === undefined || locale.addLanguage === undefined || locale.getLocale === undefined)
991
+ return;
992
+ const known = new Set(locale.getLocale().locales.map((entry) => entry.id.toLowerCase()));
993
+ for (const language of i18n_ts_1.CLIENT_LANGUAGES) {
994
+ if (known.has(language.id))
995
+ continue;
996
+ locale.addLanguage({ id: language.id, label: language.label, fallback: "en" });
997
+ }
998
+ }
999
+ const plugin = {
1000
+ inject: ["slots", "locale", "configForms"],
1001
+ apply(ctx) {
1002
+ const t = ctx.locale === undefined ? translateWith(EN) : ctx.locale.bind(i18n_ts_1.NS);
1003
+ if (ctx.locale !== undefined && ctx.effect !== undefined) {
1004
+ ctx.effect(() => {
1005
+ const dispose = ctx.locale.register(i18n_ts_1.NS, i18n_ts_1.DICTIONARIES);
1006
+ return dispose;
1007
+ });
1008
+ }
1009
+ registerLanguages(ctx.locale);
1010
+ if (ctx.effect !== undefined)
1011
+ ctx.effect(() => (0, styles_ts_1.installStyles)());
1012
+ else
1013
+ (0, styles_ts_1.installStyles)();
1014
+ const register = () => ctx.slots.inject("settings.section", () => ctx.slots.register({
1015
+ name: "settings.section",
1016
+ id: "ci-diet",
1017
+ order: 91,
1018
+ label: () => t("nav"),
1019
+ locale: i18n_ts_1.NS,
1020
+ inject: () => ({ form: ctx.configForms.get(FORM_NAMESPACE) }),
1021
+ }, SettingsPage));
1022
+ if (ctx.effect !== undefined)
1023
+ ctx.effect(register);
1024
+ else
1025
+ register();
1026
+ },
1027
+ };
1028
+ exports.default = plugin;
1029
+
1030
+ });
1031
+
1032
+ var entry = __load("index");
1033
+ return entry.default !== undefined ? entry.default : entry;
1034
+ },
1035
+ });