@mrweicodes/dsh-permgate 1.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client.js ADDED
@@ -0,0 +1,1867 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-permgate",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let React = require("react");
8
+
9
+ const CSS = ".pg-modal { position: fixed; top: 16px; right: 16px; z-index: 9999; width: 420px; max-width: 92vw; max-height: 82vh; overflow-y: auto; background: var(--dsw-alias-bg-overlay, #ffffff); color: var(--dsw-alias-label-primary, #24292f); border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4)); border-radius: 10px; box-shadow: 0 10px 36px rgba(0,0,0,0.32); padding: 14px 16px; font-family: system-ui, -apple-system, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif; } .pg-modal-title { font-size: 14px; font-weight: 600; margin-bottom: 6px; } .pg-modal-req { font-size: 12px; margin-bottom: 4px; } .pg-modal-body { font-size: 12px; color: var(--dsw-alias-label-secondary, #57606a); margin-bottom: 8px; } .pg-intent { font-size: 12px; background: var(--dsw-alias-bg-layer-1, #f6f8fa); border: 1px solid var(--dsw-alias-border-l1, #d0d7de); border-left: 3px solid var(--dsw-alias-brand-primary, #1f6feb); border-radius: 6px; padding: 6px 8px; color: var(--dsw-alias-label-secondary, #57606a); margin-bottom: 6px; line-height: 1.5; word-break: break-word; } .pg-intent-tag { display: inline-block; font-weight: 700; color: var(--dsw-alias-brand-primary, #1f6feb); margin-right: 6px; font-size: 11px; } .pg-args { font-size: 11px; background: var(--dsw-alias-bg-layer-1, #f6f8fa); border: 1px solid var(--dsw-alias-border-l1, #d0d7de); border-left: 3px solid rgba(128,128,128,0.65); border-radius: 6px; padding: 5px 8px; color: var(--dsw-alias-label-secondary, #57606a); margin-bottom: 8px; line-height: 1.5; word-break: break-all; font-family: ui-monospace, Consolas, monospace; } .pg-args-tag { display: inline-block; font-weight: 700; color: rgba(128,128,128,0.95); margin-right: 6px; font-size: 11px; font-family: system-ui, -apple-system, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif; } .pg-args-row { display: flex; gap: 6px; padding: 1px 0; } .pg-args-label { font-weight: 600; flex: 0 0 auto; } .pg-args-val { word-break: break-all; flex: 1; } .pg-cand-head { font-size: 12px; font-weight: 600; margin: 6px 0 4px; } .pg-cand-hint { font-size: 11px; color: var(--dsw-alias-label-secondary, #8c959f); margin-bottom: 4px; } .pg-cand { display: flex; align-items: center; gap: 6px; padding: 4px 0; border-bottom: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,0.2)); } .pg-cand-label { flex: 1; font-family: ui-monospace, Consolas, monospace; font-size: 11px; word-break: break-all; } .pg-radio { display: inline-flex; align-items: center; gap: 3px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4)); background: transparent; color: var(--dsw-alias-label-secondary, #57606a); font-size: 11px; font-family: inherit; cursor: pointer; transition: all 0.15s ease; } .pg-radio:hover { border-color: var(--dsw-alias-brand-primary, #1f6feb); } .pg-radio-allow { border-color: var(--dsw-alias-state-success-primary, #1a7f37); color: var(--dsw-alias-state-success-primary, #1a7f37); background: rgba(26, 127, 55, 0.12); font-weight: 600; } .pg-radio-deny { border-color: var(--dsw-alias-state-error-primary, #cf222e); color: var(--dsw-alias-state-error-primary, #cf222e); background: rgba(207, 34, 46, 0.12); font-weight: 600; } .pg-footer { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; } .pg-action { display: inline-flex; align-items: center; padding: 5px 16px; border-radius: 6px; border: 1px solid; font-size: 12px; font-family: inherit; font-weight: 600; cursor: pointer; transition: filter 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease; } .pg-action:hover { filter: brightness(1.07); box-shadow: 0 2px 8px rgba(0,0,0,0.18); } .pg-action:active { transform: translateY(1px); box-shadow: none; } .pg-action:disabled { opacity: 0.5; cursor: not-allowed; filter: none; box-shadow: none; } .pg-action-allow { border-color: rgba(26, 127, 55, 0.45); color: #1a7f37; background: rgba(26, 127, 55, 0.12); } @supports (color: color-mix(in srgb, red 10%, blue)) { .pg-action-allow { border-color: color-mix(in srgb, var(--dsw-alias-state-success-primary, #1a7f37) 45%, transparent); color: var(--dsw-alias-state-success-primary, #1a7f37); background: color-mix(in srgb, var(--dsw-alias-state-success-primary, #1a7f37) 12%, transparent); } } .pg-action-deny { border-color: rgba(207, 34, 46, 0.45); color: #cf222e; background: rgba(207, 34, 46, 0.12); } @supports (color: color-mix(in srgb, red 10%, blue)) { .pg-action-deny { border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary, #cf222e) 45%, transparent); color: var(--dsw-alias-state-error-primary, #cf222e); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #cf222e) 12%, transparent); } } .pg-btn { padding: 4px 10px; border-radius: 6px; border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4)); background: transparent; color: var(--dsw-alias-label-primary, #24292f); cursor: pointer; font-size: 13px; font-family: inherit; transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease; } .pg-btn:hover { border-color: var(--dsw-alias-brand-primary, #1f6feb); background: rgba(31, 111, 235, 0.08); } .pg-btn:active { transform: translateY(1px); } .pg-btn:disabled { opacity: 0.5; cursor: not-allowed; } .pg-btn-danger:hover { border-color: var(--dsw-alias-state-error-primary, #cf222e); color: var(--dsw-alias-state-error-primary, #cf222e); background: rgba(207, 34, 46, 0.08); } .pg-btn-on { border-color: var(--dsw-alias-state-success-primary, #1a7f37); color: var(--dsw-alias-state-success-primary, #1a7f37); background: rgba(26, 127, 55, 0.12); font-weight: 600; } .pg-btn-on:hover { border-color: var(--dsw-alias-state-success-primary, #1a7f37); color: var(--dsw-alias-state-success-primary, #1a7f37); background: rgba(26, 127, 55, 0.2); } .pg-btn-off { border-color: var(--dsw-alias-state-error-primary, #cf222e); color: var(--dsw-alias-state-error-primary, #cf222e); background: rgba(207, 34, 46, 0.1); font-weight: 600; } .pg-btn-off:hover { border-color: var(--dsw-alias-state-error-primary, #cf222e); color: var(--dsw-alias-state-error-primary, #cf222e); background: rgba(207, 34, 46, 0.16); } .pg-btn-confirm { border-color: var(--dsw-alias-state-error-primary, #cf222e); color: #ffffff; background: var(--dsw-alias-state-error-primary, #cf222e); } .pg-btn-confirm:hover { border-color: var(--dsw-alias-state-error-primary, #cf222e); color: #ffffff; background: var(--dsw-alias-state-error-primary, #cf222e); filter: brightness(1.12); } .pg-field { padding: 4px 8px; border-radius: 6px; border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4)); background: var(--dsw-alias-bg-layer-1, #f6f8fa); color: var(--dsw-alias-label-primary, #24292f); color-scheme: light dark; font-size: 13px; font-family: inherit; transition: border-color 0.15s ease, box-shadow 0.15s ease; } .pg-field:hover { border-color: var(--dsw-alias-brand-primary, #1f6feb); } .pg-field:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #1f6feb); box-shadow: 0 0 0 2px rgba(31, 111, 235, 0.22); } .pg-field:disabled { opacity: 0.5; cursor: not-allowed; } .pg-field option { color: var(--dsw-alias-label-primary, #24292f); background: var(--dsw-alias-bg-overlay, #ffffff); } .pg-tab { padding: 6px 16px; border-radius: 6px 6px 0 0; border: none; border-bottom: 2px solid transparent; background: transparent; color: var(--dsw-alias-label-primary, #24292f); cursor: pointer; font-size: 13px; font-family: inherit; margin-right: 4px; transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease; } .pg-tab:hover { background: rgba(128, 128, 128, 0.12); } .pg-tab-on { border-bottom-color: var(--dsw-alias-brand-primary, #1f6feb); color: var(--dsw-alias-brand-primary, #1f6feb); font-weight: 600; }";
10
+
11
+ const DIFF_CSS = '.pg-link { display: inline-flex; align-items: center; gap: 4px; background: var(--dsw-alias-bg-layer-1, #f0f2f5); color: var(--dsw-alias-label-primary, #24292f); border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.45)); border-radius: 6px; padding: 3px 12px; cursor: pointer; font-size: 12px; font-weight: 500; margin-bottom: 6px; } .pg-link:hover { border-color: #1f6feb; color: #1f6feb; }';
12
+
13
+ // dsh-file-review 风格 unified diff + 右侧对比抽屉(浅/深色主题 token 跟随)
14
+ const DIFF2_CSS = '.pg2-block { position: relative; margin-top: 6px; margin-bottom: 8px; border: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,0.3)); border-radius: 8px; overflow: hidden; background: var(--dsw-alias-bg-layer-1, rgba(0,0,0,0.03)); } .pg2-header { display: flex; align-items: center; gap: 8px; min-height: 32px; padding: 0 8px; border-bottom: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,0.2)); font-family: ui-monospace, Consolas, monospace; font-size: 11px; } .pg2-status { color: var(--dsw-alias-state-success-primary, #1a7f37); font-weight: 700; } .pg2-status-read { color: var(--dsw-alias-brand-primary, #1f6feb); font-weight: 700; } .pg2-path { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; color: inherit; } .pg2-path:hover { color: var(--dsw-alias-brand-primary, #1f6feb); text-decoration: underline; } .pg2-added { margin-left: auto; color: var(--dsw-alias-state-success-primary, #1a7f37); } .pg2-removed { color: var(--dsw-alias-state-error-primary, #cf222e); } .pg2-copy { border: 0; background: transparent; color: var(--dsw-alias-label-secondary, #57606a); cursor: pointer; font-size: 11px; font-family: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; padding: 0 2px; margin-left: 6px; flex: 0 0 auto; } .pg2-copy:hover { color: var(--dsw-alias-brand-primary, #1f6feb); } .pg2-body { overflow: auto; max-height: 240px; font-family: ui-monospace, Consolas, monospace; font-size: 11px; line-height: 1.5; } .pg2-row { display: grid; grid-template-columns: 40px 40px 22px minmax(max-content, 1fr); min-width: max-content; white-space: pre; } .pg2-old, .pg2-new { padding: 0 6px; border-right: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,0.2)); color: var(--dsw-alias-label-tertiary, #8c959f); text-align: right; user-select: none; } .pg2-sign { text-align: center; user-select: none; } .pg2-text { padding-right: 12px; } .pg2-del { color: var(--dsw-alias-state-error-primary, #cf222e); background: rgba(207, 34, 46, 0.10); } .pg2-add { color: var(--dsw-alias-state-success-primary, #1a7f37); background: rgba(26, 127, 55, 0.10); } .pg2-ctx { color: var(--dsw-alias-label-primary, #24292f); } .pg2-gap { display: block; width: 100%; border: 0; border-top: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,0.2)); border-bottom: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,0.2)); background: var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.07)); color: var(--dsw-alias-label-secondary, #57606a); cursor: pointer; font-size: 11px; font-family: ui-monospace, Consolas, monospace; text-align: left; padding: 4px 8px 4px 104px; } .pg2-gap:hover { color: var(--dsw-alias-label-primary, #24292f); } .pg2-gap-more { padding: 4px 8px 4px 104px; color: var(--dsw-alias-label-secondary, #57606a); font-size: 11px; font-family: ui-monospace, Consolas, monospace; } .pg2-fadd { color: #2e7d32; } .pg2-fdel { color: #c62828; } .pg2-fctx { color: rgba(128,128,128,0.7); } .pg2-foot { padding: 4px 8px; border-top: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,0.2)); font-size: 11px; color: rgba(128,128,128,0.8); } .pg2-rrow { display: grid; grid-template-columns: 40px minmax(max-content, 1fr); min-width: max-content; white-space: pre; } .pg2-rnum { padding: 0 6px; border-right: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,0.2)); color: var(--dsw-alias-label-tertiary, #8c959f); text-align: right; user-select: none; } .pg2-load { padding: 10px 12px; font-size: 12px; color: var(--dsw-alias-label-secondary, #57606a); } .pg2-err { padding: 10px 12px; font-size: 12px; color: var(--dsw-alias-state-error-primary, #cf222e); } .pg-path-link { color: var(--dsw-alias-brand-primary, #1f6feb); cursor: pointer; word-break: break-all; flex: 1; } .pg-path-link:hover { text-decoration: underline; } .pg2-drawer { position: fixed; top: 0; right: 0; bottom: 0; left: auto; width: 42vw; min-width: 480px; max-width: 96vw; z-index: 10000; background: var(--dsw-alias-bg-overlay, #ffffff); color: var(--dsw-alias-label-primary, #24292f); border-left: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4)); box-shadow: -8px 0 28px rgba(0,0,0,0.25); display: flex; flex-direction: column; } .pg2-drawer-head { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,0.25)); min-height: 44px; } .pg2-drawer-file { font-family: ui-monospace, Consolas, monospace; font-size: 12px; word-break: break-all; flex: 1; min-width: 0; } .pg2-drawer-close { border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4)); background: transparent; color: var(--dsw-alias-label-secondary, #57606a); border-radius: 6px; width: 26px; height: 26px; cursor: pointer; font-size: 13px; line-height: 1; flex: 0 0 auto; } .pg2-drawer-close:hover { color: var(--dsw-alias-brand-primary, #1f6feb); border-color: var(--dsw-alias-brand-primary, #1f6feb); } .pg2-drawer-body { flex: 1; overflow: hidden; padding: 10px 12px; display: flex; flex-direction: column; min-height: 0; } .pg2-drawer-body .pg2-block { margin: 0; flex: 1; display: flex; flex-direction: column; min-height: 0; } .pg2-drawer-body .pg2-body { flex: 1; overflow: auto; max-height: none; min-height: 0; } .pg2-drawer-body .pg2-load, .pg2-drawer-body .pg2-err { flex: 1; display: flex; align-items: center; justify-content: center; } .pg2-drawer-resize { position: absolute; top: 0; bottom: 0; left: -4px; width: 8px; cursor: col-resize; z-index: 2; } .pg2-drawer-resize:hover, .pg2-drawer-resize.drag { background: rgba(31, 111, 235, 0.18); } .pg2-drawer-open { border: 1px solid var(--dsw-alias-brand-primary, #1f6feb); background: transparent; color: var(--dsw-alias-brand-primary, #1f6feb); border-radius: 6px; padding: 2px 10px; height: 26px; cursor: pointer; font-size: 12px; flex: 0 0 auto; } .pg2-drawer-open:hover { background: rgba(31, 111, 235, 0.10); } .pg2-edit-msg { font-size: 12px; color: var(--dsw-alias-label-secondary, #57606a); margin-right: 6px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pg2-edit-msg.err { color: var(--dsw-alias-state-error-primary, #cf222e); } .pg2-expand { border: 0; background: transparent; color: var(--dsw-alias-brand-primary, #1f6feb); cursor: pointer; font-size: 11px; font-family: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; padding: 0 2px; margin-left: 6px; flex: 0 0 auto; } .pg2-expand:hover { text-decoration: underline; } .pg2-romit { padding: 3px 8px 3px 48px; color: var(--dsw-alias-label-secondary, #57606a); font-size: 11px; font-family: ui-monospace, Consolas, monospace; font-style: italic; } .pg2-text .token.comment, .pg2-text .token.prolog, .pg2-text .token.doctype, .pg2-text .token.cdata { color: #8c959f; font-style: italic; } .pg2-text .token.punctuation { color: #57606a; } .pg2-text .token.property, .pg2-text .token.tag, .pg2-text .token.constant, .pg2-text .token.symbol, .pg2-text .token.deleted { color: #cf222e; } .pg2-text .token.boolean, .pg2-text .token.number { color: #9a6700; } .pg2-text .token.selector, .pg2-text .token.attr-name, .pg2-text .token.string, .pg2-text .token.char, .pg2-text .token.builtin, .pg2-text .token.inserted { color: #1a7f37; } .pg2-text .token.operator, .pg2-text .token.entity, .pg2-text .token.url { color: #8250df; } .pg2-text .token.atrule, .pg2-text .token.attr-value, .pg2-text .token.keyword { color: #1f6feb; } .pg2-text .token.function, .pg2-text .token.class-name { color: #8250df; } .pg2-text .token.regex, .pg2-text .token.important, .pg2-text .token.variable { color: #953800; }';
15
+
16
+ // ── Prism 语法高亮(vendored,prismjs 1.29.0,MIT © Lea Verou,https://prismjs.com)──
17
+ // PRISM_SRC 为 prism-core + 常用语言语法(components/prism-*.min.js)的拼接源码,
18
+ // 由构建脚本 JSON 转义后填入下方占位符。运行时代码经过 new Function 执行(非严格模式),
19
+ // 语言文件引用的 Prism 变量解析到同一函数作用域;CSP 等场景禁用时 PG_PRISM 为 null,
20
+ // 高亮静默降级为纯文本,不影响其余功能。
21
+ const PRISM_SRC = "var _self=\"undefined\"!=typeof window?window:\"undefined\"!=typeof WorkerGlobalScope\u0026\u0026self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\\s)lang(?:uage)?-([\\w-]+)(?=\\s|$)/i,t=0,r={},a={manual:e.Prism\u0026\u0026e.Prism.manual,disableWorkerMessageHandler:e.Prism\u0026\u0026e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/\u0026/g,\"\u0026amp;\").replace(/\u003c/g,\"\u0026lt;\").replace(/\\u00a0/g,\" \")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,\"__id\",{value:++t}),e.__id},clone:function e(n,t){var r,i;switch(t=t||{},a.util.type(n)){case\"Object\":if(i=a.util.objId(n),t[i])return t[i];for(var l in r={},t[i]=r,n)n.hasOwnProperty(l)\u0026\u0026(r[l]=e(n[l],t));return r;case\"Array\":return i=a.util.objId(n),t[i]?t[i]:(r=[],t[i]=r,n.forEach((function(n,a){r[a]=e(n,t)})),r);default:return n}},getLanguage:function(e){for(;e;){var t=n.exec(e.className);if(t)return t[1].toLowerCase();e=e.parentElement}return\"none\"},setLanguage:function(e,t){e.className=e.className.replace(RegExp(n,\"gi\"),\"\"),e.classList.add(\"language-\"+t)},currentScript:function(){if(\"undefined\"==typeof document)return null;if(\"currentScript\"in document)return document.currentScript;try{throw new Error}catch(r){var e=(/at [^(\\r\\n]*\\((.*):[^:]+:[^:]+\\)$/i.exec(r.stack)||[])[1];if(e){var n=document.getElementsByTagName(\"script\");for(var t in n)if(n[t].src==e)return n[t]}return null}},isActive:function(e,n,t){for(var r=\"no-\"+n;e;){var a=e.classList;if(a.contains(n))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!t}},languages:{plain:r,plaintext:r,text:r,txt:r,extend:function(e,n){var t=a.util.clone(a.languages[e]);for(var r in n)t[r]=n[r];return t},insertBefore:function(e,n,t,r){var i=(r=r||a.languages)[e],l={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var s in t)t.hasOwnProperty(s)\u0026\u0026(l[s]=t[s]);t.hasOwnProperty(o)||(l[o]=i[o])}var u=r[e];return r[e]=l,a.languages.DFS(a.languages,(function(n,t){t===u\u0026\u0026n!=e\u0026\u0026(this[n]=l)})),l},DFS:function e(n,t,r,i){i=i||{};var l=a.util.objId;for(var o in n)if(n.hasOwnProperty(o)){t.call(n,o,n[o],r||o);var s=n[o],u=a.util.type(s);\"Object\"!==u||i[l(s)]?\"Array\"!==u||i[l(s)]||(i[l(s)]=!0,e(s,t,o,i)):(i[l(s)]=!0,e(s,t,null,i))}}},plugins:{},highlightAll:function(e,n){a.highlightAllUnder(document,e,n)},highlightAllUnder:function(e,n,t){var r={callback:t,container:e,selector:\u0027code[class*=\"language-\"], [class*=\"language-\"] code, code[class*=\"lang-\"], [class*=\"lang-\"] code\u0027};a.hooks.run(\"before-highlightall\",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),a.hooks.run(\"before-all-elements-highlight\",r);for(var i,l=0;i=r.elements[l++];)a.highlightElement(i,!0===n,r.callback)},highlightElement:function(n,t,r){var i=a.util.getLanguage(n),l=a.languages[i];a.util.setLanguage(n,i);var o=n.parentElement;o\u0026\u0026\"pre\"===o.nodeName.toLowerCase()\u0026\u0026a.util.setLanguage(o,i);var s={element:n,language:i,grammar:l,code:n.textContent};function u(e){s.highlightedCode=e,a.hooks.run(\"before-insert\",s),s.element.innerHTML=s.highlightedCode,a.hooks.run(\"after-highlight\",s),a.hooks.run(\"complete\",s),r\u0026\u0026r.call(s.element)}if(a.hooks.run(\"before-sanity-check\",s),(o=s.element.parentElement)\u0026\u0026\"pre\"===o.nodeName.toLowerCase()\u0026\u0026!o.hasAttribute(\"tabindex\")\u0026\u0026o.setAttribute(\"tabindex\",\"0\"),!s.code)return a.hooks.run(\"complete\",s),void(r\u0026\u0026r.call(s.element));if(a.hooks.run(\"before-highlight\",s),s.grammar)if(t\u0026\u0026e.Worker){var c=new Worker(a.filename);c.onmessage=function(e){u(e.data)},c.postMessage(JSON.stringify({language:s.language,code:s.code,immediateClose:!0}))}else u(a.highlight(s.code,s.grammar,s.language));else u(a.util.encode(s.code))},highlight:function(e,n,t){var r={code:e,grammar:n,language:t};if(a.hooks.run(\"before-tokenize\",r),!r.grammar)throw new Error(\u0027The language \"\u0027+r.language+\u0027\" has no grammar.\u0027);return r.tokens=a.tokenize(r.code,r.grammar),a.hooks.run(\"after-tokenize\",r),i.stringify(a.util.encode(r.tokens),r.language)},tokenize:function(e,n){var t=n.rest;if(t){for(var r in t)n[r]=t[r];delete n.rest}var a=new s;return u(a,a.head,e),o(e,a,n,a.head,0),function(e){for(var n=[],t=e.head.next;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=a.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=a.hooks.all[e];if(t\u0026\u0026t.length)for(var r,i=0;r=t[i++];)r(n)}},Token:i};function i(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||\"\").length}function l(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a\u0026\u0026r\u0026\u0026a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function o(e,n,t,r,s,g){for(var f in t)if(t.hasOwnProperty(f)\u0026\u0026t[f]){var h=t[f];h=Array.isArray(h)?h:[h];for(var d=0;d\u003ch.length;++d){if(g\u0026\u0026g.cause==f+\",\"+d)return;var v=h[d],p=v.inside,m=!!v.lookbehind,y=!!v.greedy,k=v.alias;if(y\u0026\u0026!v.pattern.global){var x=v.pattern.toString().match(/[imsuy]*$/)[0];v.pattern=RegExp(v.pattern.source,x+\"g\")}for(var b=v.pattern||v,w=r.next,A=s;w!==n.tail\u0026\u0026!(g\u0026\u0026A\u003e=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length\u003ee.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index\u003e=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S\u003e=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail\u0026\u0026(j\u003cO||\"string\"==typeof C.value);C=C.next)L++,j+=C.value.length;L--,E=e.slice(A,j),P.index-=A}else if(!(P=l(b,0,E,m)))continue;S=P.index;var N=P[0],_=E.slice(0,S),M=E.slice(S+N.length),W=A+E.length;g\u0026\u0026W\u003eg.reach\u0026\u0026(g.reach=W);var z=w.prev;if(_\u0026\u0026(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M\u0026\u0026u(n,w,M),L\u003e1){var I={cause:f+\",\"+d,reach:W};o(e,n,t,w.prev,A,I),g\u0026\u0026I.reach\u003eg.reach\u0026\u0026(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a\u003ct\u0026\u0026r!==e.tail;a++)r=r.next;n.next=r,r.prev=n,e.length-=a}if(e.Prism=a,i.stringify=function e(n,t){if(\"string\"==typeof n)return n;if(Array.isArray(n)){var r=\"\";return n.forEach((function(n){r+=e(n,t)})),r}var i={type:n.type,content:e(n.content,t),tag:\"span\",classes:[\"token\",n.type],attributes:{},language:t},l=n.alias;l\u0026\u0026(Array.isArray(l)?Array.prototype.push.apply(i.classes,l):i.classes.push(l)),a.hooks.run(\"wrap\",i);var o=\"\";for(var s in i.attributes)o+=\" \"+s+\u0027=\"\u0027+(i.attributes[s]||\"\").replace(/\"/g,\"\u0026quot;\")+\u0027\"\u0027;return\"\u003c\"+i.tag+\u0027 class=\"\u0027+i.classes.join(\" \")+\u0027\"\u0027+o+\"\u003e\"+i.content+\"\u003c/\"+i.tag+\"\u003e\"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener(\"message\",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l\u0026\u0026e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g\u0026\u0026(a.filename=g.src,g.hasAttribute(\"data-manual\")\u0026\u0026(a.manual=!0)),!a.manual){var h=document.readyState;\"loading\"===h||\"interactive\"===h\u0026\u0026g\u0026\u0026g.defer?document.addEventListener(\"DOMContentLoaded\",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);\"undefined\"!=typeof module\u0026\u0026module.exports\u0026\u0026(module.exports=Prism),\"undefined\"!=typeof global\u0026\u0026(global.Prism=Prism);\nPrism.languages.markup={comment:{pattern:/\u003c!--(?:(?!\u003c!--)[\\s\\S])*?--\u003e/,greedy:!0},prolog:{pattern:/\u003c\\?[\\s\\S]+?\\?\u003e/,greedy:!0},doctype:{pattern:/\u003c!DOCTYPE(?:[^\u003e\"\u0027[\\]]|\"[^\"]*\"|\u0027[^\u0027]*\u0027)+(?:\\[(?:[^\u003c\"\u0027\\]]|\"[^\"]*\"|\u0027[^\u0027]*\u0027|\u003c(?!!--)|\u003c!--(?:[^-]|-(?!-\u003e))*--\u003e)*\\]\\s*)?\u003e/i,greedy:!0,inside:{\"internal-subset\":{pattern:/(^[^\\[]*\\[)[\\s\\S]+(?=\\]\u003e$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/\"[^\"]*\"|\u0027[^\u0027]*\u0027/,greedy:!0},punctuation:/^\u003c!|\u003e$|[[\\]]/,\"doctype-tag\":/^DOCTYPE/i,name:/[^\\s\u003c\u003e\u0027\"]+/}},cdata:{pattern:/\u003c!\\[CDATA\\[[\\s\\S]*?\\]\\]\u003e/i,greedy:!0},tag:{pattern:/\u003c\\/?(?!\\d)[^\\s\u003e\\/=$\u003c%]+(?:\\s(?:\\s*[^\\s\u003e\\/=]+(?:\\s*=\\s*(?:\"[^\"]*\"|\u0027[^\u0027]*\u0027|[^\\s\u0027\"\u003e=]+(?=[\\s\u003e]))|(?=[\\s/\u003e])))+)?\\s*\\/?\u003e/,greedy:!0,inside:{tag:{pattern:/^\u003c\\/?[^\\s\u003e\\/]+/,inside:{punctuation:/^\u003c\\/?/,namespace:/^[^\\s\u003e\\/:]+:/}},\"special-attr\":[],\"attr-value\":{pattern:/=\\s*(?:\"[^\"]*\"|\u0027[^\u0027]*\u0027|[^\\s\u0027\"\u003e=]+)/,inside:{punctuation:[{pattern:/^=/,alias:\"attr-equals\"},{pattern:/^(\\s*)[\"\u0027]|[\"\u0027]$/,lookbehind:!0}]}},punctuation:/\\/?\u003e/,\"attr-name\":{pattern:/[^\\s\u003e\\/]+/,inside:{namespace:/^[^\\s\u003e\\/:]+:/}}}},entity:[{pattern:/\u0026[\\da-z]{1,8};/i,alias:\"named-entity\"},/\u0026#x?[\\da-f]{1,8};/i]},Prism.languages.markup.tag.inside[\"attr-value\"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside[\"internal-subset\"].inside=Prism.languages.markup,Prism.hooks.add(\"wrap\",(function(a){\"entity\"===a.type\u0026\u0026(a.attributes.title=a.content.replace(/\u0026amp;/,\"\u0026\"))})),Object.defineProperty(Prism.languages.markup.tag,\"addInlined\",{value:function(a,e){var s={};s[\"language-\"+e]={pattern:/(^\u003c!\\[CDATA\\[)[\\s\\S]+?(?=\\]\\]\u003e$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^\u003c!\\[CDATA\\[|\\]\\]\u003e$/i;var t={\"included-cdata\":{pattern:/\u003c!\\[CDATA\\[[\\s\\S]*?\\]\\]\u003e/i,inside:s}};t[\"language-\"+e]={pattern:/[\\s\\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp(\"(\u003c__[^\u003e]*\u003e)(?:\u003c!\\\\[CDATA\\\\[(?:[^\\\\]]|\\\\](?!\\\\]\u003e))*\\\\]\\\\]\u003e|(?!\u003c!\\\\[CDATA\\\\[)[^])*?(?=\u003c/__\u003e)\".replace(/__/g,(function(){return a})),\"i\"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore(\"markup\",\"cdata\",n)}}),Object.defineProperty(Prism.languages.markup.tag,\"addAttribute\",{value:function(a,e){Prism.languages.markup.tag.inside[\"special-attr\"].push({pattern:RegExp(\"(^|[\\\"\u0027\\\\s])(?:\"+a+\")\\\\s*=\\\\s*(?:\\\"[^\\\"]*\\\"|\u0027[^\u0027]*\u0027|[^\\\\s\u0027\\\"\u003e=]+(?=[\\\\s\u003e]))\",\"i\"),lookbehind:!0,inside:{\"attr-name\":/^[^\\s=]+/,\"attr-value\":{pattern:/=[\\s\\S]+/,inside:{value:{pattern:/(^=\\s*([\"\u0027]|(?![\"\u0027])))\\S[\\s\\S]*(?=\\2$)/,lookbehind:!0,alias:[e,\"language-\"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:\"attr-equals\"},/\"|\u0027/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend(\"markup\",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;\n!function(s){var e=/(?:\"(?:\\\\(?:\\r\\n|[\\s\\S])|[^\"\\\\\\r\\n])*\"|\u0027(?:\\\\(?:\\r\\n|[\\s\\S])|[^\u0027\\\\\\r\\n])*\u0027)/;s.languages.css={comment:/\\/\\*[\\s\\S]*?\\*\\//,atrule:{pattern:RegExp(\"@[\\\\w-](?:[^;{\\\\s\\\"\u0027]|\\\\s+(?!\\\\s)|\"+e.source+\")*?(?:;|(?=\\\\s*\\\\{))\"),inside:{rule:/^@[\\w-]+/,\"selector-function-argument\":{pattern:/(\\bselector\\s*\\(\\s*(?![\\s)]))(?:[^()\\s]|\\s+(?![\\s)])|\\((?:[^()]|\\([^()]*\\))*\\))+(?=\\s*\\))/,lookbehind:!0,alias:\"selector\"},keyword:{pattern:/(^|[^\\w-])(?:and|not|only|or)(?![\\w-])/,lookbehind:!0}}},url:{pattern:RegExp(\"\\\\burl\\\\((?:\"+e.source+\"|(?:[^\\\\\\\\\\r\\n()\\\"\u0027]|\\\\\\\\[^])*)\\\\)\",\"i\"),greedy:!0,inside:{function:/^url/i,punctuation:/^\\(|\\)$/,string:{pattern:RegExp(\"^\"+e.source+\"$\"),alias:\"url\"}}},selector:{pattern:RegExp(\"(^|[{}\\\\s])[^{}\\\\s](?:[^{};\\\"\u0027\\\\s]|\\\\s+(?![\\\\s{])|\"+e.source+\")*(?=\\\\s*\\\\{)\"),lookbehind:!0},string:{pattern:e,greedy:!0},property:{pattern:/(^|[^-\\w\\xA0-\\uFFFF])(?!\\s)[-_a-z\\xA0-\\uFFFF](?:(?!\\s)[-\\w\\xA0-\\uFFFF])*(?=\\s*:)/i,lookbehind:!0},important:/!important\\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\\()/i,lookbehind:!0},punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t\u0026\u0026(t.tag.addInlined(\"style\",\"css\"),t.tag.addAttribute(\"style\",\"css\"))}(Prism);\nPrism.languages.clike={comment:[{pattern:/(^|[^\\\\])\\/\\*[\\s\\S]*?(?:\\*\\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\\\:])\\/\\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/([\"\u0027])(?:\\\\(?:\\r\\n|[\\s\\S])|(?!\\1)[^\\\\\\r\\n])*\\1/,greedy:!0},\"class-name\":{pattern:/(\\b(?:class|extends|implements|instanceof|interface|new|trait)\\s+|\\bcatch\\s+\\()[\\w.\\\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\\\]/}},keyword:/\\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\\b/,boolean:/\\b(?:false|true)\\b/,function:/\\b\\w+(?=\\()/,number:/\\b0x[\\da-f]+\\b|(?:\\b\\d+(?:\\.\\d*)?|\\B\\.\\d+)(?:e[+-]?\\d+)?/i,operator:/[\u003c\u003e]=?|[!=]=?=?|--?|\\+\\+?|\u0026\u0026?|\\|\\|?|[?*/~^%]/,punctuation:/[{}[\\];(),.:]/};\nPrism.languages.javascript=Prism.languages.extend(\"clike\",{\"class-name\":[Prism.languages.clike[\"class-name\"],{pattern:/(^|[^$\\w\\xA0-\\uFFFF])(?!\\s)[_$A-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\\})\\s*)catch\\b/,lookbehind:!0},{pattern:/(^|[^.]|\\.\\.\\.\\s*)\\b(?:as|assert(?=\\s*\\{)|async(?=\\s*(?:function\\b|\\(|[$\\w\\xA0-\\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\\s*(?:\\{|$))|for|from(?=\\s*(?:[\u0027\"]|$))|function|(?:get|set)(?=\\s*(?:[#\\[$\\w\\xA0-\\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\\b/,lookbehind:!0}],function:/#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*(?:\\.\\s*(?:apply|bind|call)\\s*)?\\()/,number:{pattern:RegExp(\"(^|[^\\\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\\\dA-Fa-f]+(?:_[\\\\dA-Fa-f]+)*n?|\\\\d+(?:_\\\\d+)*n|(?:\\\\d+(?:_\\\\d+)*(?:\\\\.(?:\\\\d+(?:_\\\\d+)*)?)?|\\\\.\\\\d+(?:_\\\\d+)*)(?:[Ee][+-]?\\\\d+(?:_\\\\d+)*)?)(?![\\\\w$])\"),lookbehind:!0},operator:/--|\\+\\+|\\*\\*=?|=\u003e|\u0026\u0026=?|\\|\\|=?|[!=]==|\u003c\u003c=?|\u003e\u003e\u003e?=?|[-+*/%\u0026|^!=\u003c\u003e]=?|\\.{3}|\\?\\?=?|\\?\\.?|[~:]/}),Prism.languages.javascript[\"class-name\"][0].pattern=/(\\b(?:class|extends|implements|instanceof|interface|new)\\s+)[\\w.\\\\]+/,Prism.languages.insertBefore(\"javascript\",\"keyword\",{regex:{pattern:RegExp(\"((?:^|[^$\\\\w\\\\xA0-\\\\uFFFF.\\\"\u0027\\\\])\\\\s]|\\\\b(?:return|yield))\\\\s*)/(?:(?:\\\\[(?:[^\\\\]\\\\\\\\\\r\\n]|\\\\\\\\.)*\\\\]|\\\\\\\\.|[^/\\\\\\\\\\\\[\\r\\n])+/[dgimyus]{0,7}|(?:\\\\[(?:[^[\\\\]\\\\\\\\\\r\\n]|\\\\\\\\.|\\\\[(?:[^[\\\\]\\\\\\\\\\r\\n]|\\\\\\\\.|\\\\[(?:[^[\\\\]\\\\\\\\\\r\\n]|\\\\\\\\.)*\\\\])*\\\\])*\\\\]|\\\\\\\\.|[^/\\\\\\\\\\\\[\\r\\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\\\s|/\\\\*(?:[^*]|\\\\*(?!/))*\\\\*/)*(?:$|[\\r\\n,.;:})\\\\]]|//))\"),lookbehind:!0,greedy:!0,inside:{\"regex-source\":{pattern:/^(\\/)[\\s\\S]+(?=\\/[a-z]*$)/,lookbehind:!0,alias:\"language-regex\",inside:Prism.languages.regex},\"regex-delimiter\":/^\\/|\\/$/,\"regex-flags\":/^[a-z]+$/}},\"function-variable\":{pattern:/#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*[=:]\\s*(?:async\\s*)?(?:\\bfunction\\b|(?:\\((?:[^()]|\\([^()]*\\))*\\)|(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*)\\s*=\u003e))/,alias:\"function\"},parameter:[{pattern:/(function(?:\\s+(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*)?\\s*\\(\\s*)(?!\\s)(?:[^()\\s]|\\s+(?![\\s)])|\\([^()]*\\))+(?=\\s*\\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\\w\\xA0-\\uFFFF])(?!\\s)[_$a-z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*=\u003e)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\\(\\s*)(?!\\s)(?:[^()\\s]|\\s+(?![\\s)])|\\([^()]*\\))+(?=\\s*\\)\\s*=\u003e)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\\b|\\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\\w\\xA0-\\uFFFF]))(?:(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*\\s*)\\(\\s*|\\]\\s*\\(\\s*)(?!\\s)(?:[^()\\s]|\\s+(?![\\s)])|\\([^()]*\\))+(?=\\s*\\)\\s*\\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\\b[A-Z](?:[A-Z_]|\\dx?)*\\b/}),Prism.languages.insertBefore(\"javascript\",\"string\",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:\"comment\"},\"template-string\":{pattern:/`(?:\\\\[\\s\\S]|\\$\\{(?:[^{}]|\\{(?:[^{}]|\\{[^}]*\\})*\\})+\\}|(?!\\$\\{)[^\\\\`])*`/,greedy:!0,inside:{\"template-punctuation\":{pattern:/^`|`$/,alias:\"string\"},interpolation:{pattern:/((?:^|[^\\\\])(?:\\\\{2})*)\\$\\{(?:[^{}]|\\{(?:[^{}]|\\{[^}]*\\})*\\})+\\}/,lookbehind:!0,inside:{\"interpolation-punctuation\":{pattern:/^\\$\\{|\\}$/,alias:\"punctuation\"},rest:Prism.languages.javascript}},string:/[\\s\\S]+/}},\"string-property\":{pattern:/((?:^|[,{])[ \\t]*)([\"\u0027])(?:\\\\(?:\\r\\n|[\\s\\S])|(?!\\2)[^\\\\\\r\\n])*\\2(?=\\s*:)/m,lookbehind:!0,greedy:!0,alias:\"property\"}}),Prism.languages.insertBefore(\"javascript\",\"operator\",{\"literal-property\":{pattern:/((?:^|[,{])[ \\t]*)(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*:)/m,lookbehind:!0,alias:\"property\"}}),Prism.languages.markup\u0026\u0026(Prism.languages.markup.tag.addInlined(\"script\",\"javascript\"),Prism.languages.markup.tag.addAttribute(\"on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)\",\"javascript\")),Prism.languages.js=Prism.languages.javascript;\n!function(e){e.languages.typescript=e.languages.extend(\"javascript\",{\"class-name\":{pattern:/(\\b(?:class|extends|implements|instanceof|interface|new|type)\\s+)(?!keyof\\b)(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?:\\s*\u003c(?:[^\u003c\u003e]|\u003c(?:[^\u003c\u003e]|\u003c[^\u003c\u003e]*\u003e)*\u003e)*\u003e)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\\b/}),e.languages.typescript.keyword.push(/\\b(?:abstract|declare|is|keyof|readonly|require)\\b/,/\\b(?:asserts|infer|interface|module|namespace|type)\\b(?=\\s*(?:[{_$a-zA-Z\\xA0-\\uFFFF]|$))/,/\\btype\\b(?=\\s*(?:[\\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript[\"literal-property\"];var s=e.languages.extend(\"typescript\",{});delete s[\"class-name\"],e.languages.typescript[\"class-name\"].inside=s,e.languages.insertBefore(\"typescript\",\"function\",{decorator:{pattern:/@[$\\w\\xA0-\\uFFFF]+/,inside:{at:{pattern:/^@/,alias:\"operator\"},function:/^[\\s\\S]+/}},\"generic-function\":{pattern:/#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*\\s*\u003c(?:[^\u003c\u003e]|\u003c(?:[^\u003c\u003e]|\u003c[^\u003c\u003e]*\u003e)*\u003e)*\u003e(?=\\s*\\()/,greedy:!0,inside:{function:/^#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*/,generic:{pattern:/\u003c[\\s\\S]+/,alias:\"class-name\",inside:s}}}}),e.languages.ts=e.languages.typescript}(Prism);\nPrism.languages.json={property:{pattern:/(^|[^\\\\])\"(?:\\\\.|[^\\\\\"\\r\\n])*\"(?=\\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\\\])\"(?:\\\\.|[^\\\\\"\\r\\n])*\"(?!\\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\\/\\/.*|\\/\\*[\\s\\S]*?(?:\\*\\/|$)/,greedy:!0},number:/-?\\b\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?\\b/i,punctuation:/[{}[\\],]/,operator:/:/,boolean:/\\b(?:false|true)\\b/,null:{pattern:/\\bnull\\b/,alias:\"keyword\"}},Prism.languages.webmanifest=Prism.languages.json;\n!function(e){var n=/[*\u0026][^\\s[\\]{},]+/,r=/!(?:\u003c[\\w\\-%#;/?:@\u0026=+$,.!~*\u0027()[\\]]+\u003e|(?:[a-zA-Z\\d-]*!)?[\\w\\-%#;/?:@\u0026=+$.~*\u0027()]+)?/,t=\"(?:\"+r.source+\"(?:[ \\t]+\"+n.source+\")?|\"+n.source+\"(?:[ \\t]+\"+r.source+\")?)\",a=\"(?:[^\\\\s\\\\x00-\\\\x08\\\\x0e-\\\\x1f!\\\"#%\u0026\u0027*,\\\\-:\u003e?@[\\\\]`{|}\\\\x7f-\\\\x84\\\\x86-\\\\x9f\\\\ud800-\\\\udfff\\\\ufffe\\\\uffff]|[?:-]\u003cPLAIN\u003e)(?:[ \\t]*(?:(?![#:])\u003cPLAIN\u003e|:\u003cPLAIN\u003e))*\".replace(/\u003cPLAIN\u003e/g,(function(){return\"[^\\\\s\\\\x00-\\\\x08\\\\x0e-\\\\x1f,[\\\\]{}\\\\x7f-\\\\x84\\\\x86-\\\\x9f\\\\ud800-\\\\udfff\\\\ufffe\\\\uffff]\"})),d=\"\\\"(?:[^\\\"\\\\\\\\\\r\\n]|\\\\\\\\.)*\\\"|\u0027(?:[^\u0027\\\\\\\\\\r\\n]|\\\\\\\\.)*\u0027\";function o(e,n){n=(n||\"\").replace(/m/g,\"\")+\"m\";var r=\"([:\\\\-,[{]\\\\s*(?:\\\\s\u003c\u003cprop\u003e\u003e[ \\t]+)?)(?:\u003c\u003cvalue\u003e\u003e)(?=[ \\t]*(?:$|,|\\\\]|\\\\}|(?:[\\r\\n]\\\\s*)?#))\".replace(/\u003c\u003cprop\u003e\u003e/g,(function(){return t})).replace(/\u003c\u003cvalue\u003e\u003e/g,(function(){return e}));return RegExp(r,n)}e.languages.yaml={scalar:{pattern:RegExp(\"([\\\\-:]\\\\s*(?:\\\\s\u003c\u003cprop\u003e\u003e[ \\t]+)?[|\u003e])[ \\t]*(?:((?:\\r?\\n|\\r)[ \\t]+)\\\\S[^\\r\\n]*(?:\\\\2[^\\r\\n]+)*)\".replace(/\u003c\u003cprop\u003e\u003e/g,(function(){return t}))),lookbehind:!0,alias:\"string\"},comment:/#.*/,key:{pattern:RegExp(\"((?:^|[:\\\\-,[{\\r\\n?])[ \\t]*(?:\u003c\u003cprop\u003e\u003e[ \\t]+)?)\u003c\u003ckey\u003e\u003e(?=\\\\s*:\\\\s)\".replace(/\u003c\u003cprop\u003e\u003e/g,(function(){return t})).replace(/\u003c\u003ckey\u003e\u003e/g,(function(){return\"(?:\"+a+\"|\"+d+\")\"}))),lookbehind:!0,greedy:!0,alias:\"atrule\"},directive:{pattern:/(^[ \\t]*)%.+/m,lookbehind:!0,alias:\"important\"},datetime:{pattern:o(\"\\\\d{4}-\\\\d\\\\d?-\\\\d\\\\d?(?:[tT]|[ \\t]+)\\\\d\\\\d?:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d*)?(?:[ \\t]*(?:Z|[-+]\\\\d\\\\d?(?::\\\\d{2})?))?|\\\\d{4}-\\\\d{2}-\\\\d{2}|\\\\d\\\\d?:\\\\d{2}(?::\\\\d{2}(?:\\\\.\\\\d*)?)?\"),lookbehind:!0,alias:\"number\"},boolean:{pattern:o(\"false|true\",\"i\"),lookbehind:!0,alias:\"important\"},null:{pattern:o(\"null|~\",\"i\"),lookbehind:!0,alias:\"important\"},string:{pattern:o(d),lookbehind:!0,greedy:!0},number:{pattern:o(\"[+-]?(?:0x[\\\\da-f]+|0o[0-7]+|(?:\\\\d+(?:\\\\.\\\\d*)?|\\\\.\\\\d+)(?:e[+-]?\\\\d+)?|\\\\.inf|\\\\.nan)\",\"i\"),lookbehind:!0},tag:r,important:n,punctuation:/---|[:[\\]{}\\-,|\u003e?]|\\.\\.\\./},e.languages.yml=e.languages.yaml}(Prism);\n!function(n){function e(n){return n=n.replace(/\u003cinner\u003e/g,(function(){return\"(?:\\\\\\\\.|[^\\\\\\\\\\n\\r]|(?:\\n|\\r\\n?)(?![\\r\\n]))\"})),RegExp(\"((?:^|[^\\\\\\\\])(?:\\\\\\\\{2})*)(?:\"+n+\")\")}var t=\"(?:\\\\\\\\.|``(?:[^`\\r\\n]|`(?!`))+``|`[^`\\r\\n]+`|[^\\\\\\\\|\\r\\n`])+\",a=\"\\\\|?__(?:\\\\|__)+\\\\|?(?:(?:\\n|\\r\\n?)|(?![^]))\".replace(/__/g,(function(){return t})),i=\"\\\\|?[ \\t]*:?-{3,}:?[ \\t]*(?:\\\\|[ \\t]*:?-{3,}:?[ \\t]*)+\\\\|?(?:\\n|\\r\\n?)\";n.languages.markdown=n.languages.extend(\"markup\",{}),n.languages.insertBefore(\"markdown\",\"prolog\",{\"front-matter-block\":{pattern:/(^(?:\\s*[\\r\\n])?)---(?!.)[\\s\\S]*?[\\r\\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,\"front-matter\":{pattern:/\\S+(?:\\s+\\S+)*/,alias:[\"yaml\",\"language-yaml\"],inside:n.languages.yaml}}},blockquote:{pattern:/^\u003e(?:[\\t ]*\u003e)*/m,alias:\"punctuation\"},table:{pattern:RegExp(\"^\"+a+i+\"(?:\"+a+\")*\",\"m\"),inside:{\"table-data-rows\":{pattern:RegExp(\"^(\"+a+i+\")(?:\"+a+\")*$\"),lookbehind:!0,inside:{\"table-data\":{pattern:RegExp(t),inside:n.languages.markdown},punctuation:/\\|/}},\"table-line\":{pattern:RegExp(\"^(\"+a+\")\"+i+\"$\"),lookbehind:!0,inside:{punctuation:/\\||:?-{3,}:?/}},\"table-header-row\":{pattern:RegExp(\"^\"+a+\"$\"),inside:{\"table-header\":{pattern:RegExp(t),alias:\"important\",inside:n.languages.markdown},punctuation:/\\|/}}}},code:[{pattern:/((?:^|\\n)[ \\t]*\\n|(?:^|\\r\\n?)[ \\t]*\\r\\n?)(?: {4}|\\t).+(?:(?:\\n|\\r\\n?)(?: {4}|\\t).+)*/,lookbehind:!0,alias:\"keyword\"},{pattern:/^```[\\s\\S]*?^```$/m,greedy:!0,inside:{\"code-block\":{pattern:/^(```.*(?:\\n|\\r\\n?))[\\s\\S]+?(?=(?:\\n|\\r\\n?)^```$)/m,lookbehind:!0},\"code-language\":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\\S.*(?:\\n|\\r\\n?)(?:==+|--+)(?=[ \\t]*$)/m,alias:\"important\",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\\s*)#.+/m,lookbehind:!0,alias:\"important\",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\\s*)([*-])(?:[\\t ]*\\2){2,}(?=\\s*$)/m,lookbehind:!0,alias:\"punctuation\"},list:{pattern:/(^\\s*)(?:[*+-]|\\d+\\.)(?=[\\t ].)/m,lookbehind:!0,alias:\"punctuation\"},\"url-reference\":{pattern:/!?\\[[^\\]]+\\]:[\\t ]+(?:\\S+|\u003c(?:\\\\.|[^\u003e\\\\])+\u003e)(?:[\\t ]+(?:\"(?:\\\\.|[^\"\\\\])*\"|\u0027(?:\\\\.|[^\u0027\\\\])*\u0027|\\((?:\\\\.|[^)\\\\])*\\)))?/,inside:{variable:{pattern:/^(!?\\[)[^\\]]+/,lookbehind:!0},string:/(?:\"(?:\\\\.|[^\"\\\\])*\"|\u0027(?:\\\\.|[^\u0027\\\\])*\u0027|\\((?:\\\\.|[^)\\\\])*\\))$/,punctuation:/^[\\[\\]!:]|[\u003c\u003e]/},alias:\"url\"},bold:{pattern:e(\"\\\\b__(?:(?!_)\u003cinner\u003e|_(?:(?!_)\u003cinner\u003e)+_)+__\\\\b|\\\\*\\\\*(?:(?!\\\\*)\u003cinner\u003e|\\\\*(?:(?!\\\\*)\u003cinner\u003e)+\\\\*)+\\\\*\\\\*\"),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\\s\\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\\*\\*|__/}},italic:{pattern:e(\"\\\\b_(?:(?!_)\u003cinner\u003e|__(?:(?!_)\u003cinner\u003e)+__)+_\\\\b|\\\\*(?:(?!\\\\*)\u003cinner\u003e|\\\\*\\\\*(?:(?!\\\\*)\u003cinner\u003e)+\\\\*\\\\*)+\\\\*\"),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\\s\\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:e(\"(~~?)(?:(?!~)\u003cinner\u003e)+\\\\2\"),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\\s\\S]+(?=\\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},\"code-snippet\":{pattern:/(^|[^\\\\`])(?:``[^`\\r\\n]+(?:`[^`\\r\\n]+)*``(?!`)|`[^`\\r\\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:[\"code\",\"keyword\"]},url:{pattern:e(\u0027!?\\\\[(?:(?!\\\\])\u003cinner\u003e)+\\\\](?:\\\\([^\\\\s)]+(?:[\\t ]+\"(?:\\\\\\\\.|[^\"\\\\\\\\])*\")?\\\\)|[ \\t]?\\\\[(?:(?!\\\\])\u003cinner\u003e)+\\\\])\u0027),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\\[)[^\\]]+(?=\\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\\][ \\t]?\\[)[^\\]]+(?=\\]$)/,lookbehind:!0},url:{pattern:/(^\\]\\()[^\\s)]+/,lookbehind:!0},string:{pattern:/(^[ \\t]+)\"(?:\\\\.|[^\"\\\\])*\"(?=\\)$)/,lookbehind:!0}}}}),[\"url\",\"bold\",\"italic\",\"strike\"].forEach((function(e){[\"url\",\"bold\",\"italic\",\"strike\",\"code-snippet\"].forEach((function(t){e!==t\u0026\u0026(n.languages.markdown[e].inside.content.inside[t]=n.languages.markdown[t])}))})),n.hooks.add(\"after-tokenize\",(function(n){\"markdown\"!==n.language\u0026\u0026\"md\"!==n.language||function n(e){if(e\u0026\u0026\"string\"!=typeof e)for(var t=0,a=e.length;t\u003ca;t++){var i=e[t];if(\"code\"===i.type){var r=i.content[1],o=i.content[3];if(r\u0026\u0026o\u0026\u0026\"code-language\"===r.type\u0026\u0026\"code-block\"===o.type\u0026\u0026\"string\"==typeof r.content){var l=r.content.replace(/\\b#/g,\"sharp\").replace(/\\b\\+\\+/g,\"pp\"),s=\"language-\"+(l=(/[a-z][\\w-]*/i.exec(l)||[\"\"])[0].toLowerCase());o.alias?\"string\"==typeof o.alias?o.alias=[o.alias,s]:o.alias.push(s):o.alias=[s]}}else n(i.content)}}(n.tokens)})),n.hooks.add(\"wrap\",(function(e){if(\"code-block\"===e.type){for(var t=\"\",a=0,i=e.classes.length;a\u003ci;a++){var s=e.classes[a],d=/language-(.+)/.exec(s);if(d){t=d[1];break}}var p=n.languages[t];if(p)e.content=n.highlight(e.content.replace(r,\"\").replace(/\u0026(\\w{1,8}|#x?[\\da-f]{1,8});/gi,(function(n,e){var t;return\"#\"===(e=e.toLowerCase())[0]?(t=\"x\"===e[1]?parseInt(e.slice(2),16):Number(e.slice(1)),l(t)):o[e]||n})),p,t);else if(t\u0026\u0026\"none\"!==t\u0026\u0026n.plugins.autoloader){var u=\"md-\"+(new Date).valueOf()+\"-\"+Math.floor(1e16*Math.random());e.attributes.id=u,n.plugins.autoloader.loadLanguages(t,(function(){var e=document.getElementById(u);e\u0026\u0026(e.innerHTML=n.highlight(e.textContent,n.languages[t],t))}))}}}));var r=RegExp(n.languages.markup.tag.pattern.source,\"gi\"),o={amp:\"\u0026\",lt:\"\u003c\",gt:\"\u003e\",quot:\u0027\"\u0027},l=String.fromCodePoint||String.fromCharCode;n.languages.md=n.languages.markdown}(Prism);\n!function(e){var t=\"\\\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\\\b\",a={pattern:/(^([\"\u0027]?)\\w+\\2)[ \\t]+\\S.*/,lookbehind:!0,alias:\"punctuation\",inside:null},n={bash:a,environment:{pattern:RegExp(\"\\\\$\"+t),alias:\"constant\"},variable:[{pattern:/\\$?\\(\\([\\s\\S]+?\\)\\)/,greedy:!0,inside:{variable:[{pattern:/(^\\$\\(\\([\\s\\S]+)\\)\\)/,lookbehind:!0},/^\\$\\(\\(/],number:/\\b0x[\\dA-Fa-f]+\\b|(?:\\b\\d+(?:\\.\\d*)?|\\B\\.\\d+)(?:[Ee]-?\\d+)?/,operator:/--|\\+\\+|\\*\\*=?|\u003c\u003c=?|\u003e\u003e=?|\u0026\u0026|\\|\\||[=!+\\-*/%\u003c\u003e^\u0026|]=?|[?~:]/,punctuation:/\\(\\(?|\\)\\)?|,|;/}},{pattern:/\\$\\((?:\\([^)]+\\)|[^()])+\\)|`[^`]+`/,greedy:!0,inside:{variable:/^\\$\\(|^`|\\)$|`$/}},{pattern:/\\$\\{[^}]+\\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\\/]|##?|%%?|\\^\\^?|,,?/,punctuation:/[\\[\\]]/,environment:{pattern:RegExp(\"(\\\\{)\"+t),lookbehind:!0,alias:\"constant\"}}},/\\$(?:\\w+|[#?*!@$])/],entity:/\\\\(?:[abceEfnrtv\\\\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\\s*\\/.*/,alias:\"important\"},comment:{pattern:/(^|[^\"{\\\\$])#.*/,lookbehind:!0},\"function-name\":[{pattern:/(\\bfunction\\s+)[\\w-]+(?=(?:\\s*\\(?:\\s*\\))?\\s*\\{)/,lookbehind:!0,alias:\"function\"},{pattern:/\\b[\\w-]+(?=\\s*\\(\\s*\\)\\s*\\{)/,alias:\"function\"}],\"for-or-select\":{pattern:/(\\b(?:for|select)\\s+)\\w+(?=\\s+in\\s)/,alias:\"variable\",lookbehind:!0},\"assign-left\":{pattern:/(^|[\\s;|\u0026]|[\u003c\u003e]\\()\\w+(?:\\.\\w+)*(?=\\+?=)/,inside:{environment:{pattern:RegExp(\"(^|[\\\\s;|\u0026]|[\u003c\u003e]\\\\()\"+t),lookbehind:!0,alias:\"constant\"}},alias:\"variable\",lookbehind:!0},parameter:{pattern:/(^|\\s)-{1,2}(?:\\w+:[+-]?)?\\w+(?:\\.\\w+)*(?=[=\\s]|$)/,alias:\"variable\",lookbehind:!0},string:[{pattern:/((?:^|[^\u003c])\u003c\u003c-?\\s*)(\\w+)\\s[\\s\\S]*?(?:\\r?\\n|\\r)\\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^\u003c])\u003c\u003c-?\\s*)([\"\u0027])(\\w+)\\2\\s[\\s\\S]*?(?:\\r?\\n|\\r)\\3/,lookbehind:!0,greedy:!0,inside:{bash:a}},{pattern:/(^|[^\\\\](?:\\\\\\\\)*)\"(?:\\\\[\\s\\S]|\\$\\([^)]+\\)|\\$(?!\\()|`[^`]+`|[^\"\\\\`$])*\"/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|[^$\\\\])\u0027[^\u0027]*\u0027/,lookbehind:!0,greedy:!0},{pattern:/\\$\u0027(?:[^\u0027\\\\]|\\\\[\\s\\S])*\u0027/,greedy:!0,inside:{entity:n.entity}}],environment:{pattern:RegExp(\"\\\\$?\"+t),alias:\"constant\"},variable:n.variable,function:{pattern:/(^|[\\s;|\u0026]|[\u003c\u003e]\\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\\s;|\u0026])/,lookbehind:!0},keyword:{pattern:/(^|[\\s;|\u0026]|[\u003c\u003e]\\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\\s;|\u0026])/,lookbehind:!0},builtin:{pattern:/(^|[\\s;|\u0026]|[\u003c\u003e]\\()(?:\\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\\s;|\u0026])/,lookbehind:!0,alias:\"class-name\"},boolean:{pattern:/(^|[\\s;|\u0026]|[\u003c\u003e]\\()(?:false|true)(?=$|[)\\s;|\u0026])/,lookbehind:!0},\"file-descriptor\":{pattern:/\\B\u0026\\d\\b/,alias:\"important\"},operator:{pattern:/\\d?\u003c\u003e|\u003e\\||\\+=|=[=~]?|!=?|\u003c\u003c[\u003c-]?|[\u0026\\d]?\u003e\u003e|\\d[\u003c\u003e]\u0026?|[\u003c\u003e][\u0026=]?|\u0026[\u003e\u0026]?|\\|[\u0026|]?/,inside:{\"file-descriptor\":{pattern:/^\\d/,alias:\"important\"}}},punctuation:/\\$?\\(\\(?|\\)\\)?|\\.\\.|[{}[\\];\\\\]/,number:{pattern:/(^|\\s)(?:[1-9]\\d*|0)(?:[.,]\\d+)?\\b/,lookbehind:!0}},a.inside=e.languages.bash;for(var s=[\"comment\",\"function-name\",\"for-or-select\",\"assign-left\",\"parameter\",\"string\",\"environment\",\"function\",\"keyword\",\"builtin\",\"boolean\",\"file-descriptor\",\"operator\",\"punctuation\",\"number\"],o=n.variable[1].inside,i=0;i\u003cs.length;i++)o[s[i]]=e.languages.bash[s[i]];e.languages.sh=e.languages.bash,e.languages.shell=e.languages.bash}(Prism);\n!function(e){var r=/%%?[~:\\w]+%?|!\\S+!/,t={pattern:/\\/[a-z?]+(?=[ :]|$):?|-[a-z]\\b|--[a-z-]+\\b/im,alias:\"attr-name\",inside:{punctuation:/:/}},n=/\"(?:[\\\\\"]\"|[^\"])*\"(?!\")/,i=/(?:\\b|-)\\d+\\b/;e.languages.batch={comment:[/^::.*/m,{pattern:/((?:^|[\u0026(])[ \\t]*)rem\\b(?:[^^\u0026)\\r\\n]|\\^(?:\\r\\n|[\\s\\S]))*/im,lookbehind:!0}],label:{pattern:/^:.*/m,alias:\"property\"},command:[{pattern:/((?:^|[\u0026(])[ \\t]*)for(?: \\/[a-z?](?:[ :](?:\"[^\"]*\"|[^\\s\"/]\\S*))?)* \\S+ in \\([^)]+\\) do/im,lookbehind:!0,inside:{keyword:/\\b(?:do|in)\\b|^for\\b/i,string:n,parameter:t,variable:r,number:i,punctuation:/[()\u0027,]/}},{pattern:/((?:^|[\u0026(])[ \\t]*)if(?: \\/[a-z?](?:[ :](?:\"[^\"]*\"|[^\\s\"/]\\S*))?)* (?:not )?(?:cmdextversion \\d+|defined \\w+|errorlevel \\d+|exist \\S+|(?:\"[^\"]*\"|(?!\")(?:(?!==)\\S)+)?(?:==| (?:equ|geq|gtr|leq|lss|neq) )(?:\"[^\"]*\"|[^\\s\"]\\S*))/im,lookbehind:!0,inside:{keyword:/\\b(?:cmdextversion|defined|errorlevel|exist|not)\\b|^if\\b/i,string:n,parameter:t,variable:r,number:i,operator:/\\^|==|\\b(?:equ|geq|gtr|leq|lss|neq)\\b/i}},{pattern:/((?:^|[\u0026()])[ \\t]*)else\\b/im,lookbehind:!0,inside:{keyword:/^else\\b/i}},{pattern:/((?:^|[\u0026(])[ \\t]*)set(?: \\/[a-z](?:[ :](?:\"[^\"]*\"|[^\\s\"/]\\S*))?)* (?:[^^\u0026)\\r\\n]|\\^(?:\\r\\n|[\\s\\S]))*/im,lookbehind:!0,inside:{keyword:/^set\\b/i,string:n,parameter:t,variable:[r,/\\w+(?=(?:[*\\/%+\\-\u0026^|]|\u003c\u003c|\u003e\u003e)?=)/],number:i,operator:/[*\\/%+\\-\u0026^|]=?|\u003c\u003c=?|\u003e\u003e=?|[!~_=]/,punctuation:/[()\u0027,]/}},{pattern:/((?:^|[\u0026(])[ \\t]*@?)\\w+\\b(?:\"(?:[\\\\\"]\"|[^\"])*\"(?!\")|[^\"^\u0026)\\r\\n]|\\^(?:\\r\\n|[\\s\\S]))*/m,lookbehind:!0,inside:{keyword:/^\\w+\\b/,string:n,parameter:t,label:{pattern:/(^\\s*):\\S+/m,lookbehind:!0,alias:\"property\"},variable:r,number:i,operator:/\\^/}}],operator:/[\u0026@]/,punctuation:/[()\u0027]/}}(Prism);\n!function(e){var i=e.languages.powershell={comment:[{pattern:/(^|[^`])\u003c#[\\s\\S]*?#\u003e/,lookbehind:!0},{pattern:/(^|[^`])#.*/,lookbehind:!0}],string:[{pattern:/\"(?:`[\\s\\S]|[^`\"])*\"/,greedy:!0,inside:null},{pattern:/\u0027(?:[^\u0027]|\u0027\u0027)*\u0027/,greedy:!0}],namespace:/\\[[a-z](?:\\[(?:\\[[^\\]]*\\]|[^\\[\\]])*\\]|[^\\[\\]])*\\]/i,boolean:/\\$(?:false|true)\\b/i,variable:/\\$\\w+\\b/,function:[/\\b(?:Add|Approve|Assert|Backup|Block|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy|Debug|Deny|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|ForEach|Format|Get|Grant|Group|Hide|Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push|Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke|Save|Search|Select|Send|Set|Show|Skip|Sort|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Tee|Test|Trace|Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Where|Write)-[a-z]+\\b/i,/\\b(?:ac|cat|chdir|clc|cli|clp|clv|compare|copy|cp|cpi|cpp|cvpa|dbp|del|diff|dir|ebp|echo|epal|epcsv|epsn|erase|fc|fl|ft|fw|gal|gbp|gc|gci|gcs|gdr|gi|gl|gm|gp|gps|group|gsv|gu|gv|gwmi|iex|ii|ipal|ipcsv|ipsn|irm|iwmi|iwr|kill|lp|ls|measure|mi|mount|move|mp|mv|nal|ndr|ni|nv|ogv|popd|ps|pushd|pwd|rbp|rd|rdr|ren|ri|rm|rmdir|rni|rnp|rp|rv|rvpa|rwmi|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls|sort|sp|spps|spsv|start|sv|swmi|tee|trcm|type|write)\\b/i],keyword:/\\b(?:Begin|Break|Catch|Class|Continue|Data|Define|Do|DynamicParam|Else|ElseIf|End|Exit|Filter|Finally|For|ForEach|From|Function|If|InlineScript|Parallel|Param|Process|Return|Sequence|Switch|Throw|Trap|Try|Until|Using|Var|While|Workflow)\\b/i,operator:{pattern:/(^|\\W)(?:!|-(?:b?(?:and|x?or)|as|(?:Not)?(?:Contains|In|Like|Match)|eq|ge|gt|is(?:Not)?|Join|le|lt|ne|not|Replace|sh[lr])\\b|-[-=]?|\\+[+=]?|[*\\/%]=?)/i,lookbehind:!0},punctuation:/[|{}[\\];(),.]/};i.string[0].inside={function:{pattern:/(^|[^`])\\$\\((?:\\$\\([^\\r\\n()]*\\)|(?!\\$\\()[^\\r\\n)])*\\)/,lookbehind:!0,inside:i},boolean:i.boolean,variable:i.variable}}(Prism);\nPrism.languages.c=Prism.languages.extend(\"clike\",{comment:{pattern:/\\/\\/(?:[^\\r\\n\\\\]|\\\\(?:\\r\\n?|\\n|(?![\\r\\n])))*|\\/\\*[\\s\\S]*?(?:\\*\\/|$)/,greedy:!0},string:{pattern:/\"(?:\\\\(?:\\r\\n|[\\s\\S])|[^\"\\\\\\r\\n])*\"/,greedy:!0},\"class-name\":{pattern:/(\\b(?:enum|struct)\\s+(?:__attribute__\\s*\\(\\([\\s\\S]*?\\)\\)\\s*)?)\\w+|\\b[a-z]\\w*_t\\b/,lookbehind:!0},keyword:/\\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\\b/,function:/\\b[a-z_]\\w*(?=\\s*\\()/i,number:/(?:\\b0x(?:[\\da-f]+(?:\\.[\\da-f]*)?|\\.[\\da-f]+)(?:p[+-]?\\d+)?|(?:\\b\\d+(?:\\.\\d*)?|\\B\\.\\d+)(?:e[+-]?\\d+)?)[ful]{0,4}/i,operator:/\u003e\u003e=?|\u003c\u003c=?|-\u003e|([-+\u0026|:])\\1|[?:~]|[-+*/%\u0026|^!=\u003c\u003e]=?/}),Prism.languages.insertBefore(\"c\",\"string\",{char:{pattern:/\u0027(?:\\\\(?:\\r\\n|[\\s\\S])|[^\u0027\\\\\\r\\n]){0,32}\u0027/,greedy:!0}}),Prism.languages.insertBefore(\"c\",\"string\",{macro:{pattern:/(^[\\t ]*)#\\s*[a-z](?:[^\\r\\n\\\\/]|\\/(?!\\*)|\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/|\\\\(?:\\r\\n|[\\s\\S]))*/im,lookbehind:!0,greedy:!0,alias:\"property\",inside:{string:[{pattern:/^(#\\s*include\\s*)\u003c[^\u003e]+\u003e/,lookbehind:!0},Prism.languages.c.string],char:Prism.languages.c.char,comment:Prism.languages.c.comment,\"macro-name\":[{pattern:/(^#\\s*define\\s+)\\w+\\b(?!\\()/i,lookbehind:!0},{pattern:/(^#\\s*define\\s+)\\w+\\b(?=\\()/i,lookbehind:!0,alias:\"function\"}],directive:{pattern:/^(#\\s*)[a-z]+/,lookbehind:!0,alias:\"keyword\"},\"directive-hash\":/^#/,punctuation:/##|\\\\(?=[\\r\\n])/,expression:{pattern:/\\S[\\s\\S]*/,inside:Prism.languages.c}}}}),Prism.languages.insertBefore(\"c\",\"function\",{constant:/\\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\\b/}),delete Prism.languages.c.boolean;\n!function(e){var t=/\\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\\b/,n=\"\\\\b(?!\u003ckeyword\u003e)\\\\w+(?:\\\\s*\\\\.\\\\s*\\\\w+)*\\\\b\".replace(/\u003ckeyword\u003e/g,(function(){return t.source}));e.languages.cpp=e.languages.extend(\"c\",{\"class-name\":[{pattern:RegExp(\"(\\\\b(?:class|concept|enum|struct|typename)\\\\s+)(?!\u003ckeyword\u003e)\\\\w+\".replace(/\u003ckeyword\u003e/g,(function(){return t.source}))),lookbehind:!0},/\\b[A-Z]\\w*(?=\\s*::\\s*\\w+\\s*\\()/,/\\b[A-Z_]\\w*(?=\\s*::\\s*~\\w+\\s*\\()/i,/\\b\\w+(?=\\s*\u003c(?:[^\u003c\u003e]|\u003c(?:[^\u003c\u003e]|\u003c[^\u003c\u003e]*\u003e)*\u003e)*\u003e\\s*::\\s*\\w+\\s*\\()/],keyword:t,number:{pattern:/(?:\\b0b[01\u0027]+|\\b0x(?:[\\da-f\u0027]+(?:\\.[\\da-f\u0027]*)?|\\.[\\da-f\u0027]+)(?:p[+-]?[\\d\u0027]+)?|(?:\\b[\\d\u0027]+(?:\\.[\\d\u0027]*)?|\\B\\.[\\d\u0027]+)(?:e[+-]?[\\d\u0027]+)?)[ful]{0,4}/i,greedy:!0},operator:/\u003e\u003e=?|\u003c\u003c=?|-\u003e|--|\\+\\+|\u0026\u0026|\\|\\||[?:~]|\u003c=\u003e|[-+*/%\u0026|^!=\u003c\u003e]=?|\\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\\b/,boolean:/\\b(?:false|true)\\b/}),e.languages.insertBefore(\"cpp\",\"string\",{module:{pattern:RegExp(\u0027(\\\\b(?:import|module)\\\\s+)(?:\"(?:\\\\\\\\(?:\\r\\n|[^])|[^\"\\\\\\\\\\r\\n])*\"|\u003c[^\u003c\u003e\\r\\n]*\u003e|\u0027+\"\u003cmod-name\u003e(?:\\\\s*:\\\\s*\u003cmod-name\u003e)?|:\\\\s*\u003cmod-name\u003e\".replace(/\u003cmod-name\u003e/g,(function(){return n}))+\")\"),lookbehind:!0,greedy:!0,inside:{string:/^[\u003c\"][\\s\\S]+/,operator:/:/,punctuation:/\\./}},\"raw-string\":{pattern:/R\"([^()\\\\ ]{0,16})\\([\\s\\S]*?\\)\\1\"/,alias:\"string\",greedy:!0}}),e.languages.insertBefore(\"cpp\",\"keyword\",{\"generic-function\":{pattern:/\\b(?!operator\\b)[a-z_]\\w*\\s*\u003c(?:[^\u003c\u003e]|\u003c[^\u003c\u003e]*\u003e)*\u003e(?=\\s*\\()/i,inside:{function:/^\\w+/,generic:{pattern:/\u003c[\\s\\S]+/,alias:\"class-name\",inside:e.languages.cpp}}}}),e.languages.insertBefore(\"cpp\",\"operator\",{\"double-colon\":{pattern:/::/,alias:\"punctuation\"}}),e.languages.insertBefore(\"cpp\",\"class-name\",{\"base-clause\":{pattern:/(\\b(?:class|struct)\\s+\\w+\\s*:\\s*)[^;{}\"\u0027\\s]+(?:\\s+[^;{}\"\u0027\\s]+)*(?=\\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend(\"cpp\",{})}}),e.languages.insertBefore(\"inside\",\"double-colon\",{\"class-name\":/\\b[a-z_]\\w*\\b(?!\\s*::)/i},e.languages.cpp[\"base-clause\"])}(Prism);\n!function(e){function n(e,n){return e.replace(/\u003c\u003c(\\d+)\u003e\u003e/g,(function(e,s){return\"(?:\"+n[+s]+\")\"}))}function s(e,s,a){return RegExp(n(e,s),a||\"\")}function a(e,n){for(var s=0;s\u003cn;s++)e=e.replace(/\u003c\u003cself\u003e\u003e/g,(function(){return\"(?:\"+e+\")\"}));return e.replace(/\u003c\u003cself\u003e\u003e/g,\"[^\\\\s\\\\S]\")}var t=\"bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void\",r=\"class enum interface record struct\",i=\"add alias and ascending async await by descending from(?=\\\\s*(?:\\\\w|$)) get global group into init(?=\\\\s*;) join let nameof not notnull on or orderby partial remove select set unmanaged value when where with(?=\\\\s*{)\",o=\"abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield\";function l(e){return\"\\\\b(?:\"+e.trim().replace(/ /g,\"|\")+\")\\\\b\"}var d=l(r),p=RegExp(l(t+\" \"+r+\" \"+i+\" \"+o)),c=l(r+\" \"+i+\" \"+o),u=l(t+\" \"+r+\" \"+o),g=a(\"\u003c(?:[^\u003c\u003e;=+\\\\-*/%\u0026|^]|\u003c\u003cself\u003e\u003e)*\u003e\",2),b=a(\"\\\\((?:[^()]|\u003c\u003cself\u003e\u003e)*\\\\)\",2),h=\"@?\\\\b[A-Za-z_]\\\\w*\\\\b\",f=n(\"\u003c\u003c0\u003e\u003e(?:\\\\s*\u003c\u003c1\u003e\u003e)?\",[h,g]),m=n(\"(?!\u003c\u003c0\u003e\u003e)\u003c\u003c1\u003e\u003e(?:\\\\s*\\\\.\\\\s*\u003c\u003c1\u003e\u003e)*\",[c,f]),k=\"\\\\[\\\\s*(?:,\\\\s*)*\\\\]\",y=n(\"\u003c\u003c0\u003e\u003e(?:\\\\s*(?:\\\\?\\\\s*)?\u003c\u003c1\u003e\u003e)*(?:\\\\s*\\\\?)?\",[m,k]),w=n(\"[^,()\u003c\u003e[\\\\];=+\\\\-*/%\u0026|^]|\u003c\u003c0\u003e\u003e|\u003c\u003c1\u003e\u003e|\u003c\u003c2\u003e\u003e\",[g,b,k]),v=n(\"\\\\(\u003c\u003c0\u003e\u003e+(?:,\u003c\u003c0\u003e\u003e+)+\\\\)\",[w]),x=n(\"(?:\u003c\u003c0\u003e\u003e|\u003c\u003c1\u003e\u003e)(?:\\\\s*(?:\\\\?\\\\s*)?\u003c\u003c2\u003e\u003e)*(?:\\\\s*\\\\?)?\",[v,m,k]),$={keyword:p,punctuation:/[\u003c\u003e()?,.:[\\]]/},_=\"\u0027(?:[^\\r\\n\u0027\\\\\\\\]|\\\\\\\\.|\\\\\\\\[Uux][\\\\da-fA-F]{1,8})\u0027\",B=\u0027\"(?:\\\\\\\\.|[^\\\\\\\\\"\\r\\n])*\"\u0027;e.languages.csharp=e.languages.extend(\"clike\",{string:[{pattern:s(\"(^|[^$\\\\\\\\])\u003c\u003c0\u003e\u003e\",[\u0027@\"(?:\"\"|\\\\\\\\[^]|[^\\\\\\\\\"])*\"(?!\")\u0027]),lookbehind:!0,greedy:!0},{pattern:s(\"(^|[^@$\\\\\\\\])\u003c\u003c0\u003e\u003e\",[B]),lookbehind:!0,greedy:!0}],\"class-name\":[{pattern:s(\"(\\\\busing\\\\s+static\\\\s+)\u003c\u003c0\u003e\u003e(?=\\\\s*;)\",[m]),lookbehind:!0,inside:$},{pattern:s(\"(\\\\busing\\\\s+\u003c\u003c0\u003e\u003e\\\\s*=\\\\s*)\u003c\u003c1\u003e\u003e(?=\\\\s*;)\",[h,x]),lookbehind:!0,inside:$},{pattern:s(\"(\\\\busing\\\\s+)\u003c\u003c0\u003e\u003e(?=\\\\s*=)\",[h]),lookbehind:!0},{pattern:s(\"(\\\\b\u003c\u003c0\u003e\u003e\\\\s+)\u003c\u003c1\u003e\u003e\",[d,f]),lookbehind:!0,inside:$},{pattern:s(\"(\\\\bcatch\\\\s*\\\\(\\\\s*)\u003c\u003c0\u003e\u003e\",[m]),lookbehind:!0,inside:$},{pattern:s(\"(\\\\bwhere\\\\s+)\u003c\u003c0\u003e\u003e\",[h]),lookbehind:!0},{pattern:s(\"(\\\\b(?:is(?:\\\\s+not)?|as)\\\\s+)\u003c\u003c0\u003e\u003e\",[y]),lookbehind:!0,inside:$},{pattern:s(\"\\\\b\u003c\u003c0\u003e\u003e(?=\\\\s+(?!\u003c\u003c1\u003e\u003e|with\\\\s*\\\\{)\u003c\u003c2\u003e\u003e(?:\\\\s*[=,;:{)\\\\]]|\\\\s+(?:in|when)\\\\b))\",[x,u,h]),inside:$}],keyword:p,number:/(?:\\b0(?:x[\\da-f_]*[\\da-f]|b[01_]*[01])|(?:\\B\\.\\d+(?:_+\\d+)*|\\b\\d+(?:_+\\d+)*(?:\\.\\d+(?:_+\\d+)*)?)(?:e[-+]?\\d+(?:_+\\d+)*)?)(?:[dflmu]|lu|ul)?\\b/i,operator:/\u003e\u003e=?|\u003c\u003c=?|[-=]\u003e|([-+\u0026|])\\1|~|\\?\\?=?|[-+*/%\u0026|^!=\u003c\u003e]=?/,punctuation:/\\?\\.?|::|[{}[\\];(),.:]/}),e.languages.insertBefore(\"csharp\",\"number\",{range:{pattern:/\\.\\./,alias:\"operator\"}}),e.languages.insertBefore(\"csharp\",\"punctuation\",{\"named-parameter\":{pattern:s(\"([(,]\\\\s*)\u003c\u003c0\u003e\u003e(?=\\\\s*:)\",[h]),lookbehind:!0,alias:\"punctuation\"}}),e.languages.insertBefore(\"csharp\",\"class-name\",{namespace:{pattern:s(\"(\\\\b(?:namespace|using)\\\\s+)\u003c\u003c0\u003e\u003e(?:\\\\s*\\\\.\\\\s*\u003c\u003c0\u003e\u003e)*(?=\\\\s*[;{])\",[h]),lookbehind:!0,inside:{punctuation:/\\./}},\"type-expression\":{pattern:s(\"(\\\\b(?:default|sizeof|typeof)\\\\s*\\\\(\\\\s*(?!\\\\s))(?:[^()\\\\s]|\\\\s(?!\\\\s)|\u003c\u003c0\u003e\u003e)*(?=\\\\s*\\\\))\",[b]),lookbehind:!0,alias:\"class-name\",inside:$},\"return-type\":{pattern:s(\"\u003c\u003c0\u003e\u003e(?=\\\\s+(?:\u003c\u003c1\u003e\u003e\\\\s*(?:=\u003e|[({]|\\\\.\\\\s*this\\\\s*\\\\[)|this\\\\s*\\\\[))\",[x,m]),inside:$,alias:\"class-name\"},\"constructor-invocation\":{pattern:s(\"(\\\\bnew\\\\s+)\u003c\u003c0\u003e\u003e(?=\\\\s*[[({])\",[x]),lookbehind:!0,inside:$,alias:\"class-name\"},\"generic-method\":{pattern:s(\"\u003c\u003c0\u003e\u003e\\\\s*\u003c\u003c1\u003e\u003e(?=\\\\s*\\\\()\",[h,g]),inside:{function:s(\"^\u003c\u003c0\u003e\u003e\",[h]),generic:{pattern:RegExp(g),alias:\"class-name\",inside:$}}},\"type-list\":{pattern:s(\"\\\\b((?:\u003c\u003c0\u003e\u003e\\\\s+\u003c\u003c1\u003e\u003e|record\\\\s+\u003c\u003c1\u003e\u003e\\\\s*\u003c\u003c5\u003e\u003e|where\\\\s+\u003c\u003c2\u003e\u003e)\\\\s*:\\\\s*)(?:\u003c\u003c3\u003e\u003e|\u003c\u003c4\u003e\u003e|\u003c\u003c1\u003e\u003e\\\\s*\u003c\u003c5\u003e\u003e|\u003c\u003c6\u003e\u003e)(?:\\\\s*,\\\\s*(?:\u003c\u003c3\u003e\u003e|\u003c\u003c4\u003e\u003e|\u003c\u003c6\u003e\u003e))*(?=\\\\s*(?:where|[{;]|=\u003e|$))\",[d,f,h,x,p.source,b,\"\\\\bnew\\\\s*\\\\(\\\\s*\\\\)\"]),lookbehind:!0,inside:{\"record-arguments\":{pattern:s(\"(^(?!new\\\\s*\\\\()\u003c\u003c0\u003e\u003e\\\\s*)\u003c\u003c1\u003e\u003e\",[f,b]),lookbehind:!0,greedy:!0,inside:e.languages.csharp},keyword:p,\"class-name\":{pattern:RegExp(x),greedy:!0,inside:$},punctuation:/[,()]/}},preprocessor:{pattern:/(^[\\t ]*)#.*/m,lookbehind:!0,alias:\"property\",inside:{directive:{pattern:/(#)\\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\\b/,lookbehind:!0,alias:\"keyword\"}}}});var E=B+\"|\"+_,R=n(\"/(?![*/])|//[^\\r\\n]*[\\r\\n]|/\\\\*(?:[^*]|\\\\*(?!/))*\\\\*/|\u003c\u003c0\u003e\u003e\",[E]),z=a(n(\"[^\\\"\u0027/()]|\u003c\u003c0\u003e\u003e|\\\\(\u003c\u003cself\u003e\u003e*\\\\)\",[R]),2),S=\"\\\\b(?:assembly|event|field|method|module|param|property|return|type)\\\\b\",j=n(\"\u003c\u003c0\u003e\u003e(?:\\\\s*\\\\(\u003c\u003c1\u003e\u003e*\\\\))?\",[m,z]);e.languages.insertBefore(\"csharp\",\"class-name\",{attribute:{pattern:s(\"((?:^|[^\\\\s\\\\w\u003e)?])\\\\s*\\\\[\\\\s*)(?:\u003c\u003c0\u003e\u003e\\\\s*:\\\\s*)?\u003c\u003c1\u003e\u003e(?:\\\\s*,\\\\s*\u003c\u003c1\u003e\u003e)*(?=\\\\s*\\\\])\",[S,j]),lookbehind:!0,greedy:!0,inside:{target:{pattern:s(\"^\u003c\u003c0\u003e\u003e(?=\\\\s*:)\",[S]),alias:\"keyword\"},\"attribute-arguments\":{pattern:s(\"\\\\(\u003c\u003c0\u003e\u003e*\\\\)\",[z]),inside:e.languages.csharp},\"class-name\":{pattern:RegExp(m),inside:{punctuation:/\\./}},punctuation:/[:,]/}}});var A=\":[^}\\r\\n]+\",F=a(n(\"[^\\\"\u0027/()]|\u003c\u003c0\u003e\u003e|\\\\(\u003c\u003cself\u003e\u003e*\\\\)\",[R]),2),P=n(\"\\\\{(?!\\\\{)(?:(?![}:])\u003c\u003c0\u003e\u003e)*\u003c\u003c1\u003e\u003e?\\\\}\",[F,A]),U=a(n(\"[^\\\"\u0027/()]|/(?!\\\\*)|/\\\\*(?:[^*]|\\\\*(?!/))*\\\\*/|\u003c\u003c0\u003e\u003e|\\\\(\u003c\u003cself\u003e\u003e*\\\\)\",[E]),2),Z=n(\"\\\\{(?!\\\\{)(?:(?![}:])\u003c\u003c0\u003e\u003e)*\u003c\u003c1\u003e\u003e?\\\\}\",[U,A]);function q(n,a){return{interpolation:{pattern:s(\"((?:^|[^{])(?:\\\\{\\\\{)*)\u003c\u003c0\u003e\u003e\",[n]),lookbehind:!0,inside:{\"format-string\":{pattern:s(\"(^\\\\{(?:(?![}:])\u003c\u003c0\u003e\u003e)*)\u003c\u003c1\u003e\u003e(?=\\\\}$)\",[a,A]),lookbehind:!0,inside:{punctuation:/^:/}},punctuation:/^\\{|\\}$/,expression:{pattern:/[\\s\\S]+/,alias:\"language-csharp\",inside:e.languages.csharp}}},string:/[\\s\\S]+/}}e.languages.insertBefore(\"csharp\",\"string\",{\"interpolation-string\":[{pattern:s(\u0027(^|[^\\\\\\\\])(?:\\\\$@|@\\\\$)\"(?:\"\"|\\\\\\\\[^]|\\\\{\\\\{|\u003c\u003c0\u003e\u003e|[^\\\\\\\\{\"])*\"\u0027,[P]),lookbehind:!0,greedy:!0,inside:q(P,F)},{pattern:s(\u0027(^|[^@\\\\\\\\])\\\\$\"(?:\\\\\\\\.|\\\\{\\\\{|\u003c\u003c0\u003e\u003e|[^\\\\\\\\\"{])*\"\u0027,[Z]),lookbehind:!0,greedy:!0,inside:q(Z,U)}],char:{pattern:RegExp(_),greedy:!0}}),e.languages.dotnet=e.languages.cs=e.languages.csharp}(Prism);\n!function(e){var n=/\\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\\s*[(){}[\\]\u003c\u003e=%~.:,;?+\\-*/\u0026|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\\b/,t=\"(?:[a-z]\\\\w*\\\\s*\\\\.\\\\s*)*(?:[A-Z]\\\\w*\\\\s*\\\\.\\\\s*)*\",s={pattern:RegExp(\"(^|[^\\\\w.])\"+t+\"[A-Z](?:[\\\\d_A-Z]*[a-z]\\\\w*)?\\\\b\"),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\\w*(?:\\s*\\.\\s*[a-z]\\w*)*(?:\\s*\\.)?/,inside:{punctuation:/\\./}},punctuation:/\\./}};e.languages.java=e.languages.extend(\"clike\",{string:{pattern:/(^|[^\\\\])\"(?:\\\\.|[^\"\\\\\\r\\n])*\"/,lookbehind:!0,greedy:!0},\"class-name\":[s,{pattern:RegExp(\"(^|[^\\\\w.])\"+t+\"[A-Z]\\\\w*(?=\\\\s+\\\\w+\\\\s*[;,=()]|\\\\s*(?:\\\\[[\\\\s,]*\\\\]\\\\s*)?::\\\\s*new\\\\b)\"),lookbehind:!0,inside:s.inside},{pattern:RegExp(\"(\\\\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\\\\s+)\"+t+\"[A-Z]\\\\w*\\\\b\"),lookbehind:!0,inside:s.inside}],keyword:n,function:[e.languages.clike.function,{pattern:/(::\\s*)[a-z_]\\w*/,lookbehind:!0}],number:/\\b0b[01][01_]*L?\\b|\\b0x(?:\\.[\\da-f_p+-]+|[\\da-f_]+(?:\\.[\\da-f_p+-]+)?)\\b|(?:\\b\\d[\\d_]*(?:\\.[\\d_]*)?|\\B\\.\\d[\\d_]*)(?:e[+-]?\\d[\\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:\u003c\u003c=?|\u003e\u003e\u003e?=?|-\u003e|--|\\+\\+|\u0026\u0026|\\|\\||::|[?:~]|[-+*/%\u0026|^!=\u003c\u003e]=?)/m,lookbehind:!0},constant:/\\b[A-Z][A-Z_\\d]+\\b/}),e.languages.insertBefore(\"java\",\"string\",{\"triple-quoted-string\":{pattern:/\"\"\"[ \\t]*[\\r\\n](?:(?:\"|\"\")?(?:\\\\.|[^\"\\\\]))*\"\"\"/,greedy:!0,alias:\"string\"},char:{pattern:/\u0027(?:\\\\.|[^\u0027\\\\\\r\\n]){1,6}\u0027/,greedy:!0}}),e.languages.insertBefore(\"java\",\"class-name\",{annotation:{pattern:/(^|[^.])@\\w+(?:\\s*\\.\\s*\\w+)*/,lookbehind:!0,alias:\"punctuation\"},generics:{pattern:/\u003c(?:[\\w\\s,.?]|\u0026(?!\u0026)|\u003c(?:[\\w\\s,.?]|\u0026(?!\u0026)|\u003c(?:[\\w\\s,.?]|\u0026(?!\u0026)|\u003c(?:[\\w\\s,.?]|\u0026(?!\u0026))*\u003e)*\u003e)*\u003e)*\u003e/,inside:{\"class-name\":s,keyword:n,punctuation:/[\u003c\u003e(),.:]/,operator:/[?\u0026|]/}},import:[{pattern:RegExp(\"(\\\\bimport\\\\s+)\"+t+\"(?:[A-Z]\\\\w*|\\\\*)(?=\\\\s*;)\"),lookbehind:!0,inside:{namespace:s.inside.namespace,punctuation:/\\./,operator:/\\*/,\"class-name\":/\\w+/}},{pattern:RegExp(\"(\\\\bimport\\\\s+static\\\\s+)\"+t+\"(?:\\\\w+|\\\\*)(?=\\\\s*;)\"),lookbehind:!0,alias:\"static\",inside:{namespace:s.inside.namespace,static:/\\b\\w+$/,punctuation:/\\./,operator:/\\*/,\"class-name\":/\\w+/}}],namespace:{pattern:RegExp(\"(\\\\b(?:exports|import(?:\\\\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\\\\s+)(?!\u003ckeyword\u003e)[a-z]\\\\w*(?:\\\\.[a-z]\\\\w*)*\\\\.?\".replace(/\u003ckeyword\u003e/g,(function(){return n.source}))),lookbehind:!0,inside:{punctuation:/\\./}}})}(Prism);\nPrism.languages.go=Prism.languages.extend(\"clike\",{string:{pattern:/(^|[^\\\\])\"(?:\\\\.|[^\"\\\\\\r\\n])*\"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\\b/,boolean:/\\b(?:_|false|iota|nil|true)\\b/,number:[/\\b0(?:b[01_]+|o[0-7_]+)i?\\b/i,/\\b0x(?:[a-f\\d_]+(?:\\.[a-f\\d_]*)?|\\.[a-f\\d_]+)(?:p[+-]?\\d+(?:_\\d+)*)?i?(?!\\w)/i,/(?:\\b\\d[\\d_]*(?:\\.[\\d_]*)?|\\B\\.\\d[\\d_]*)(?:e[+-]?[\\d_]+)?i?(?!\\w)/i],operator:/[*\\/%^!=]=?|\\+[=+]?|-[=-]?|\\|[=|]?|\u0026(?:=|\u0026|\\^=?)?|\u003e(?:\u003e=?|=)?|\u003c(?:\u003c=?|=|-)?|:=|\\.\\.\\./,builtin:/\\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\\b/}),Prism.languages.insertBefore(\"go\",\"string\",{char:{pattern:/\u0027(?:\\\\.|[^\u0027\\\\\\r\\n]){0,10}\u0027/,greedy:!0}}),delete Prism.languages.go[\"class-name\"];\n!function(e){for(var a=\"/\\\\*(?:[^*/]|\\\\*(?!/)|/(?!\\\\*)|\u003cself\u003e)*\\\\*/\",t=0;t\u003c2;t++)a=a.replace(/\u003cself\u003e/g,(function(){return a}));a=a.replace(/\u003cself\u003e/g,(function(){return\"[^\\\\s\\\\S]\"})),e.languages.rust={comment:[{pattern:RegExp(\"(^|[^\\\\\\\\])\"+a),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\\\:])\\/\\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?\"(?:\\\\[\\s\\S]|[^\\\\\"])*\"|b?r(#*)\"(?:[^\"]|\"(?!\\1))*\"\\1/,greedy:!0},char:{pattern:/b?\u0027(?:\\\\(?:x[0-7][\\da-fA-F]|u\\{(?:[\\da-fA-F]_*){1,6}\\}|.)|[^\\\\\\r\\n\\t\u0027])\u0027/,greedy:!0},attribute:{pattern:/#!?\\[(?:[^\\[\\]\"]|\"(?:\\\\[\\s\\S]|[^\\\\\"])*\")*\\]/,greedy:!0,alias:\"attr-name\",inside:{string:null}},\"closure-params\":{pattern:/([=(,:]\\s*|\\bmove\\s*)\\|[^|]*\\||\\|[^|]*\\|(?=\\s*(?:\\{|-\u003e))/,lookbehind:!0,greedy:!0,inside:{\"closure-punctuation\":{pattern:/^\\||\\|$/,alias:\"punctuation\"},rest:null}},\"lifetime-annotation\":{pattern:/\u0027\\w+/,alias:\"symbol\"},\"fragment-specifier\":{pattern:/(\\$\\w+:)[a-z]+/,lookbehind:!0,alias:\"punctuation\"},variable:/\\$\\w+/,\"function-definition\":{pattern:/(\\bfn\\s+)\\w+/,lookbehind:!0,alias:\"function\"},\"type-definition\":{pattern:/(\\b(?:enum|struct|trait|type|union)\\s+)\\w+/,lookbehind:!0,alias:\"class-name\"},\"module-declaration\":[{pattern:/(\\b(?:crate|mod)\\s+)[a-z][a-z_\\d]*/,lookbehind:!0,alias:\"namespace\"},{pattern:/(\\b(?:crate|self|super)\\s*)::\\s*[a-z][a-z_\\d]*\\b(?:\\s*::(?:\\s*[a-z][a-z_\\d]*\\s*::)*)?/,lookbehind:!0,alias:\"namespace\",inside:{punctuation:/::/}}],keyword:[/\\b(?:Self|abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\\b/,/\\b(?:bool|char|f(?:32|64)|[ui](?:8|16|32|64|128|size)|str)\\b/],function:/\\b[a-z_]\\w*(?=\\s*(?:::\\s*\u003c|\\())/,macro:{pattern:/\\b\\w+!/,alias:\"property\"},constant:/\\b[A-Z_][A-Z_\\d]+\\b/,\"class-name\":/\\b[A-Z]\\w*\\b/,namespace:{pattern:/(?:\\b[a-z][a-z_\\d]*\\s*::\\s*)*\\b[a-z][a-z_\\d]*\\s*::(?!\\s*\u003c)/,inside:{punctuation:/::/}},number:/\\b(?:0x[\\dA-Fa-f](?:_?[\\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\\d(?:_?\\d)*)?\\.)?\\d(?:_?\\d)*(?:[Ee][+-]?\\d+)?)(?:_?(?:f32|f64|[iu](?:8|16|32|64|size)?))?\\b/,boolean:/\\b(?:false|true)\\b/,punctuation:/-\u003e|\\.\\.=|\\.{1,3}|::|[{}[\\];(),:]/,operator:/[-+*\\/%!^]=?|=[=\u003e]?|\u0026[\u0026=]?|\\|[|=]?|\u003c\u003c?=?|\u003e\u003e?=?|[@?]/},e.languages.rust[\"closure-params\"].inside.rest=e.languages.rust,e.languages.rust.attribute.inside.string=e.languages.rust.string}(Prism);\nPrism.languages.python={comment:{pattern:/(^|[^\\\\])#.*/,lookbehind:!0,greedy:!0},\"string-interpolation\":{pattern:/(?:f|fr|rf)(?:(\"\"\"|\u0027\u0027\u0027)[\\s\\S]*?\\1|(\"|\u0027)(?:\\\\.|(?!\\2)[^\\\\\\r\\n])*\\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\\{\\{)*)\\{(?!\\{)(?:[^{}]|\\{(?!\\{)(?:[^{}]|\\{(?!\\{)(?:[^{}])+\\})+\\})+\\}/,lookbehind:!0,inside:{\"format-spec\":{pattern:/(:)[^:(){}]+(?=\\}$)/,lookbehind:!0},\"conversion-option\":{pattern:/![sra](?=[:}]$)/,alias:\"punctuation\"},rest:null}},string:/[\\s\\S]+/}},\"triple-quoted-string\":{pattern:/(?:[rub]|br|rb)?(\"\"\"|\u0027\u0027\u0027)[\\s\\S]*?\\1/i,greedy:!0,alias:\"string\"},string:{pattern:/(?:[rub]|br|rb)?(\"|\u0027)(?:\\\\.|(?!\\1)[^\\\\\\r\\n])*\\1/i,greedy:!0},function:{pattern:/((?:^|\\s)def[ \\t]+)[a-zA-Z_]\\w*(?=\\s*\\()/g,lookbehind:!0},\"class-name\":{pattern:/(\\bclass\\s+)\\w+/i,lookbehind:!0},decorator:{pattern:/(^[\\t ]*)@\\w+(?:\\.\\w+)*/m,lookbehind:!0,alias:[\"annotation\",\"punctuation\"],inside:{punctuation:/\\./}},keyword:/\\b(?:_(?=\\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\\b/,builtin:/\\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\\b/,boolean:/\\b(?:False|None|True)\\b/,number:/\\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\\b|(?:\\b\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\B\\.\\d+(?:_\\d+)*)(?:e[+-]?\\d+(?:_\\d+)*)?j?(?!\\w)/i,operator:/[-+%=]=?|!=|:=|\\*\\*?=?|\\/\\/?=?|\u003c[\u003c=\u003e]?|\u003e[=\u003e]?|[\u0026|^~]/,punctuation:/[{}[\\];(),.:]/},Prism.languages.python[\"string-interpolation\"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python;\nPrism.languages.sql={comment:{pattern:/(^|[^\\\\])(?:\\/\\*[\\s\\S]*?\\*\\/|(?:--|\\/\\/|#).*)/,lookbehind:!0},variable:[{pattern:/@([\"\u0027`])(?:\\\\[\\s\\S]|(?!\\1)[^\\\\])+\\1/,greedy:!0},/@[\\w.$]+/],string:{pattern:/(^|[^@\\\\])(\"|\u0027)(?:\\\\[\\s\\S]|(?!\\2)[^\\\\]|\\2\\2)*\\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\\\])`(?:\\\\[\\s\\S]|[^`\\\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\\s*\\()/i,keyword:/\\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\\b/i,boolean:/\\b(?:FALSE|NULL|TRUE)\\b/i,number:/\\b0x[\\da-f]+\\b|\\b\\d+(?:\\.\\d*)?|\\B\\.\\d+\\b/i,operator:/[-+*\\/=%^~]|\u0026\u0026?|\\|\\|?|!=?|\u003c(?:=\u003e?|\u003c|\u003e)?|\u003e[\u003e=]?|\\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\\b/i,punctuation:/[;[\\]()`,.]/};\nPrism.languages.ini={comment:{pattern:/(^[ \\f\\t\\v]*)[#;][^\\n\\r]*/m,lookbehind:!0},section:{pattern:/(^[ \\f\\t\\v]*)\\[[^\\n\\r\\]]*\\]?/m,lookbehind:!0,inside:{\"section-name\":{pattern:/(^\\[[ \\f\\t\\v]*)[^ \\f\\t\\v\\]]+(?:[ \\f\\t\\v]+[^ \\f\\t\\v\\]]+)*/,lookbehind:!0,alias:\"selector\"},punctuation:/\\[|\\]/}},key:{pattern:/(^[ \\f\\t\\v]*)[^ \\f\\n\\r\\t\\v=]+(?:[ \\f\\t\\v]+[^ \\f\\n\\r\\t\\v=]+)*(?=[ \\f\\t\\v]*=)/m,lookbehind:!0,alias:\"attr-name\"},value:{pattern:/(=[ \\f\\t\\v]*)[^ \\f\\n\\r\\t\\v]+(?:[ \\f\\t\\v]+[^ \\f\\n\\r\\t\\v]+)*/,lookbehind:!0,alias:\"attr-value\",inside:{\"inner-value\":{pattern:/^(\"|\u0027).+(?=\\1$)/,lookbehind:!0}}},punctuation:/=/};\nPrism.languages.properties={comment:/^[ \\t]*[#!].*$/m,value:{pattern:/(^[ \\t]*(?:\\\\(?:\\r\\n|[\\s\\S])|[^\\\\\\s:=])+(?: *[=:] *(?! )| ))(?:\\\\(?:\\r\\n|[\\s\\S])|[^\\\\\\r\\n])+/m,lookbehind:!0,alias:\"attr-value\"},key:{pattern:/^[ \\t]*(?:\\\\(?:\\r\\n|[\\s\\S])|[^\\\\\\s:=])+(?= *[=:]| )/m,alias:\"attr-name\"},punctuation:/[=:]/};\n!function(e){function n(e){return e.replace(/__/g,(function(){return\"(?:[\\\\w-]+|\u0027[^\u0027\\n\\r]*\u0027|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"\\r\\n])*\\\")\"}))}e.languages.toml={comment:{pattern:/#.*/,greedy:!0},table:{pattern:RegExp(n(\"(^[\\t ]*\\\\[\\\\s*(?:\\\\[\\\\s*)?)__(?:\\\\s*\\\\.\\\\s*__)*(?=\\\\s*\\\\])\"),\"m\"),lookbehind:!0,greedy:!0,alias:\"class-name\"},key:{pattern:RegExp(n(\"(^[\\t ]*|[{,]\\\\s*)__(?:\\\\s*\\\\.\\\\s*__)*(?=\\\\s*=)\"),\"m\"),lookbehind:!0,greedy:!0,alias:\"property\"},string:{pattern:/\"\"\"(?:\\\\[\\s\\S]|[^\\\\])*?\"\"\"|\u0027\u0027\u0027[\\s\\S]*?\u0027\u0027\u0027|\u0027[^\u0027\\n\\r]*\u0027|\"(?:\\\\.|[^\\\\\"\\r\\n])*\"/,greedy:!0},date:[{pattern:/\\b\\d{4}-\\d{2}-\\d{2}(?:[T\\s]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})?)?\\b/i,alias:\"number\"},{pattern:/\\b\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?\\b/,alias:\"number\"}],number:/(?:\\b0(?:x[\\da-zA-Z]+(?:_[\\da-zA-Z]+)*|o[0-7]+(?:_[0-7]+)*|b[10]+(?:_[10]+)*))\\b|[-+]?\\b\\d+(?:_\\d+)*(?:\\.\\d+(?:_\\d+)*)?(?:[eE][+-]?\\d+(?:_\\d+)*)?\\b|[-+]?\\b(?:inf|nan)\\b/,boolean:/\\b(?:false|true)\\b/,punctuation:/[.,=[\\]{}]/}}(Prism);\n!function(e){var n={pattern:/((?:^|[^\\\\$])(?:\\\\{2})*)\\$(?:\\w+|\\{[^{}]*\\})/,lookbehind:!0,inside:{\"interpolation-punctuation\":{pattern:/^\\$\\{?|\\}$/,alias:\"punctuation\"},expression:{pattern:/[\\s\\S]+/,inside:null}}};e.languages.groovy=e.languages.extend(\"clike\",{string:{pattern:/\u0027\u0027\u0027(?:[^\\\\]|\\\\[\\s\\S])*?\u0027\u0027\u0027|\u0027(?:\\\\.|[^\\\\\u0027\\r\\n])*\u0027/,greedy:!0},keyword:/\\b(?:abstract|as|assert|boolean|break|byte|case|catch|char|class|const|continue|def|default|do|double|else|enum|extends|final|finally|float|for|goto|if|implements|import|in|instanceof|int|interface|long|native|new|package|private|protected|public|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|trait|transient|try|void|volatile|while)\\b/,number:/\\b(?:0b[01_]+|0x[\\da-f_]+(?:\\.[\\da-f_p\\-]+)?|[\\d_]+(?:\\.[\\d_]+)?(?:e[+-]?\\d+)?)[glidf]?\\b/i,operator:{pattern:/(^|[^.])(?:~|==?~?|\\?[.:]?|\\*(?:[.=]|\\*=?)?|\\.[@\u0026]|\\.\\.\u003c|\\.\\.(?!\\.)|-[-=\u003e]?|\\+[+=]?|!=?|\u003c(?:\u003c=?|=\u003e?)?|\u003e(?:\u003e\u003e?=?|=)?|\u0026[\u0026=]?|\\|[|=]?|\\/=?|\\^=?|%=?)/,lookbehind:!0},punctuation:/\\.+|[{}[\\];(),:$]/}),e.languages.insertBefore(\"groovy\",\"string\",{shebang:{pattern:/#!.+/,alias:\"comment\",greedy:!0},\"interpolation-string\":{pattern:/\"\"\"(?:[^\\\\]|\\\\[\\s\\S])*?\"\"\"|([\"/])(?:\\\\.|(?!\\1)[^\\\\\\r\\n])*\\1|\\$\\/(?:[^/$]|\\$(?:[/$]|(?![/$]))|\\/(?!\\$))*\\/\\$/,greedy:!0,inside:{interpolation:n,string:/[\\s\\S]+/}}}),e.languages.insertBefore(\"groovy\",\"punctuation\",{\"spock-block\":/\\b(?:and|cleanup|expect|given|setup|then|when|where):/}),e.languages.insertBefore(\"groovy\",\"function\",{annotation:{pattern:/(^|[^.])@\\w+/,lookbehind:!0,alias:\"punctuation\"}}),n.inside.expression.inside=e.languages.groovy}(Prism);\n!function(t){var n=t.util.clone(t.languages.javascript),e=\"(?:\\\\{\u003cS\u003e*\\\\.{3}(?:[^{}]|\u003cBRACES\u003e)*\\\\})\";function a(t,n){return t=t.replace(/\u003cS\u003e/g,(function(){return\"(?:\\\\s|//.*(?!.)|/\\\\*(?:[^*]|\\\\*(?!/))\\\\*/)\"})).replace(/\u003cBRACES\u003e/g,(function(){return\"(?:\\\\{(?:\\\\{(?:\\\\{[^{}]*\\\\}|[^{}])*\\\\}|[^{}])*\\\\})\"})).replace(/\u003cSPREAD\u003e/g,(function(){return e})),RegExp(t,n)}e=a(e).source,t.languages.jsx=t.languages.extend(\"markup\",n),t.languages.jsx.tag.pattern=a(\"\u003c/?(?:[\\\\w.:-]+(?:\u003cS\u003e+(?:[\\\\w.:$-]+(?:=(?:\\\"(?:\\\\\\\\[^]|[^\\\\\\\\\\\"])*\\\"|\u0027(?:\\\\\\\\[^]|[^\\\\\\\\\u0027])*\u0027|[^\\\\s{\u0027\\\"/\u003e=]+|\u003cBRACES\u003e))?|\u003cSPREAD\u003e))*\u003cS\u003e*/?)?\u003e\"),t.languages.jsx.tag.inside.tag.pattern=/^\u003c\\/?[^\\s\u003e\\/]*/,t.languages.jsx.tag.inside[\"attr-value\"].pattern=/=(?!\\{)(?:\"(?:\\\\[\\s\\S]|[^\\\\\"])*\"|\u0027(?:\\\\[\\s\\S]|[^\\\\\u0027])*\u0027|[^\\s\u0027\"\u003e]+)/,t.languages.jsx.tag.inside.tag.inside[\"class-name\"]=/^[A-Z]\\w*(?:\\.[A-Z]\\w*)*$/,t.languages.jsx.tag.inside.comment=n.comment,t.languages.insertBefore(\"inside\",\"attr-name\",{spread:{pattern:a(\"\u003cSPREAD\u003e\"),inside:t.languages.jsx}},t.languages.jsx.tag),t.languages.insertBefore(\"inside\",\"special-attr\",{script:{pattern:a(\"=\u003cBRACES\u003e\"),alias:\"language-javascript\",inside:{\"script-punctuation\":{pattern:/^=(?=\\{)/,alias:\"punctuation\"},rest:t.languages.jsx}}},t.languages.jsx.tag);var s=function(t){return t?\"string\"==typeof t?t:\"string\"==typeof t.content?t.content:t.content.map(s).join(\"\"):\"\"},g=function(n){for(var e=[],a=0;a\u003cn.length;a++){var o=n[a],i=!1;if(\"string\"!=typeof o\u0026\u0026(\"tag\"===o.type\u0026\u0026o.content[0]\u0026\u0026\"tag\"===o.content[0].type?\"\u003c/\"===o.content[0].content[0].content?e.length\u003e0\u0026\u0026e[e.length-1].tagName===s(o.content[0].content[1])\u0026\u0026e.pop():\"/\u003e\"===o.content[o.content.length-1].content||e.push({tagName:s(o.content[0].content[1]),openedBraces:0}):e.length\u003e0\u0026\u0026\"punctuation\"===o.type\u0026\u0026\"{\"===o.content?e[e.length-1].openedBraces++:e.length\u003e0\u0026\u0026e[e.length-1].openedBraces\u003e0\u0026\u0026\"punctuation\"===o.type\u0026\u0026\"}\"===o.content?e[e.length-1].openedBraces--:i=!0),(i||\"string\"==typeof o)\u0026\u0026e.length\u003e0\u0026\u00260===e[e.length-1].openedBraces){var r=s(o);a\u003cn.length-1\u0026\u0026(\"string\"==typeof n[a+1]||\"plain-text\"===n[a+1].type)\u0026\u0026(r+=s(n[a+1]),n.splice(a+1,1)),a\u003e0\u0026\u0026(\"string\"==typeof n[a-1]||\"plain-text\"===n[a-1].type)\u0026\u0026(r=s(n[a-1])+r,n.splice(a-1,1),a--),n[a]=new t.Token(\"plain-text\",r,null,r)}o.content\u0026\u0026\"string\"!=typeof o.content\u0026\u0026g(o.content)}};t.hooks.add(\"after-tokenize\",(function(t){\"jsx\"!==t.language\u0026\u0026\"tsx\"!==t.language||g(t.tokens)}))}(Prism);\n!function(e){var a=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend(\"jsx\",a),delete e.languages.tsx.parameter,delete e.languages.tsx[\"literal-property\"];var t=e.languages.tsx.tag;t.pattern=RegExp(\"(^|[^\\\\w$]|(?=\u003c/))(?:\"+t.pattern.source+\")\",t.pattern.flags),t.lookbehind=!0}(Prism);\n!function(e){e.languages.diff={coord:[/^(?:\\*{3}|-{3}|\\+{3}).*$/m,/^@@.*@@$/m,/^\\d.*$/m]};var n={\"deleted-sign\":\"-\",\"deleted-arrow\":\"\u003c\",\"inserted-sign\":\"+\",\"inserted-arrow\":\"\u003e\",unchanged:\" \",diff:\"!\"};Object.keys(n).forEach((function(a){var i=n[a],r=[];/^\\w+$/.test(a)||r.push(/\\w+/.exec(a)[0]),\"diff\"===a\u0026\u0026r.push(\"bold\"),e.languages.diff[a]={pattern:RegExp(\"^(?:[\"+i+\"].*(?:\\r\\n?|\\n|(?![\\\\s\\\\S])))+\",\"m\"),alias:r,inside:{line:{pattern:/(.)(?=[\\s\\S]).*(?:\\r\\n?|\\n)?/,lookbehind:!0},prefix:{pattern:/[\\s\\S]/,alias:/\\w+/.exec(a)[0]}}}})),Object.defineProperty(e.languages.diff,\"PREFIXES\",{value:n})}(Prism);";
22
+ // Prism 惰性初始化:~75KB 源码的解析(正则编译)从模块加载的同步路径移走,
23
+ // 且仅在首个 diff/read payload 实际解析后才安排空闲期执行(schedulePrismIdle),
24
+ // 避免每次页面加载都无条件付出解析成本;首次实际使用时也会同步 ensurePrism()。
25
+ // Prism core 是 UMD 构建:内部自注册 _self.Prism(_self 解析为 window),尾部还
26
+ // 直接写 global.Prism(浏览器中 global === window)——直接在全局作用域执行会把
27
+ // vendored Prism 写入/覆盖宿主页面已有的 window.Prism。用局部空对象同时遮蔽
28
+ // window 与 global 两个参数(typeof 检查仍通过),写不到真全局。
29
+ let PG_PRISM = null;
30
+ let prismInitDone = false;
31
+ let prismIdleScheduled = false;
32
+ // 首个 diff/read payload 解析后安排空闲期初始化(无 requestIdleCallback 时
33
+ // setTimeout 兜底);幂等,仅调度一次。
34
+ function schedulePrismIdle() {
35
+ if (prismIdleScheduled) return;
36
+ prismIdleScheduled = true;
37
+ scheduleIdle(ensurePrism, 3000);
38
+ }
39
+ function ensurePrism() {
40
+ if (prismInitDone) return;
41
+ prismInitDone = true;
42
+ let prism = null;
43
+ try {
44
+ prism = new Function('window', 'global', PRISM_SRC + '\n;return typeof Prism !== "undefined" ? Prism : null;')({}, {});
45
+ } catch (e) { console.warn('[permgate] prism init via eval failed:', e); }
46
+ if (!prism && typeof document !== 'undefined') {
47
+ // CSP 禁 eval 时回退:内联 script 标签(宿主允许内联脚本时生效)。
48
+ // 内联脚本在全局作用域执行,Prism 会自注册 window.Prism——注入前保存宿主原值,
49
+ // 读取结果后立即恢复,避免覆盖其它模块已加载的 window.Prism;
50
+ // 结果经 window.__PG_PRISM__ 传出,读取后立即清理。
51
+ // data-manual 使 Prism core 尾部跳过自动高亮调度(highlightAll),
52
+ // 防止其改写宿主页面中 .language-* 元素(该调度持有闭包、事后恢复
53
+ // window.Prism 无法取消)。
54
+ let prevPrism;
55
+ try {
56
+ const sc = document.createElement('script');
57
+ prevPrism = (typeof window !== 'undefined' && 'Prism' in window) ? window.Prism : undefined;
58
+ sc.setAttribute('data-manual', '');
59
+ sc.textContent = '(function(){' + PRISM_SRC + '\nwindow.__PG_PRISM__ = typeof Prism !== "undefined" ? Prism : null;})();';
60
+ document.head.appendChild(sc);
61
+ prism = (typeof window !== 'undefined' && window.__PG_PRISM__) || null;
62
+ } catch (e2) { console.warn('[permgate] prism init via script failed:', e2); }
63
+ finally {
64
+ // 恢复宿主 window.Prism 与清理 __PG_PRISM__ 放在 finally:执行段内任一步抛错
65
+ // (如 document.head 为 null 时 appendChild 失败)也不会跳过恢复,
66
+ // 避免宿主 Prism 被 vendored 实例静默替换(global 存在的宿主环境)。
67
+ if (typeof window !== 'undefined') {
68
+ if ('__PG_PRISM__' in window) {
69
+ try { delete window.__PG_PRISM__ } catch (e3) { try { window.__PG_PRISM__ = null } catch (e5) {} }
70
+ }
71
+ try {
72
+ if (prevPrism === undefined) delete window.Prism;
73
+ else window.Prism = prevPrism;
74
+ } catch (e4) {}
75
+ }
76
+ }
77
+ }
78
+ PG_PRISM = prism;
79
+ }
80
+ // 扩展名 → Prism 语言
81
+ const PRISM_LANG_BY_EXT = {
82
+ js: 'javascript', mjs: 'javascript', cjs: 'javascript', jsx: 'jsx',
83
+ ts: 'typescript', tsx: 'tsx',
84
+ json: 'json', jsonc: 'json', yml: 'yaml', yaml: 'yaml',
85
+ md: 'markdown', markdown: 'markdown',
86
+ css: 'css', html: 'markup', htm: 'markup', xml: 'markup', svg: 'markup',
87
+ sh: 'bash', bash: 'bash', cmd: 'batch', bat: 'batch', ps1: 'powershell',
88
+ py: 'python', c: 'c', h: 'cpp', cpp: 'cpp', cc: 'cpp', cxx: 'cpp', hpp: 'cpp',
89
+ cs: 'csharp', java: 'java', go: 'go', rs: 'rust', sql: 'sql',
90
+ ini: 'ini', cfg: 'ini', conf: 'ini', toml: 'toml', properties: 'properties',
91
+ gradle: 'groovy', diff: 'diff', patch: 'diff',
92
+ };
93
+ function prismLangFor(file) {
94
+ const base = String(file || '').split(/[\\/]/).pop() || '';
95
+ const idx = base.lastIndexOf('.');
96
+ if (idx <= 0) return null;
97
+ const lang = PRISM_LANG_BY_EXT[base.slice(idx + 1).toLowerCase()];
98
+ return lang && PG_PRISM && PG_PRISM.languages[lang] ? lang : null;
99
+ }
100
+ // 语法高亮单侧文本上限(128KB):整段 tokenize 在 UI 主线程同步执行,超大文件降级纯文本
101
+ const HIGHLIGHT_MAX_CHARS = 131072
102
+ // 从 ops 重建完整 old/new 文本(gap 携带隐藏行;缺失时返回 null 放弃高亮),
103
+ // 并同步收集每行在文本中的位置对应的真实文件行号(oldNums/newNums),供客户端
104
+ // 按行号 Map 取高亮(changesOnly 过滤后文本非连续,不能再用「行号-首行」偏移索引)。
105
+ // 拼接过程中累计长度,任一侧一旦超过 HIGHLIGHT_MAX_CHARS 立即中止返回 null——
106
+ // 避免对超大 payload(gap 最多携带 10 万行隐藏行,见 index.js pushCtxRun)先整段
107
+ // 拼接再丢弃,白白付出主线程内存与拷贝成本。
108
+ function prismTextsFromOps(ops) {
109
+ const oldParts = [];
110
+ const newParts = [];
111
+ const oldNums = [];
112
+ const newNums = [];
113
+ let oldLen = 0;
114
+ let newLen = 0;
115
+ const over = () => oldLen > HIGHLIGHT_MAX_CHARS || newLen > HIGHLIGHT_MAX_CHARS
116
+ for (const op of ops || []) {
117
+ if (op.t === 'g') {
118
+ if (!op.lines) return null;
119
+ for (const l of op.lines) {
120
+ oldParts.push(l.s); newParts.push(l.s);
121
+ oldNums.push(l.o); newNums.push(l.n);
122
+ oldLen += l.s.length + 1; newLen += l.s.length + 1;
123
+ if (over()) return null;
124
+ }
125
+ } else if (op.t === 'a') { newParts.push(op.s); newNums.push(op.n); newLen += op.s.length + 1; if (over()) return null }
126
+ else if (op.t === 'd') { oldParts.push(op.s); oldNums.push(op.o); oldLen += op.s.length + 1; if (over()) return null }
127
+ else { oldParts.push(op.s); newParts.push(op.s); oldNums.push(op.o); newNums.push(op.n); oldLen += op.s.length + 1; newLen += op.s.length + 1; if (over()) return null }
128
+ }
129
+ return { oldText: oldParts.join('\n'), newText: newParts.join('\n'), oldNums, newNums };
130
+ }
131
+ // HTML 转义(高亮行与纯文本降级行共用同一规则,避免两处漂移导致转义不一致)
132
+ const escapeHtml = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
133
+ // 整段 tokenize 后按行切分:跨行 token(字符串/块注释)在换行处关闭并在下一行重开 span,
134
+ // 每行得到独立完整的 HTML。返回行数组(行 i 对应文本第 i+1 行),失败返回 null。
135
+ function prismHighlightLines(text, lang) {
136
+ if (!PG_PRISM || !lang || !PG_PRISM.languages[lang]) return null;
137
+ try {
138
+ const tokens = PG_PRISM.tokenize(text, PG_PRISM.languages[lang]);
139
+ const lines = [];
140
+ let cur = [];
141
+ const open = [];
142
+ const nl = () => {
143
+ for (let i = 0; i < open.length; i++) cur.push('</span>');
144
+ lines.push(cur.join(''));
145
+ cur = [];
146
+ for (let i = 0; i < open.length; i++) cur.push('<span class="token ' + open[i] + '">');
147
+ };
148
+ const emitText = (s) => {
149
+ const parts = s.split('\n');
150
+ cur.push(escapeHtml(parts[0]));
151
+ for (let i = 1; i < parts.length; i++) { nl(); cur.push(escapeHtml(parts[i])) }
152
+ };
153
+ const walk = (toks) => {
154
+ for (const t of toks) {
155
+ if (typeof t === 'string') { emitText(t); continue }
156
+ const type = String(t.type);
157
+ cur.push('<span class="token ' + type + '">');
158
+ open.push(type);
159
+ if (typeof t.content === 'string') emitText(t.content);
160
+ else walk(t.content);
161
+ open.pop();
162
+ cur.push('</span>');
163
+ }
164
+ };
165
+ walk(tokens);
166
+ for (let i = 0; i < open.length; i++) cur.push('</span>');
167
+ lines.push(cur.join(''));
168
+ return lines;
169
+ } catch (e) { return null }
170
+ }
171
+ // 分块高亮:按行数与字符预算分块 tokenize,块间跨行 token 断开(与 changesOnly 相同
172
+ // 取舍);配合 idle 调度逐块让出主线程,避免整段文本一次 tokenize 长时间占用主线程。
173
+ // 返回逐步执行器:step() 处理下一块并返回是否还有剩余块,result 为累计的行 HTML。
174
+ const CHUNK_LINES = 400
175
+ const CHUNK_CHARS = 16384
176
+ function chunkHighlighter(text, lang) {
177
+ const lines = text.split('\n');
178
+ const out = [];
179
+ let pos = 0;
180
+ let failed = false;
181
+ // 任一行超过字符预算(length >= CHUNK_CHARS,与下方首行守卫同一阈值):
182
+ // 行首则整行按纯文本转义输出;位于块中段则提前断开块、把该行留给下一轮首行
183
+ // 守卫处理——保证任何一次 tokenize 的输入都不含超长行,避免同步阻塞主线程。
184
+ const step = () => {
185
+ if (failed || pos >= lines.length) return false;
186
+ let end = pos;
187
+ let bytes = 0;
188
+ while (end < lines.length && (end - pos) < CHUNK_LINES && bytes < CHUNK_CHARS) {
189
+ if (end > pos && lines[end].length >= CHUNK_CHARS) break;
190
+ bytes += lines[end].length + 1;
191
+ end++;
192
+ }
193
+ if (end === pos + 1 && bytes > CHUNK_CHARS) {
194
+ out.push(escapeHtml(lines[pos]));
195
+ pos = end;
196
+ return pos < lines.length;
197
+ }
198
+ const chunk = lines.slice(pos, end).join('\n');
199
+ const html = prismHighlightLines(chunk, lang);
200
+ if (html === null) { failed = true; return false }
201
+ for (const h of html) out.push(h);
202
+ pos = end;
203
+ return pos < lines.length;
204
+ };
205
+ return { step, result: out, failed: () => failed };
206
+ }
207
+ // idle 调度封装:优先 requestIdleCallback(带兜底超时),无则 setTimeout;返回取消函数。
208
+ function scheduleIdle(fn, timeout) {
209
+ if (typeof requestIdleCallback === 'function') {
210
+ const id = requestIdleCallback(fn, { timeout });
211
+ return () => { try { cancelIdleCallback(id) } catch (e) {} };
212
+ }
213
+ const id = setTimeout(fn, 0);
214
+ return () => clearTimeout(id);
215
+ }
216
+ // 高亮结果缓存:key 为 diffCache 数据对象(引用稳定)。同一数据在抽屉重开/
217
+ // 详情重展开(组件重挂载)时直接复用已完成的行 HTML,避免重复分块 tokenize。
218
+ const diffHlCache = new WeakMap();
219
+ const readHlCache = new WeakMap();
220
+ // 分块渐进高亮 hook(DiffBlock old/new 双侧与 ReadBlock 单侧共用同一实现,
221
+ // 避免两份状态机漂移):首次使用时同步 ensurePrism(惰性初始化),idle 调度
222
+ // 逐块 tokenize,完成前返回 null(首渲染纯文本、渐进补高亮);结果写入 cache
223
+ // (按 data 引用缓存),重挂载直接复用。
224
+ function useProgressiveHighlight(text, file, cache) {
225
+ const [lineHtml, setLineHtml] = React.useState(() => (cache && cache.text === text) ? cache.html : null);
226
+ React.useEffect(() => {
227
+ ensurePrism();
228
+ const lang = prismLangFor(file);
229
+ if (text == null || text.length > HIGHLIGHT_MAX_CHARS || !lang) { setLineHtml(null); return undefined }
230
+ if (cache && cache.text === text && cache.html) return undefined
231
+ let alive = true;
232
+ const H = chunkHighlighter(text, lang);
233
+ let cancel = null;
234
+ const work = () => {
235
+ if (!alive) return;
236
+ const more = H.step();
237
+ if (H.failed()) { setLineHtml(null); return }
238
+ if (more) { cancel = scheduleIdle(work, 2000); return }
239
+ if (cache) { cache.text = text; cache.html = H.result }
240
+ setLineHtml(H.result);
241
+ };
242
+ cancel = scheduleIdle(work, 2000);
243
+ return () => { alive = false; if (cancel) cancel() };
244
+ }, [text, file]);
245
+ return lineHtml;
246
+ }
247
+
248
+ const ROUTES = {
249
+ 'permgate:status': ['GET', '/permgate/status'],
250
+ 'permgate:pending': ['GET', '/permgate/pending'],
251
+ 'permgate:file-diff': ['POST', '/permgate/file-diff'],
252
+ 'permgate:decide': ['POST', '/permgate/decide'],
253
+ 'permgate:set-sandbox': ['POST', '/permgate/set-sandbox'],
254
+ 'permgate:set-categories': ['POST', '/permgate/set-categories'],
255
+ 'permgate:set-category': ['POST', '/permgate/set-category'],
256
+ 'permgate:set-quick': ['POST', '/permgate/set-quick'],
257
+ 'permgate:add-exception': ['POST', '/permgate/add-exception'],
258
+ 'permgate:remove-exception': ['POST', '/permgate/remove-exception'],
259
+ 'permgate:add-rule': ['POST', '/permgate/add-rule'],
260
+ 'permgate:remove-rule': ['POST', '/permgate/remove-rule'],
261
+ 'permgate:reload': ['POST', '/permgate/reload'],
262
+ 'permgate:open-config': ['POST', '/permgate/open-config'],
263
+ 'permgate:open-file': ['POST', '/permgate/open-file'],
264
+ };
265
+ function call(method, args) {
266
+ const entry = ROUTES[method] || ['GET', '/permgate/status'];
267
+ const m = entry[0];
268
+ let path = entry[1];
269
+ // 附带当前 dsh 语言(zh/en),宿主按语言返回 reason 等文案;缺失/非法由宿主回退中文
270
+ const lang = (LC && LC.locale && typeof LC.locale.getLocale === 'function') ? LC.locale.getLocale().active : 'zh';
271
+ const opts = { method: m, headers: { accept: 'application/json' } };
272
+ const extra = args || {};
273
+ if (m === 'POST') {
274
+ opts.headers['content-type'] = 'application/json';
275
+ opts.body = JSON.stringify(Object.assign({}, extra, { lang }));
276
+ } else {
277
+ const q = ['lang=' + encodeURIComponent(lang)];
278
+ for (const k of Object.keys(extra)) {
279
+ const v = extra[k];
280
+ if (v !== undefined && v !== null && v !== '') q.push(k + '=' + encodeURIComponent(String(v)));
281
+ }
282
+ path = path + (path.indexOf('?') === -1 ? '?' : '&') + q.join('&');
283
+ }
284
+ return fetch(path, opts).then((r) => r.json());
285
+ }
286
+
287
+ // SSE 事件订阅:/permgate/events 长连接推送(状态/待审批变化即时刷新,替代轮询)
288
+ let eventSource = null;
289
+ const eventListeners = new Set();
290
+ function notifyEvents(data) {
291
+ for (const fn of Array.from(eventListeners)) { try { fn(data); } catch (e) {} }
292
+ }
293
+ function ensureEventSource() {
294
+ if (eventSource) return;
295
+ eventSource = new EventSource('/permgate/events');
296
+ eventSource.onmessage = (ev) => {
297
+ let data = null;
298
+ try { data = JSON.parse(ev.data); } catch (e) {}
299
+ if (data) notifyEvents(data);
300
+ };
301
+ // 首连与断线重连成功都会触发:全量刷新兜底,收敛重连期间丢失的事件
302
+ eventSource.onopen = () => notifyEvents({ type: 'refresh' });
303
+ eventSource.onerror = () => { /* EventSource 自动重连 */ };
304
+ }
305
+ function subscribeEvents(fn) {
306
+ eventListeners.add(fn);
307
+ ensureEventSource();
308
+ return () => {
309
+ eventListeners.delete(fn);
310
+ if (eventListeners.size === 0 && eventSource) {
311
+ try { eventSource.close(); } catch (e) {}
312
+ eventSource = null;
313
+ }
314
+ };
315
+ }
316
+
317
+ // ── 权限选择器图标兼容层(参照 dsh-auto-approve 的 DOM best-effort 方案)──
318
+ // rc.6 的权限选择器不提供自定义 preset 图标 API(glyph 为内置三个 key 硬编码)。
319
+ // 这里用 MutationObserver 识别「自定义审查」的触发器与菜单项,注入盾牌+放大镜
320
+ // svg 徽标(-webkit-mask 跟随 currentColor)。依赖 DOM 结构与无障碍文案,
321
+ // dsh 升级后可能失效——仅影响图标显示,不影响审查功能。
322
+ const PG_STYLE = 'data-dsh-permgate-style';
323
+ const PG_CUSTOM_LABEL = '自定义审查';
324
+ const PG_NAME_ZH = '自定义审查';
325
+ const PG_NAME_EN = 'Custom Review';
326
+ const PG_DESC_ZH = '按分类逐项审查工具调用;被底层沙箱拒绝时弹出升级审批';
327
+ const PG_DESC_EN = 'Per-category tool review; sandbox denials trigger an upgrade approval';
328
+ const PG_PERMISSION_LABELS = new Set(['Read Only', 'Workspace Write', '自定义审查', 'Custom Review', 'Full access', '仅可查看', '工作区内修改', '完全权限']);
329
+ const PG_TRIGGER_PREFIXES = ['访问模式,当前:', 'Access mode, current: '];
330
+ const PG_TRIGGER_SELECTOR = ['button[aria-label^="访问模式,当前:"]', 'button[aria-label^="Access mode, current: "]'].join(',');
331
+ const PG_MASK = 'url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 16 16%22 fill=%22none%22%3E%3Cpath d=%22M8.20554%200.899994L14.7901%203.36857V7.01026C14.7901%2012%2011.0466%2014.2103%208.20554%2015.3C5.36446%2014.2103%201.62012%2012%201.62012%207.01026V3.36857L8.20554%200.899994Z%22 stroke=%22black%22 stroke-width=%221.31831%22 stroke-linejoin=%22round%22/%3E%3Ccircle cx=%227%22 cy=%227%22 r=%222.9%22 stroke=%22black%22 stroke-width=%221.2%22/%3E%3Cpath d=%22M9.2%209.2L11.6%2011.6%22 stroke=%22black%22 stroke-width=%221.4%22 stroke-linecap=%22round%22/%3E%3C/svg%3E")';
332
+ const PG_CSS = '\n' +
333
+ // 触发器徽标:纯 CSS 按 aria-label 选择(平台 t() 本地化前缀 + 两种语言的
334
+ // 预设名,四个变体全覆盖)。无需 JS 打标,跟随语言/预设自动显示,重渲染不丢失。
335
+ 'button[aria-label^="访问模式,当前:自定义审查"]::before,' +
336
+ 'button[aria-label^="Access mode, current: 自定义审查"]::before,' +
337
+ 'button[aria-label^="访问模式,当前:Custom Review"]::before,' +
338
+ 'button[aria-label^="Access mode, current: Custom Review"]::before' +
339
+ ' { content: ""; display: inline-block; flex: 0 0 auto; width: 14px; height: 14px; margin-right: 4px; background-color: currentColor; -webkit-mask: ' + PG_MASK + ' center / contain no-repeat; mask: ' + PG_MASK + ' center / contain no-repeat; }\n';
340
+ const pgNoop = () => {};
341
+ function pgTriggerLabel(value) {
342
+ return typeof value === 'string' && PG_TRIGGER_PREFIXES.some((p) => value.startsWith(p));
343
+ }
344
+ // 预设名/描述文本的语言化交换(best-effort DOM 层):平台权限选择器对
345
+ // preset name/description 无 i18n 钩子(内置项在两种语言下均为英文),
346
+ // 这里在 en 语言下把「自定义审查」替换为 Custom Review(含触发器
347
+ // aria-label),在 zh 语言下把残留的英文替换回中文。仅限定权限选择器
348
+ // 相关表面(触发器/按钮/菜单/列表项),绝不触碰聊天内容等任意文本。
349
+ function pgActiveLang() {
350
+ // 三级回退:locale 服务 → <html lang> → 平台触发器 aria-label 前缀
351
+ // (前缀是平台 t() 本地化渲染的,直接反映界面实际语言)
352
+ try {
353
+ const l = LC && LC.locale && typeof LC.locale.getLocale === 'function' ? LC.locale.getLocale().active : '';
354
+ if (l === 'en' || l === 'zh') return l;
355
+ } catch (e) {}
356
+ try {
357
+ const htmlLang = document.documentElement && document.documentElement.getAttribute && document.documentElement.getAttribute('lang');
358
+ if (htmlLang === 'en' || htmlLang === 'zh') return htmlLang;
359
+ } catch (e) {}
360
+ try {
361
+ const el = document.querySelector && document.querySelector(PG_TRIGGER_SELECTOR);
362
+ const label = el && el.getAttribute && el.getAttribute('aria-label');
363
+ if (typeof label === 'string') {
364
+ if (label.indexOf('Access mode') !== -1) return 'en';
365
+ if (label.indexOf('访问模式') !== -1) return 'zh';
366
+ }
367
+ } catch (e) {}
368
+ return 'zh';
369
+ }
370
+ function pgSwapText(el, from, to) {
371
+ try {
372
+ if (!el || typeof el.textContent !== 'string') return;
373
+ // 只原位修改直接文本子节点的值(nodeValue),绝不 textContent 赋值或
374
+ // 替换节点:React 持有元素/文本节点引用,整体替换会破坏其子树结构
375
+ // 导致渲染崩溃(曾因此打不开下拉框)。React 重渲染只会把同一文本
376
+ // 节点的值改回,观察器再换回来,结构始终完好。
377
+ for (const node of Array.from(el.childNodes || [])) {
378
+ if (node.nodeType !== 3) continue;
379
+ const v = node.nodeValue || '';
380
+ if (v.indexOf(from) === -1) continue;
381
+ const next = v.split(from).join(to);
382
+ if (next !== v) node.nodeValue = next;
383
+ }
384
+ } catch (e) {}
385
+ }
386
+ function pgSwapAttr(el, attr, from, to) {
387
+ try {
388
+ const v = el.getAttribute && el.getAttribute(attr);
389
+ if (typeof v !== 'string' || v.indexOf(from) === -1) return;
390
+ const next = v.split(from).join(to);
391
+ if (next !== v) el.setAttribute(attr, next);
392
+ } catch (e) {}
393
+ }
394
+ const PG_SWAP_SELECTOR = 'button,[role="menuitem"],[role="menuitemradio"],[role="menuitemcheckbox"],[role="option"],[role="menu"],[role="listbox"]';
395
+ // 就地替换某元素首个文本子节点的值(仅当其为唯一的直接文本子节点)
396
+ function pgSwapSingleText(el, value) {
397
+ try {
398
+ if (!el || typeof el.textContent !== 'string') return false;
399
+ let hit = null;
400
+ for (const node of Array.from(el.childNodes || [])) {
401
+ if (node.nodeType !== 3) continue;
402
+ const v = node.nodeValue || '';
403
+ if (v.trim() === '') continue;
404
+ if (hit !== null) return false; // 多个文本子节点,不整段替换
405
+ hit = node;
406
+ }
407
+ if (hit === null) return false;
408
+ if (hit.nodeValue !== value) { hit.nodeValue = value; return true; }
409
+ } catch (e) {}
410
+ return false;
411
+ }
412
+ function pgScanText(document) {
413
+ try {
414
+ const lang = pgActiveLang();
415
+ const from = lang === 'en' ? PG_NAME_ZH : PG_NAME_EN;
416
+ const to = lang === 'en' ? PG_NAME_EN : PG_NAME_ZH;
417
+ const fromDesc = lang === 'en' ? PG_DESC_ZH : PG_DESC_EN;
418
+ const toDesc = lang === 'en' ? PG_DESC_EN : PG_DESC_ZH;
419
+ const body = document.body;
420
+ if (!body) return;
421
+ // 轻量早退:目标名不在页面中就跳过(交换完成后通常立即命中此处,
422
+ // 使流式渲染期间的扫描成本≈一次 textContent 读取)
423
+ const bodyText = body.textContent || '';
424
+ if (bodyText.indexOf(from) === -1) return;
425
+ // 1) 触发器按钮:aria-label 按前缀切出名称段,仅当名称恰为 zh/en 名时
426
+ // 替换(绝不误改 Read Only 等其他预设名);可见文本整段就地替换
427
+ for (const el of Array.from(document.querySelectorAll(PG_TRIGGER_SELECTOR))) {
428
+ const label = el.getAttribute && el.getAttribute('aria-label');
429
+ if (typeof label === 'string') {
430
+ for (const p of PG_TRIGGER_PREFIXES) {
431
+ if (label.length > p.length && label.startsWith(p)) {
432
+ const name = label.slice(p.length);
433
+ if (name === PG_NAME_ZH && lang === 'en') {
434
+ try { el.setAttribute('aria-label', p + PG_NAME_EN); } catch (e) {}
435
+ } else if (name === PG_NAME_EN && lang === 'zh') {
436
+ try { el.setAttribute('aria-label', p + PG_NAME_ZH); } catch (e) {}
437
+ }
438
+ break;
439
+ }
440
+ }
441
+ }
442
+ pgSwapAttr(el, 'title', from, to);
443
+ pgSwapAttr(el, 'title', fromDesc, toDesc);
444
+ // 可见文本:触发器自身及后代叶子中,文本恰为「非当前语言名」的整段替换
445
+ const nodes = [el];
446
+ try { nodes.push.apply(nodes, Array.from(el.querySelectorAll('*'))); } catch (e) {}
447
+ for (const n of nodes) {
448
+ if (n.children && n.children.length) continue;
449
+ const t = (n.textContent || '').trim();
450
+ if (t !== from) continue;
451
+ pgSwapSingleText(n, to);
452
+ }
453
+ }
454
+ // 2) 按钮/菜单/列表项:子串交换(名称与描述)+ title 交换
455
+ for (const el of Array.from(document.querySelectorAll(PG_SWAP_SELECTOR))) {
456
+ pgSwapText(el, from, to);
457
+ pgSwapText(el, fromDesc, toDesc);
458
+ pgSwapAttr(el, 'title', from, to);
459
+ pgSwapAttr(el, 'title', fromDesc, toDesc);
460
+ }
461
+ // 3) 按钮/菜单表面的叶子后代:子串交换(菜单项 label/detail 可能
462
+ // 是独立叶子元素)
463
+ for (const el of Array.from(document.querySelectorAll('button *,[role="menu"] *,[role="listbox"] *,[role="combobox"] *'))) {
464
+ if (el.children && el.children.length) continue;
465
+ pgSwapText(el, from, to);
466
+ pgSwapText(el, fromDesc, toDesc);
467
+ }
468
+ } catch (e) {}
469
+ }
470
+ function pgTouchesSurface(node) {
471
+ try {
472
+ if (!node || node.nodeType !== 1) return false;
473
+ if (node.getAttribute && node.getAttribute('data-composer-seat') !== null) return true;
474
+ if (node.getAttribute && node.getAttribute('role') === 'menu') return true;
475
+ if (pgTriggerLabel(node.getAttribute && node.getAttribute('aria-label'))) return true;
476
+ if (typeof node.querySelector === 'function') {
477
+ if (node.querySelector('[data-composer-seat]') !== null) return true;
478
+ if (node.querySelector('[role="menu"]') !== null) return true;
479
+ if (node.querySelector(PG_TRIGGER_SELECTOR) !== null) return true;
480
+ }
481
+ return typeof node.closest === 'function' && node.closest('[data-composer-seat]') !== null;
482
+ } catch (e) { return false; }
483
+ }
484
+ function pgInsideComposer(node) {
485
+ try {
486
+ if (!node || node.nodeType !== 1) return false;
487
+ if (node.getAttribute && node.getAttribute('data-composer-seat') !== null) return true;
488
+ return typeof node.closest === 'function' && node.closest('[data-composer-seat]') !== null;
489
+ } catch (e) { return false; }
490
+ }
491
+ function pgRelevant(records) {
492
+ try {
493
+ for (const record of records) {
494
+ if (record.type === 'attributes') {
495
+ if (record.attributeName === 'aria-label') {
496
+ if (pgTriggerLabel(record.target && record.target.getAttribute && record.target.getAttribute('aria-label'))) return true;
497
+ }
498
+ if (record.attributeName === 'role' && pgTouchesSurface(record.target)) return true;
499
+ continue;
500
+ }
501
+ // React 更新既有文本节点走 nodeValue(characterData 突变),
502
+ // 不产生 childList 记录 —— en 下必须监听它才能把被改回的中文换回来
503
+ if (record.type === 'characterData') {
504
+ if (pgActiveLang() === 'en') return true;
505
+ continue;
506
+ }
507
+ if (record.type !== 'childList') continue;
508
+ // en 语言下需要全局扫描(设置页行、弹层菜单等不位于 composer seat)
509
+ if (pgActiveLang() === 'en') return true;
510
+ if (pgInsideComposer(record.target)) return true;
511
+ for (const node of record.addedNodes || []) { if (pgTouchesSurface(node)) return true; }
512
+ for (const node of record.removedNodes || []) { if (pgTouchesSurface(node)) return true; }
513
+ }
514
+ } catch (e) {}
515
+ return false;
516
+ }
517
+ function pgCustomItem(container) {
518
+ try {
519
+ const labels = new Set();
520
+ let custom = undefined;
521
+ // 兼容各代权限选择器结构:旧 Menu(menuitem*)、0.1.2 popupSelect
522
+ // (div[role="option"])。label 一律优先取首个非空 SPAN 子元素,
523
+ // 排除 detail/check 兄弟节点,避免文本并入误判;无 SPAN 时回退整项文本。
524
+ const items = Array.from(container.querySelectorAll('[role="menuitem"], [role="menuitemradio"], [role="menuitemcheckbox"], [role="option"]'));
525
+ for (const item of items) {
526
+ let label = '';
527
+ // 优先取第一个非空 SPAN 子元素(label),排除 detail/check 兄弟,
528
+ // 避免文本并入误判;无 SPAN 时回退整项文本(旧 Menu 按钮结构)
529
+ for (const child of Array.from(item.children || [])) {
530
+ if (child.tagName !== 'SPAN') continue;
531
+ const t = (child.textContent || '').trim();
532
+ if (t) { label = t; break; }
533
+ }
534
+ if (!label) label = item.textContent ? item.textContent.trim() : '';
535
+ if (!label || labels.has(label)) continue;
536
+ labels.add(label);
537
+ if (label === PG_CUSTOM_LABEL || label === PG_NAME_EN || label.indexOf(PG_CUSTOM_LABEL) !== -1 || label.indexOf(PG_NAME_EN) !== -1) custom = item;
538
+ }
539
+ if (!custom) return undefined;
540
+ let hits = 0;
541
+ for (const want of PG_PERMISSION_LABELS) {
542
+ if (labels.has(want)) hits++;
543
+ }
544
+ // 至少命中 3 项预设标签才认定是权限选择器(宽容标签变体;其他菜单
545
+ // 不可能同时含有自定义审查/多项预设名,误伤概率为零)
546
+ return hits >= 3 ? custom : undefined;
547
+ } catch (e) { return undefined; }
548
+ }
549
+ // ── 菜单项徽标:向 label span 注入内联样式图标元素。不依赖平台按钮布局
550
+ // (::before 在 flex/grid 布局下可能不可见),inline-block 与文本同行;
551
+ // React 不管理未知子元素,重渲染/切语言不丢失,卸载时统一清理。──
552
+ const pgMenuIcons = new Set();
553
+ const PG_MENU_ICON_ATTR = 'data-pg-menu-icon';
554
+ function pgEnsureMenuIcon(document, item) {
555
+ try {
556
+ let label = null;
557
+ for (const child of Array.from(item.children || [])) {
558
+ if (child.tagName !== 'SPAN') continue;
559
+ const t = (child.textContent || '').trim();
560
+ if (t === PG_CUSTOM_LABEL || t === PG_NAME_EN || t.indexOf(PG_CUSTOM_LABEL) !== -1 || t.indexOf(PG_NAME_EN) !== -1) { label = child; break; }
561
+ }
562
+ if (!label || label.querySelector('[' + PG_MENU_ICON_ATTR + ']')) return;
563
+ const icon = document.createElement('span');
564
+ icon.setAttribute(PG_MENU_ICON_ATTR, '1');
565
+ icon.setAttribute('aria-hidden', 'true');
566
+ icon.style.cssText = 'display:inline-block;flex:none;width:14px;height:14px;margin-right:6px;vertical-align:-2px;background-color:currentColor;-webkit-mask:' + PG_MASK + ' center/contain no-repeat;mask:' + PG_MASK + ' center/contain no-repeat;';
567
+ label.insertBefore(icon, label.firstChild);
568
+ pgMenuIcons.add(icon);
569
+ } catch (e) {}
570
+ }
571
+ function pgScanIcons(document) {
572
+ try {
573
+ // 菜单/选择器是 portal 渲染(不位于触发器旁边),全文档扫描所有
574
+ // role=menu(旧 Menu 组件)与 role=listbox(0.1.2 popupSelect);
575
+ // pgCustomItem 要求多项预设齐全才认,误伤其他菜单的概率为零
576
+ for (const menu of Array.from(document.querySelectorAll('[role="menu"], [role="listbox"]'))) {
577
+ const customItem = pgCustomItem(menu);
578
+ if (customItem !== undefined) pgEnsureMenuIcon(document, customItem);
579
+ }
580
+ } catch (e) {}
581
+ }
582
+ function pgScanAll(document) {
583
+ try { pgScanText(document); } catch (e) {}
584
+ try { pgScanIcons(document); } catch (e) {}
585
+ }
586
+ function pgInstallCompat() {
587
+ const document = window.document;
588
+ const Observer = window.MutationObserver;
589
+ const requestFrame = window.requestAnimationFrame;
590
+ const cancelFrame = window.cancelAnimationFrame;
591
+ if (!document || typeof Observer !== 'function' || typeof requestFrame !== 'function') return pgNoop;
592
+ let stopped = false;
593
+ let frame;
594
+ let styleNode = null;
595
+ try {
596
+ styleNode = document.createElement('style');
597
+ styleNode.setAttribute(PG_STYLE, 'client');
598
+ styleNode.textContent = PG_CSS;
599
+ (document.head || document.documentElement).appendChild(styleNode);
600
+ } catch (e) { styleNode = null; }
601
+ const scan = () => {
602
+ try { pgScanAll(document); } catch (e) {}
603
+ };
604
+ const runScan = () => {
605
+ frame = undefined;
606
+ if (stopped) return;
607
+ try { scan(); } catch (e) {}
608
+ };
609
+ const schedule = () => {
610
+ if (stopped || frame !== undefined) return;
611
+ try { frame = requestFrame.call(window, runScan); } catch (e) {}
612
+ };
613
+ let observer;
614
+ try {
615
+ observer = new Observer((records) => {
616
+ try { if (pgRelevant(records)) schedule(); } catch (e) {}
617
+ });
618
+ observer.observe(document.documentElement, {
619
+ subtree: true,
620
+ childList: true,
621
+ attributes: true,
622
+ characterData: true,
623
+ attributeFilter: ['aria-label', 'role'],
624
+ });
625
+ schedule();
626
+ } catch (e) {
627
+ try { if (observer) observer.disconnect(); } catch (e2) {}
628
+ if (styleNode && styleNode.parentElement) { try { styleNode.remove(); } catch (e2) {} }
629
+ return pgNoop;
630
+ }
631
+ // 事件驱动的兜底调度:点击/键盘聚焦任何元素后,下一帧扫描一次。
632
+ // 菜单由 portal 挂载,点击触发器的下一帧菜单必然已在 DOM 中 ——
633
+ // 即使 MutationObserver 失效,菜单项图标注入与文本交换仍然可靠。
634
+ const onInteraction = () => { schedule(); };
635
+ try {
636
+ document.addEventListener('click', onInteraction, true);
637
+ document.addEventListener('focusin', onInteraction, true);
638
+ } catch (e) {}
639
+ let offLocale = null;
640
+ try {
641
+ if (LC && LC.locale && typeof LC.locale.subscribe === 'function') {
642
+ offLocale = LC.locale.subscribe(() => schedule());
643
+ }
644
+ } catch (e) {}
645
+ return () => {
646
+ if (stopped) return;
647
+ stopped = true;
648
+ try { observer.disconnect(); } catch (e) {}
649
+ try { document.removeEventListener('click', onInteraction, true); } catch (e) {}
650
+ try { document.removeEventListener('focusin', onInteraction, true); } catch (e) {}
651
+ if (offLocale) { try { offLocale(); } catch (e) {} }
652
+ if (frame !== undefined && typeof cancelFrame === 'function') {
653
+ try { cancelFrame.call(window, frame); } catch (e) {}
654
+ }
655
+ frame = undefined;
656
+ for (const icon of Array.from(pgMenuIcons)) { try { icon.remove(); } catch (e) {} }
657
+ pgMenuIcons.clear();
658
+ if (styleNode && styleNode.parentElement) { try { styleNode.remove(); } catch (e) {} }
659
+ };
660
+ }
661
+
662
+ const CATS = ['directory', 'command', 'read', 'edit', 'subagent', 'doomloop'];
663
+ const EXC_CATS = ['directory', 'command', 'read', 'edit'];
664
+ // 例外列表超过该数量默认折叠(展开/折叠按钮在标题行右侧)
665
+ const EXC_COLLAPSE_THRESHOLD = 6;
666
+ // 文件对比缓存:按审批 id 缓存 /permgate/file-diff 结果(弹窗详情与右侧抽屉共用)
667
+ const diffCache = new Map();
668
+ const diffFetching = new Set();
669
+ const ALL_MODES = ['ask', 'allow', 'deny', 'inherit'];
670
+ const MODES = ['ask', 'allow', 'deny'];
671
+ const MODE_COLORS = { ask: '#e65100', allow: '#2e7d32', deny: '#c62828', inherit: '#888' };
672
+ const QUICK_PRESET = ['web_search', 'skill', 'grep', 'glob', 'web_fetch'];
673
+
674
+ // ── 中英文适配:字典 + locale 绑定(跟随 dsh 语言设置自动切换)──────────
675
+ let LC = null; // apply 时注入的 ctx
676
+ const I18N = {
677
+ zh: {
678
+ 'app.title': '权限审批',
679
+ 'app.request': '{tool} 请求执行',
680
+ 'app.intent': '意图说明',
681
+ 'app.args': '参数',
682
+ 'app.cand.head': '添加项目规则(当前项目)',
683
+ 'app.cand.hint': '点亮条目后点击「允许 / 拒绝」,选中的条目自动加入当前项目例外,之后不再询问。',
684
+ 'app.allow': '允许',
685
+ 'app.deny': '拒绝',
686
+ 'app.allowOne': '√ 允许',
687
+ 'app.denyOne': '× 拒绝',
688
+ 'app.denyReason': '拒绝意见',
689
+ 'app.denyReasonPh': '选填:填写后作为拒绝理由告知 agent(默认为「用户拒绝」)',
690
+ 'app.confirmDeny': '确认拒绝',
691
+ 'app.detail': '详情',
692
+ 'app.diffNew': '新增',
693
+ 'app.diffMod': '修改',
694
+ 'app.diffMore': '其余 {n} 行',
695
+ 'app.diffLoading': '加载中…',
696
+ 'app.diffErr': '无法生成对比',
697
+ 'app.uiErr': '界面渲染出错',
698
+ 'app.eolNote': '行尾已按 LF 归一化匹配(磁盘为 CRLF/CR 而参数为 LF):预览仅为意图展示,实际编辑可能因行尾不一致失败',
699
+ 'app.diffGapShow': '显示 {n} 行未更改',
700
+ 'app.diffGapHide': '隐藏 {n} 行',
701
+ 'app.diffGapMore': '… {n} 行未更改',
702
+ 'app.diffCopy': '复制',
703
+ 'app.diffCopied': '已复制',
704
+ 'app.diffExpand': '展开',
705
+ 'app.diffCollapse': '收缩',
706
+ 'app.diffRead': '读取',
707
+ 'app.diffLines': '{n} 行',
708
+ 'app.diffTopMore': '… 上方还有 {n} 行',
709
+ 'app.diffBottomMore': '… 下方还有更多行',
710
+ 'app.diffClose': '关闭',
711
+ 'app.openFile': '打开文件',
712
+ 'app.editOpened': '已用默认编辑器打开',
713
+ 'app.diffResize': '拖动调整宽度',
714
+ 'dock.title': '● 权限网关',
715
+ 'settings.title': '权限网关',
716
+ 'dock.refresh': '刷新',
717
+ 'dock.loading': '加载中…',
718
+ 'cat.directory': '目录访问(工作区外)',
719
+ 'cat.command': '执行命令',
720
+ 'cat.read': '读取文件',
721
+ 'cat.edit': '编辑文件',
722
+ 'cat.subagent': '启动子代理',
723
+ 'cat.doomloop': '重复操作(Doom Loop)',
724
+ 'catS.directory': '目录',
725
+ 'catS.command': '命令',
726
+ 'catS.read': '读取',
727
+ 'catS.edit': '编辑',
728
+ 'catS.subagent': '子代理',
729
+ 'catS.doomloop': '循环',
730
+ 'mode.ask': '询问',
731
+ 'mode.allow': '允许',
732
+ 'mode.deny': '拒绝',
733
+ 'mode.inherit': '继承全局',
734
+ 'panel.status': '状态',
735
+ 'panel.sessionPerm': '当前会话权限',
736
+ 'panel.customReview': '自定义审查',
737
+ 'panel.none': '未选择',
738
+ 'panel.active': '权限网关审查生效中',
739
+ 'panel.inactive': '权限网关未生效:请在会话的权限选择中选择「自定义审查」',
740
+ 'panel.sandboxEff': '底层沙箱(生效)',
741
+ 'panel.sandboxDesc.ww': '(文件操作限制在工作区内)',
742
+ 'panel.sandboxDesc.fa': '(文件操作不受工作区限制)',
743
+ 'panel.effCats': '生效分类默认:',
744
+ 'panel.config': '配置文件:',
745
+ 'panel.project': '项目:',
746
+ 'panel.stats': '最近统计:拦截 {d} · 审批 {a}',
747
+ 'panel.loadErr': '加载错误:',
748
+ 'panel.saveErr': '保存错误:',
749
+ 'panel.tabGlobal': '全局',
750
+ 'panel.tabProject': '项目',
751
+ 'panel.editGlobal': '正在编辑全局设置(所有项目的默认)。',
752
+ 'panel.editProject': '正在编辑当前项目({p}),项目设置优先于全局。',
753
+ 'panel.sandbox': '底层沙箱',
754
+ 'sandbox.ww': 'Workspace Write',
755
+ 'sandbox.fa': 'Full access',
756
+ 'sandbox.inherit': '继承全局',
757
+ 'sandbox.desc.ww': '文件操作限制在工作区内',
758
+ 'sandbox.desc.fa': '文件操作不受工作区限制',
759
+ 'sandbox.desc.inherit': '跟随全局设置(当前全局:{g})',
760
+ 'panel.quick': '快捷工具(其他工具快速设置)',
761
+ 'panel.quickHint': '适用于网页搜索、技能加载、grep/glob 等未归类的工具;改动即时生效。',
762
+ 'panel.quickAdd': '新增工具名(如 todo_write,支持通配)',
763
+ 'panel.quickAddBtn': '添加(默认允许)',
764
+ 'panel.rules': '自定义规则(通用匹配)',
765
+ 'panel.rulesHint': '优先级:自定义规则 > 分类例外 > 分类默认/快捷工具。',
766
+ 'panel.actionDeny': 'deny 拒绝',
767
+ 'panel.actionAsk': 'ask 审批',
768
+ 'panel.actionAllow': 'allow 放行',
769
+ 'panel.toolPh': 'tool 通配(如 cordis_*)',
770
+ 'panel.pathPh': 'path glob',
771
+ 'panel.argsPh': 'args 子串',
772
+ 'panel.reasonPh': '原因(可选)',
773
+ 'panel.addToGlobal': '添加到全局',
774
+ 'panel.addToProject': '添加到项目',
775
+ 'panel.ruleList': '{t}规则({n})',
776
+ 'panel.noRules': '暂无规则',
777
+ 'panel.decisions': '最近决策',
778
+ 'panel.noDecisions': '暂无决策记录',
779
+ 'panel.reload': '重新加载配置文件',
780
+ 'panel.openConfig': '打开',
781
+ 'panel.openConfigDone': '已用默认编辑器打开配置文件',
782
+ 'panel.saved': '已保存',
783
+ 'panel.savedToGlobal': '已保存到全局',
784
+ 'panel.savedToProject': '已保存到当前项目',
785
+ 'panel.all': '(全部)',
786
+ 'panel.sandboxSaved': '底层沙箱已保存({t}):{v}',
787
+ 'panel.needValue': '请先输入例外匹配',
788
+ 'panel.needTool': '请输入工具名',
789
+ 'panel.excCount': '例外({n})',
790
+ 'panel.excCmdHint': ':匹配命令子串/glob,优先于分类默认',
791
+ 'panel.excPathHint': ':路径 glob,优先于分类默认',
792
+ 'panel.excNone': '无例外',
793
+ 'panel.excExpand': '展开',
794
+ 'panel.excCollapse': '折叠',
795
+ 'panel.excListLink': '例外列表',
796
+ 'panel.excUnit': '条',
797
+ 'panel.excPh': '命令子串/glob',
798
+ 'panel.excPathPh': '路径 glob',
799
+ 'panel.excReason': '拒绝原因',
800
+ 'panel.excReasonPh': '选填:拒绝时显示的提示',
801
+ 'panel.addExc': '添加例外',
802
+ 'panel.confirmDel': '确认删除?',
803
+ 'panel.cancel': '取消',
804
+ 'panel.del': '删除',
805
+ 'panel.allow': '允许',
806
+ 'panel.deny': '拒绝',
807
+ },
808
+ en: {
809
+ 'app.title': 'Permission Review',
810
+ 'app.request': '{tool} requests to run',
811
+ 'app.intent': 'Intent',
812
+ 'app.args': 'Arguments',
813
+ 'app.cand.head': 'Add project rule (current project)',
814
+ 'app.cand.hint': 'Toggle items, then click Allow / Deny to add them to the current project exceptions permanently.',
815
+ 'app.allow': 'Allow',
816
+ 'app.deny': 'Deny',
817
+ 'app.allowOne': '√ Allow',
818
+ 'app.denyOne': '× Deny',
819
+ 'app.denyReason': 'Rejection reason',
820
+ 'app.denyReasonPh': 'Optional: sent to the agent as the denial reason (default: User denied)',
821
+ 'app.confirmDeny': 'Confirm deny',
822
+ 'app.detail': 'Details',
823
+ 'app.diffNew': 'New file',
824
+ 'app.diffMod': 'Modified',
825
+ 'app.diffMore': '{n} more lines',
826
+ 'app.diffLoading': 'Loading…',
827
+ 'app.diffErr': 'Cannot build comparison',
828
+ 'app.uiErr': 'UI render error',
829
+ 'app.eolNote': 'Line endings normalized to LF for matching (CRLF/CR file with LF args): preview is indicative only; the actual edit may fail on line-ending mismatch',
830
+ 'app.diffGapShow': 'Show {n} unchanged lines',
831
+ 'app.diffGapHide': 'Hide {n} lines',
832
+ 'app.diffGapMore': '… {n} unchanged lines',
833
+ 'app.diffCopy': 'Copy',
834
+ 'app.diffCopied': 'Copied',
835
+ 'app.diffExpand': 'Expand',
836
+ 'app.diffCollapse': 'Collapse',
837
+ 'app.diffRead': 'Read',
838
+ 'app.diffLines': '{n} lines',
839
+ 'app.diffTopMore': '… {n} more lines above',
840
+ 'app.diffBottomMore': '… more lines below',
841
+ 'app.diffClose': 'Close',
842
+ 'app.openFile': 'Open File',
843
+ 'app.editOpened': 'Opened with default editor',
844
+ 'app.diffResize': 'Drag to resize',
845
+ 'dock.title': '● Permission Gate',
846
+ 'settings.title': 'Permissions',
847
+ 'dock.refresh': 'Refresh',
848
+ 'dock.loading': 'Loading…',
849
+ 'cat.directory': 'Directory access (outside workspace)',
850
+ 'cat.command': 'Run command',
851
+ 'cat.read': 'Read file',
852
+ 'cat.edit': 'Edit file',
853
+ 'cat.subagent': 'Spawn subagent',
854
+ 'cat.doomloop': 'Doom Loop',
855
+ 'catS.directory': 'Dir',
856
+ 'catS.command': 'Cmd',
857
+ 'catS.read': 'Read',
858
+ 'catS.edit': 'Edit',
859
+ 'catS.subagent': 'Sub',
860
+ 'catS.doomloop': 'Loop',
861
+ 'mode.ask': 'Ask',
862
+ 'mode.allow': 'Allow',
863
+ 'mode.deny': 'Deny',
864
+ 'mode.inherit': 'Inherit global',
865
+ 'panel.status': 'Status',
866
+ 'panel.sessionPerm': 'Current session permission',
867
+ 'panel.customReview': 'Custom Review',
868
+ 'panel.none': 'None',
869
+ 'panel.active': 'Permission gate is active',
870
+ 'panel.inactive': 'Permission gate inactive: select "Custom Review" in the session permission picker',
871
+ 'panel.sandboxEff': 'Sandbox (effective)',
872
+ 'panel.sandboxDesc.ww': '(file operations restricted to the workspace)',
873
+ 'panel.sandboxDesc.fa': '(file operations are not workspace-restricted)',
874
+ 'panel.effCats': 'Effective category defaults: ',
875
+ 'panel.config': 'Config: ',
876
+ 'panel.project': 'Project: ',
877
+ 'panel.stats': 'Recent: {d} denied · {a} asked',
878
+ 'panel.loadErr': 'Load error: ',
879
+ 'panel.saveErr': 'Save error: ',
880
+ 'panel.tabGlobal': 'Global',
881
+ 'panel.tabProject': 'Project',
882
+ 'panel.editGlobal': 'Editing global settings (default for all projects).',
883
+ 'panel.editProject': 'Editing current project ({p}); project settings override global.',
884
+ 'panel.sandbox': 'Sandbox',
885
+ 'sandbox.ww': 'Workspace Write',
886
+ 'sandbox.fa': 'Full access',
887
+ 'sandbox.inherit': 'Inherit global',
888
+ 'sandbox.desc.ww': 'File operations are restricted to the workspace',
889
+ 'sandbox.desc.fa': 'File operations are not workspace-restricted',
890
+ 'sandbox.desc.inherit': 'Follows global (currently: {g})',
891
+ 'panel.quick': 'Quick tools (other tools)',
892
+ 'panel.quickHint': 'For uncategorized tools like web search, skills, grep/glob; changes apply immediately.',
893
+ 'panel.quickAdd': 'New tool name (e.g. todo_write, wildcards allowed)',
894
+ 'panel.quickAddBtn': 'Add (allow by default)',
895
+ 'panel.rules': 'Custom rules (generic matching)',
896
+ 'panel.rulesHint': 'Priority: custom rules > category exceptions > category default / quick tools.',
897
+ 'panel.actionDeny': 'deny Deny',
898
+ 'panel.actionAsk': 'ask Ask',
899
+ 'panel.actionAllow': 'allow Allow',
900
+ 'panel.toolPh': 'tool wildcard (e.g. cordis_*)',
901
+ 'panel.pathPh': 'path glob',
902
+ 'panel.argsPh': 'args substring',
903
+ 'panel.reasonPh': 'reason (optional)',
904
+ 'panel.addToGlobal': 'Add to Global',
905
+ 'panel.addToProject': 'Add to Project',
906
+ 'panel.ruleList': '{t} rules ({n})',
907
+ 'panel.noRules': 'No rules',
908
+ 'panel.decisions': 'Recent decisions',
909
+ 'panel.noDecisions': 'No decisions yet',
910
+ 'panel.reload': 'Reload config file',
911
+ 'panel.openConfig': 'Open',
912
+ 'panel.openConfigDone': 'Opened with default editor',
913
+ 'panel.saved': 'Saved',
914
+ 'panel.sandboxSaved': 'Sandbox saved ({t}): {v}',
915
+ 'panel.needValue': 'Enter a match value first',
916
+ 'panel.needTool': 'Enter a tool name',
917
+ 'panel.excCount': 'Exceptions ({n})',
918
+ 'panel.excCmdHint': ': command substring/glob, overrides category default',
919
+ 'panel.excPathHint': ': path glob, overrides category default',
920
+ 'panel.excNone': 'No exceptions',
921
+ 'panel.excExpand': 'Expand',
922
+ 'panel.excCollapse': 'Collapse',
923
+ 'panel.excListLink': 'Exception list',
924
+ 'panel.excUnit': 'items',
925
+ 'panel.excPh': 'command substring/glob',
926
+ 'panel.excPathPh': 'path glob',
927
+ 'panel.excReason': 'Deny reason',
928
+ 'panel.excReasonPh': 'optional: shown when denied',
929
+ 'panel.addExc': 'Add exception',
930
+ 'panel.confirmDel': 'Confirm delete?',
931
+ 'panel.cancel': 'Cancel',
932
+ 'panel.del': 'Delete',
933
+ 'panel.allow': 'Allow',
934
+ 'panel.deny': 'Deny',
935
+ },
936
+ };
937
+ let T = (key) => (I18N.zh[key] !== undefined ? I18N.zh[key] : key);
938
+ function useLocaleTick() {
939
+ const [, setTick] = React.useState(0);
940
+ React.useEffect(() => {
941
+ if (!LC || !LC.locale || typeof LC.locale.subscribe !== 'function') return undefined;
942
+ return LC.locale.subscribe(() => setTick((v) => v + 1));
943
+ }, []);
944
+ return T;
945
+ }
946
+ function catLabel(c) { return T('cat.' + c); }
947
+ function catShort(c) { return T('catS.' + c); }
948
+ function modeLabel(m) { return T('mode.' + m); }
949
+ const FONT = 'system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif';
950
+ const card = { border: '1px solid rgba(128,128,128,0.35)', borderRadius: 8, padding: 12, marginBottom: 12 };
951
+ const rowStyle = { display: 'flex', alignItems: 'center', gap: 8, padding: '6px 0', borderBottom: '1px solid rgba(128,128,128,0.15)', flexWrap: 'wrap' };
952
+ const small = { fontSize: 12, color: 'rgba(128,128,128,0.95)', marginRight: 6 };
953
+ const h = () => ({ fontSize: 13, fontWeight: 600, margin: '0 0 8px' });
954
+
955
+ function ApprovalOverlay(props) {
956
+ const [pending, setPending] = React.useState([]);
957
+ const [busyId, setBusyId] = React.useState(null);
958
+ const [sel, setSel] = React.useState({});
959
+ const [openDetail, setOpenDetail] = React.useState({});
960
+ const [openArgs, setOpenArgs] = React.useState({});
961
+ const [denyText, setDenyText] = React.useState({});
962
+ const [denyMode, setDenyMode] = React.useState({});
963
+ const [, setTick] = React.useState(0);
964
+ useLocaleTick();
965
+ const poll = () => {
966
+ call('permgate:pending', {}).then((r) => setPending(Array.isArray(r) ? r : [])).catch(() => {});
967
+ };
968
+ React.useEffect(() => {
969
+ poll();
970
+ const off = subscribeEvents((ev) => {
971
+ if (ev.type === 'pending' || ev.type === 'refresh') poll();
972
+ });
973
+ return () => off();
974
+ }, []);
975
+ // 懒加载对比数据:详情默认展开的编辑/写入审批按需拉取(缓存按审批 id 复用)。
976
+ // 注意:必须在 `if (!pending.length) return null` 提前返回之前声明(hooks 顺序约束)。
977
+ React.useEffect(() => {
978
+ // 缓存随待审批池淘汰:已结算/已消失的审批 id 立即清理,避免长期会话无界增长
979
+ const aliveIds = new Set(pending.map((p) => p.id));
980
+ for (const key of diffCache.keys()) if (!aliveIds.has(key)) diffCache.delete(key);
981
+ for (const key of diffFetching) if (!aliveIds.has(key)) diffFetching.delete(key);
982
+ for (const p of pending) {
983
+ if (!p.hasDiff || diffCache.has(p.id) || diffFetching.has(p.id)) continue
984
+ const open = openDetail[p.id] === undefined ? true : openDetail[p.id] === true
985
+ if (!open) continue
986
+ diffFetching.add(p.id)
987
+ schedulePrismIdle()
988
+ call('permgate:file-diff', { id: p.id }).then((r) => {
989
+ diffCache.set(p.id, (r && typeof r === 'object') ? r : { ok: false, error: '' })
990
+ diffFetching.delete(p.id)
991
+ setTick((v) => v + 1)
992
+ }).catch(() => {
993
+ diffCache.set(p.id, { ok: false, error: '' })
994
+ diffFetching.delete(p.id)
995
+ setTick((v) => v + 1)
996
+ })
997
+ }
998
+ });
999
+ const decide = (id, action, rules, reason) => {
1000
+ setBusyId(id);
1001
+ call('permgate:decide', { id, action, rules, reason: reason || undefined }).then(poll).catch(() => {}).then(() => setBusyId(null));
1002
+ };
1003
+ if (!pending.length) return null;
1004
+ const pick = (candId, v) => {
1005
+ const next = Object.assign({}, sel);
1006
+ if (next[candId] === v) delete next[candId];
1007
+ else next[candId] = v;
1008
+ setSel(next);
1009
+ };
1010
+ const submit = (p, action) => {
1011
+ const rules = (p.candidates || []).filter((c) => sel[c.id]).map((c) => ({ value: c.value, kind: c.kind, decision: sel[c.id] }));
1012
+ decide(p.id, action, rules);
1013
+ };
1014
+ // 两段式拒绝:确认阶段提交(携带意见)或取消返回
1015
+ const confirmDeny = (p) => {
1016
+ const reason = (denyText[p.id] || '').trim();
1017
+ setDenyMode(Object.assign({}, denyMode, { [p.id]: false }));
1018
+ decide(p.id, 'deny', [], reason || undefined);
1019
+ };
1020
+ const cancelDeny = (p) => setDenyMode(Object.assign({}, denyMode, { [p.id]: false }));
1021
+ const radio = (p, c, v, label, cls) => React.createElement('button', {
1022
+ className: 'pg-radio' + (sel[c.id] === v ? ' ' + cls : ''),
1023
+ disabled: busyId === p.id,
1024
+ onClick: () => pick(c.id, v),
1025
+ }, label);
1026
+ // 编辑/写入审批(有 diff):详情默认展开、参数默认收起;无 diff 时参数照常显示
1027
+ const detailOpen = (p) => (openDetail[p.id] === undefined ? !!p.hasDiff : openDetail[p.id]);
1028
+ const argsOpen = (p) => (openArgs[p.id] === undefined ? !p.hasDiff : openArgs[p.id]);
1029
+ const toggle = (map, setMap, p, v) => setMap(Object.assign({}, map, { [p.id]: v }));
1030
+ // 点击文件名 → 打开右侧对比抽屉(父组件 OverlayRoot 持有 pin 状态)
1031
+ const openFile = (p, file) => {
1032
+ if (props && typeof props.onOpenFile === 'function') props.onOpenFile(p.id, file);
1033
+ };
1034
+ const argsBox = (p) => React.createElement('div', { className: 'pg-args' },
1035
+ React.createElement('span', { className: 'pg-args-tag' }, T('app.args')),
1036
+ (p.argLines && p.argLines.length)
1037
+ ? p.argLines.map((l, i) => React.createElement('div', { key: i, className: 'pg-args-row' },
1038
+ React.createElement('span', { className: 'pg-args-label' }, l.label + ':'),
1039
+ l.path ? React.createElement('span', { className: 'pg-path-link', title: l.path, onClick: () => openFile(p, l.path) }, l.value)
1040
+ : React.createElement('span', { className: 'pg-args-val' }, l.value),
1041
+ ))
1042
+ : React.createElement('span', null, p.args),
1043
+ );
1044
+ return React.createElement('div', null,
1045
+ pending.map((p) => React.createElement('div', { key: p.id, className: 'pg-modal' },
1046
+ React.createElement('div', { className: 'pg-modal-title' }, T('app.title')),
1047
+ React.createElement('div', { className: 'pg-modal-req' }, React.createElement('b', null, p.tool), ' ', T('app.request').replace('{tool}', '')),
1048
+ p.reason ? React.createElement('div', { className: 'pg-modal-body' }, p.reason) : null,
1049
+ p.intent ? React.createElement('div', { className: 'pg-intent' },
1050
+ React.createElement('span', { className: 'pg-intent-tag' }, T('app.intent')),
1051
+ React.createElement('span', null, p.intent),
1052
+ ) : null,
1053
+ (p.args || (p.argLines && p.argLines.length)) ? (p.hasDiff
1054
+ ? React.createElement('div', null,
1055
+ React.createElement('button', { className: 'pg-link', onClick: () => toggle(openArgs, setOpenArgs, p, !argsOpen(p)) },
1056
+ (argsOpen(p) ? '▾ ' : '▸ ') + T('app.args'),
1057
+ ),
1058
+ argsOpen(p) ? argsBox(p) : null,
1059
+ )
1060
+ : argsBox(p)) : null,
1061
+ p.hasDiff ? React.createElement('div', null,
1062
+ React.createElement('button', { className: 'pg-link', onClick: () => toggle(openDetail, setOpenDetail, p, !detailOpen(p)) },
1063
+ (detailOpen(p) ? '▾ ' : '▸ ') + T('app.detail'),
1064
+ ),
1065
+ detailOpen(p) ? (() => {
1066
+ const c = diffCache.get(p.id)
1067
+ if (!c) return React.createElement('div', { className: 'pg2-load' }, T('app.diffLoading'))
1068
+ if (!c.ok) return React.createElement('div', { className: 'pg2-err' }, (c.error || T('app.diffErr')))
1069
+ return c.kind === 'read'
1070
+ ? React.createElement(PGErrorBoundary, { resetKey: payloadKey(c), fallback: T('app.diffErr') }, React.createElement(ReadBlockMemo, { data: c, onOpenFile: (f) => openFile(p, f) }))
1071
+ : React.createElement(PGErrorBoundary, { resetKey: payloadKey(c), fallback: T('app.diffErr') }, React.createElement(DiffBlockMemo, { data: c, onOpenFile: (f) => openFile(p, f), changesOnly: true }))
1072
+ })() : null,
1073
+ ) : null,
1074
+ (p.candidates || []).length ? React.createElement('div', null,
1075
+ React.createElement('div', { className: 'pg-cand-head' }, T('app.cand.head')),
1076
+ React.createElement('div', { className: 'pg-cand-hint' }, T('app.cand.hint')),
1077
+ (p.candidates || []).map((c) => React.createElement('div', { key: c.id, className: 'pg-cand' },
1078
+ React.createElement('span', { className: 'pg-cand-label' }, c.label),
1079
+ radio(p, c, 'allow', T('app.allowOne'), 'pg-radio-allow'),
1080
+ radio(p, c, 'deny', T('app.denyOne'), 'pg-radio-deny'),
1081
+ )),
1082
+ ) : null,
1083
+ // 两段式拒绝:平时只显示 拒绝/允许 两个按钮;点「拒绝」后展开
1084
+ // 意见输入框 + 「确认拒绝/取消」(Enter 确认、Esc 取消),
1085
+ // 输入框只在真要拒绝时出现,允许仍是一键。
1086
+ denyMode[p.id] ? React.createElement('div', null,
1087
+ React.createElement('div', { style: { margin: '8px 0 4px' } },
1088
+ React.createElement('div', { style: { fontSize: 12, fontWeight: 600, color: 'var(--dsw-alias-label-primary, #24292f)', marginBottom: 4 } }, T('app.denyReason')),
1089
+ React.createElement('input', { className: 'pg-field', autoFocus: true, style: { width: '100%', boxSizing: 'border-box' }, placeholder: T('app.denyReasonPh'), value: denyText[p.id] || '', onChange: (e) => setDenyText(Object.assign({}, denyText, { [p.id]: e.target.value })), onKeyDown: (e) => { if (e.key === 'Enter') { e.preventDefault(); confirmDeny(p); } else if (e.key === 'Escape') { e.preventDefault(); cancelDeny(p); } } }),
1090
+ ),
1091
+ React.createElement('div', { className: 'pg-footer' },
1092
+ React.createElement('button', { className: 'pg-action pg-action-deny', disabled: busyId === p.id, onClick: () => confirmDeny(p) }, T('app.confirmDeny')),
1093
+ React.createElement('button', { className: 'pg-action', disabled: busyId === p.id, onClick: () => cancelDeny(p) }, T('panel.cancel')),
1094
+ ),
1095
+ ) : React.createElement('div', { className: 'pg-footer' },
1096
+ React.createElement('button', { className: 'pg-action pg-action-deny', disabled: busyId === p.id, onClick: () => setDenyMode(Object.assign({}, denyMode, { [p.id]: true })) }, T('app.deny')),
1097
+ React.createElement('button', { className: 'pg-action pg-action-allow', disabled: busyId === p.id, onClick: () => submit(p, 'allow') }, T('app.allow')),
1098
+ ),
1099
+ )),
1100
+ );
1101
+ }
1102
+
1103
+ // ── 文件对比(dsh-file-review 风格 unified diff + 右侧抽屉)─────────
1104
+ // 弹窗内联「详情」与右侧抽屉共用 DiffBlock/ReadBlock;数据来自
1105
+ // /permgate/file-diff(按审批 id 取,diffCache 缓存复用)。
1106
+ function diffOpsText(file, ops) {
1107
+ const out = [file];
1108
+ for (const op of ops || []) {
1109
+ if (op.t === 'g') {
1110
+ if (op.lines) { for (const l of op.lines) out.push(' ' + l.s) }
1111
+ else out.push('…');
1112
+ } else if (op.t === 'a') out.push('+ ' + op.s);
1113
+ else if (op.t === 'd') out.push('- ' + op.s);
1114
+ else out.push(' ' + op.s);
1115
+ }
1116
+ return out.join('\n');
1117
+ }
1118
+ function DiffBlock({ data, onOpenFile, onCollapse, changesOnly }) {
1119
+ const [copied, setCopied] = React.useState(false);
1120
+ const [gaps, setGaps] = React.useState({});
1121
+ useLocaleTick();
1122
+ // 语法高亮:从 ops 重建 old/new 文本后分块 tokenize,按真实文件行号 Map 取
1123
+ // 对应行的 HTML(edit 窗口化 diff 的 ops 行号是真实文件行号,窗口 base 可能 >1;
1124
+ // changesOnly 过滤后文本非连续,不能再用「行号-首行」偏移索引)。
1125
+ // 重建结果按 data 对象引用 + 视图模式缓存(diffHlCache:抽屉/弹窗共用同一
1126
+ // payload,但重建内容不同——changesOnly 只含改动行,full 含全部 gap/ctx 行)。
1127
+ const hlMode = changesOnly ? 'co' : 'full';
1128
+ const [hl, setHl] = React.useState(() => {
1129
+ const c = data ? diffHlCache.get(data) : null;
1130
+ return (c && c[hlMode]) || null;
1131
+ });
1132
+ React.useEffect(() => {
1133
+ ensurePrism();
1134
+ const lang = prismLangFor(data.file);
1135
+ if (data.fallback || !lang) { setHl(null); return undefined }
1136
+ const cached = data ? diffHlCache.get(data) : null;
1137
+ if (cached && cached[hlMode]) { setHl(cached[hlMode]); return undefined }
1138
+ // changesOnly(弹窗内联)只渲染改动行:重建文本仅含 a/d 行,不把 gap 携带的
1139
+ // 隐藏行(最多 10 万行)拼进文本,避免对永不展示的内容做拼接与分块 tokenize。
1140
+ const ops = changesOnly
1141
+ ? (data.ops || []).filter((op) => op.t === 'a' || op.t === 'd')
1142
+ : (data.ops || []);
1143
+ const texts = prismTextsFromOps(ops);
1144
+ if (!texts) { setHl(null); return undefined }
1145
+ const hl = {
1146
+ old: { text: texts.oldText, html: null },
1147
+ new: { text: texts.newText, html: null },
1148
+ // 行号 → 重建文本行序号的索引(行号来自 ops,为真实文件行号)
1149
+ oldByNum: new Map(texts.oldNums.map((n, i) => [n, i])),
1150
+ newByNum: new Map(texts.newNums.map((n, i) => [n, i])),
1151
+ };
1152
+ if (data) {
1153
+ const entry = diffHlCache.get(data) || {};
1154
+ entry[hlMode] = hl;
1155
+ diffHlCache.set(data, entry);
1156
+ }
1157
+ setHl(hl);
1158
+ }, [data, hlMode]);
1159
+ // 双侧分块渐进高亮(idle 调度):首渲染纯文本、分块完成后补高亮。
1160
+ const oldHtml = useProgressiveHighlight(hl ? hl.old.text : null, data.file, hl ? hl.old : null);
1161
+ const newHtml = useProgressiveHighlight(hl ? hl.new.text : null, data.file, hl ? hl.new : null);
1162
+ // changesOnly 与抽屉统一走渐进高亮:首渲染纯文本、idle 分块完成后补高亮,
1163
+ // 避免弹窗打开时同步逐行 tokenize 阻塞主线程(maps 就绪前行数按纯文本渲染)。
1164
+ const rowText = (num, side, plain) => {
1165
+ const byNum = hl ? (side === 'old' ? hl.oldByNum : hl.newByNum) : null;
1166
+ const htmlArr = side === 'old' ? oldHtml : newHtml;
1167
+ const idx = (byNum && num !== null && num !== undefined) ? byNum.get(num) : undefined;
1168
+ const html = htmlArr && idx !== undefined ? htmlArr[idx] : undefined;
1169
+ if (html === undefined) return React.createElement('span', { className: 'pg2-text' }, plain);
1170
+ return React.createElement('span', { className: 'pg2-text', dangerouslySetInnerHTML: { __html: html } });
1171
+ };
1172
+ const onCopy = () => {
1173
+ if (copied) return;
1174
+ const text = data.fallback ? (data.lines || []).join('\n') : diffOpsText(data.file, data.ops || []);
1175
+ if (typeof navigator !== 'undefined' && navigator.clipboard) {
1176
+ navigator.clipboard.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1000) }).catch(() => {});
1177
+ }
1178
+ };
1179
+ const kindLabel = data.kind === 'new' ? T('app.diffNew') : T('app.diffMod');
1180
+ const badge = data.kind === 'new' ? '+' : 'M';
1181
+ const copyBtn = React.createElement('button', { className: 'pg2-copy', onClick: onCopy }, copied ? T('app.diffCopied') : T('app.diffCopy'));
1182
+ // 「展开」按钮:仅弹窗内联上下文显示(onOpenFile 存在时),点击打开右侧栏面板
1183
+ const expandBtn = (typeof onOpenFile === 'function')
1184
+ ? React.createElement('button', { className: 'pg2-expand', onClick: () => onOpenFile(data.file), title: T('app.diffExpand') }, T('app.diffExpand'))
1185
+ : null;
1186
+ // 「收缩」按钮:仅在侧边栏面板内显示(onCollapse 存在时),放在复制按钮旁边,点击回到权限弹窗
1187
+ const collapseBtn = (typeof onCollapse === 'function')
1188
+ ? React.createElement('button', { className: 'pg2-expand', onClick: onCollapse, title: T('app.diffCollapse') }, T('app.diffCollapse'))
1189
+ : null;
1190
+ // 行尾归一化提示条:fallback 降级视图同样渲染(该视图下实际 edit 的原始字节
1191
+ // 字面匹配同样可能因 CRLF/CR 失败,提示不应随降级丢失)
1192
+ const eolNote = data.eolNormalized ? React.createElement('div', { className: 'pg2-eolnote', style: { padding: '4px 8px', fontSize: 11, color: 'var(--dsw-alias-state-warning-primary, #9a6700)' } }, T('app.eolNote')) : null;
1193
+ if (data.fallback) {
1194
+ return React.createElement('div', { className: 'pg2-block' },
1195
+ React.createElement('div', { className: 'pg2-header' },
1196
+ React.createElement('span', { className: 'pg2-status', title: kindLabel }, badge),
1197
+ React.createElement('span', { className: 'pg2-path', title: data.file, onClick: () => onOpenFile && onOpenFile(data.file) }, data.file),
1198
+ React.createElement('span', { className: 'pg2-added' }, '+' + data.added),
1199
+ React.createElement('span', { className: 'pg2-removed' }, '-' + data.removed),
1200
+ copyBtn,
1201
+ expandBtn,
1202
+ collapseBtn,
1203
+ ),
1204
+ eolNote,
1205
+ React.createElement('div', { className: 'pg2-body' },
1206
+ (data.lines || []).map((l, i) => React.createElement('div', { key: i, className: l[0] === '+' ? 'pg2-fadd' : l[0] === '-' ? 'pg2-fdel' : 'pg2-fctx' }, l)),
1207
+ ),
1208
+ data.truncated > 0 ? React.createElement('div', { className: 'pg2-foot' }, '… ' + T('app.diffMore').replace('{n}', String(data.truncated))) : null,
1209
+ );
1210
+ }
1211
+ const rows = [];
1212
+ let gi = 0;
1213
+ let ri = 0;
1214
+ for (const op of data.ops || []) {
1215
+ // 权限弹窗内联视图(changesOnly):只显示修改行,不渲染上下文/gap
1216
+ if (changesOnly && op.t !== 'a' && op.t !== 'd') continue
1217
+ if (op.t === 'g') {
1218
+ const id = 'g' + (gi++);
1219
+ const expanded = !!gaps[id];
1220
+ if (op.lines) {
1221
+ rows.push(React.createElement('button', { key: 'r' + (ri++), className: 'pg2-gap', onClick: () => setGaps(Object.assign({}, gaps, { [id]: !expanded })) },
1222
+ (expanded ? '▾ ' : '↕ ') + (expanded ? T('app.diffGapHide') : T('app.diffGapShow')).replace('{n}', String(op.c)),
1223
+ ));
1224
+ if (expanded) {
1225
+ for (const l of op.lines) {
1226
+ rows.push(React.createElement('div', { key: 'r' + (ri++), className: 'pg2-row pg2-ctx' },
1227
+ React.createElement('span', { className: 'pg2-old' }, String(l.o)),
1228
+ React.createElement('span', { className: 'pg2-new' }, String(l.n)),
1229
+ React.createElement('span', { className: 'pg2-sign' }, ' '),
1230
+ rowText(l.o, 'old', l.s),
1231
+ ));
1232
+ }
1233
+ }
1234
+ } else {
1235
+ rows.push(React.createElement('div', { key: 'r' + (ri++), className: 'pg2-gap-more' }, T('app.diffGapMore').replace('{n}', String(op.c))));
1236
+ }
1237
+ } else {
1238
+ const cls = op.t === 'a' ? 'pg2-add' : op.t === 'd' ? 'pg2-del' : 'pg2-ctx';
1239
+ const sign = op.t === 'a' ? '+' : op.t === 'd' ? '-' : ' ';
1240
+ rows.push(React.createElement('div', { key: 'r' + (ri++), className: 'pg2-row ' + cls },
1241
+ React.createElement('span', { className: 'pg2-old' }, op.o === null ? '' : String(op.o)),
1242
+ React.createElement('span', { className: 'pg2-new' }, op.n === null ? '' : String(op.n)),
1243
+ React.createElement('span', { className: 'pg2-sign' }, sign),
1244
+ rowText(op.t === 'a' ? op.n : op.o, op.t === 'a' ? 'new' : 'old', op.s),
1245
+ ));
1246
+ }
1247
+ }
1248
+ return React.createElement('div', { className: 'pg2-block' },
1249
+ React.createElement('div', { className: 'pg2-header' },
1250
+ React.createElement('span', { className: 'pg2-status', title: kindLabel }, badge),
1251
+ React.createElement('span', { className: 'pg2-path', title: data.file, onClick: () => onOpenFile && onOpenFile(data.file) }, data.file),
1252
+ React.createElement('span', { className: 'pg2-added' }, '+' + data.added),
1253
+ React.createElement('span', { className: 'pg2-removed' }, '-' + data.removed),
1254
+ copyBtn,
1255
+ expandBtn,
1256
+ collapseBtn,
1257
+ ),
1258
+ eolNote,
1259
+ React.createElement('div', { className: 'pg2-body' }, rows),
1260
+ );
1261
+ }
1262
+ function ReadBlock({ data, onOpenFile, onCollapse }) {
1263
+ const [copied, setCopied] = React.useState(false);
1264
+ useLocaleTick();
1265
+ const text = data.text || '';
1266
+ // 窗口化读取:行号从 startLine 起算,上下省略时显示灰色提示行
1267
+ const startLine = data.startLine || 1;
1268
+ const topOmitted = data.topOmitted === true;
1269
+ const bottomOmitted = data.bottomOmitted === true;
1270
+ const body = text.endsWith('\n') ? text.slice(0, -1) : text;
1271
+ const lines = body === '' ? [] : body.split('\n');
1272
+ const onCopy = () => {
1273
+ if (copied) return;
1274
+ if (typeof navigator !== 'undefined' && navigator.clipboard) {
1275
+ navigator.clipboard.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1000) }).catch(() => {});
1276
+ }
1277
+ };
1278
+ const rows = [];
1279
+ let ri = 0;
1280
+ // 语法高亮:分块 tokenize 后按行取 HTML(超大文本降级纯文本,防主线程卡顿)。
1281
+ // 高亮结果按 data 引用缓存(readHlCache):抽屉重开/详情重展开(重挂载)
1282
+ // 直接复用已完成的行 HTML,不重复分块 tokenize。
1283
+ const [readHl] = React.useState(() => {
1284
+ if (!data) return null;
1285
+ let c = readHlCache.get(data);
1286
+ if (!c) { c = { text: null, html: null }; readHlCache.set(data, c) }
1287
+ return c;
1288
+ });
1289
+ // 分块渐进高亮(idle 调度,Prism 惰性初始化):首渲染纯文本、分块完成后补高亮。
1290
+ const lineHtml = useProgressiveHighlight(text, data.file, readHl);
1291
+ if (topOmitted) {
1292
+ rows.push(React.createElement('div', { key: 'r' + (ri++), className: 'pg2-romit' }, T('app.diffTopMore').replace('{n}', String(startLine - 1))));
1293
+ }
1294
+ for (let i = 0; i < lines.length; i++) {
1295
+ rows.push(React.createElement('div', { key: 'r' + (ri++), className: 'pg2-rrow' },
1296
+ React.createElement('span', { className: 'pg2-rnum' }, String(startLine + i)),
1297
+ lineHtml && lineHtml[i] !== undefined
1298
+ ? React.createElement('span', { className: 'pg2-text', dangerouslySetInnerHTML: { __html: lineHtml[i] } })
1299
+ : React.createElement('span', { className: 'pg2-text' }, lines[i]),
1300
+ ));
1301
+ }
1302
+ if (bottomOmitted) {
1303
+ rows.push(React.createElement('div', { key: 'r' + (ri++), className: 'pg2-romit' }, T('app.diffBottomMore')));
1304
+ }
1305
+ return React.createElement('div', { className: 'pg2-block' },
1306
+ React.createElement('div', { className: 'pg2-header' },
1307
+ React.createElement('span', { className: 'pg2-status-read', title: T('app.diffRead') }, 'R'),
1308
+ React.createElement('span', { className: 'pg2-path', title: data.file, onClick: () => onOpenFile && onOpenFile(data.file) }, data.file),
1309
+ React.createElement('span', { className: 'pg2-added' }, T('app.diffLines').replace('{n}', String(lines.length) + (topOmitted || bottomOmitted ? '+' : ''))),
1310
+ React.createElement('button', { className: 'pg2-copy', onClick: onCopy }, copied ? T('app.diffCopied') : T('app.diffCopy')),
1311
+ (typeof onOpenFile === 'function')
1312
+ ? React.createElement('button', { className: 'pg2-expand', onClick: () => onOpenFile(data.file), title: T('app.diffExpand') }, T('app.diffExpand'))
1313
+ : null,
1314
+ (typeof onCollapse === 'function')
1315
+ ? React.createElement('button', { className: 'pg2-expand', onClick: onCollapse, title: T('app.diffCollapse') }, T('app.diffCollapse'))
1316
+ : null,
1317
+ ),
1318
+ React.createElement('div', { className: 'pg2-body' }, rows),
1319
+ );
1320
+ }
1321
+ // 记忆化:ApprovalOverlay 的状态变化(如拒绝理由输入框每次按键)不应重渲染 diff 区域。
1322
+ // 回调 props 每次渲染都是新引用,比较时忽略,仅按数据与模式比较(diffCache 对象引用稳定)。
1323
+ const diffPropsEqual = (a, b) => a.data === b.data && a.changesOnly === b.changesOnly;
1324
+ const DiffBlockMemo = React.memo(DiffBlock, diffPropsEqual);
1325
+ const ReadBlockMemo = React.memo(ReadBlock, diffPropsEqual);
1326
+ function CompareDrawer({ pin, onClose }) {
1327
+ const [data, setData] = React.useState(null);
1328
+ const [err, setErr] = React.useState('');
1329
+ const [editMsg, setEditMsg] = React.useState(null);
1330
+ const [width, setWidth] = React.useState(null);
1331
+ useLocaleTick();
1332
+ React.useEffect(() => {
1333
+ setData(null);
1334
+ setErr('');
1335
+ // 与弹窗内联详情共用 diffCache:命中直接渲染(含失败态),未命中才请求并回写
1336
+ const cached = diffCache.get(pin.id);
1337
+ if (cached) {
1338
+ if (cached.ok) setData(cached);
1339
+ else setErr((cached.error) ? cached.error : T('app.diffErr'));
1340
+ return;
1341
+ }
1342
+ let alive = true;
1343
+ schedulePrismIdle()
1344
+ call('permgate:file-diff', { id: pin.id }).then((r) => {
1345
+ if (!alive) return;
1346
+ const norm = (r && typeof r === 'object') ? r : { ok: false, error: '' };
1347
+ diffCache.set(pin.id, norm);
1348
+ if (norm.ok) setData(norm);
1349
+ else setErr((norm.error) ? norm.error : T('app.diffErr'));
1350
+ }).catch(() => { if (alive) setErr(T('app.diffErr')) });
1351
+ return () => { alive = false };
1352
+ }, [pin.id]);
1353
+ // 左边缘拖动调整侧边栏宽度(面板固定在右侧,向左拖变宽)
1354
+ const onResizeStart = (e) => {
1355
+ if (e && e.preventDefault) e.preventDefault();
1356
+ const startX = e.clientX;
1357
+ const el = e.currentTarget ? e.currentTarget.parentElement : null;
1358
+ const startW = el ? el.getBoundingClientRect().width : (width || 600);
1359
+ let active = true;
1360
+ const onMove = (ev) => {
1361
+ if (!active) return;
1362
+ const maxW = (typeof window !== 'undefined' ? window.innerWidth : 1200) - 80;
1363
+ setWidth(Math.round(Math.min(Math.max(startW + (startX - ev.clientX), 480), maxW)));
1364
+ };
1365
+ const onUp = () => {
1366
+ active = false;
1367
+ if (typeof document !== 'undefined') {
1368
+ document.removeEventListener('mousemove', onMove);
1369
+ document.removeEventListener('mouseup', onUp);
1370
+ document.body.style.cursor = '';
1371
+ }
1372
+ };
1373
+ if (typeof document !== 'undefined') {
1374
+ document.body.style.cursor = 'col-resize';
1375
+ document.addEventListener('mousemove', onMove);
1376
+ document.addEventListener('mouseup', onUp);
1377
+ }
1378
+ };
1379
+ const onEdit = () => {
1380
+ setEditMsg(null);
1381
+ call('permgate:open-file', { id: pin.id }).then((r) => {
1382
+ setEditMsg(r && r.ok ? { ok: true, text: T('app.editOpened') } : { ok: false, text: (r && r.error) || T('app.diffErr') });
1383
+ setTimeout(() => setEditMsg(null), 2500);
1384
+ }).catch(() => {
1385
+ setEditMsg({ ok: false, text: T('app.diffErr') });
1386
+ setTimeout(() => setEditMsg(null), 2500);
1387
+ });
1388
+ };
1389
+ let body;
1390
+ if (data) {
1391
+ body = data.kind === 'read'
1392
+ ? React.createElement(PGErrorBoundary, { fallback: T('app.diffErr') }, React.createElement(ReadBlockMemo, { data, onOpenFile: null, onCollapse: onClose }))
1393
+ : React.createElement(PGErrorBoundary, { fallback: T('app.diffErr') }, React.createElement(DiffBlockMemo, { data, onOpenFile: null, onCollapse: onClose }));
1394
+ } else if (err) {
1395
+ body = React.createElement('div', { className: 'pg2-err' }, err);
1396
+ } else {
1397
+ body = React.createElement('div', { className: 'pg2-load' }, T('app.diffLoading'));
1398
+ }
1399
+ return React.createElement('div', { className: 'pg2-drawer', style: width ? { width: width + 'px' } : undefined },
1400
+ React.createElement('div', { className: 'pg2-drawer-resize', onMouseDown: onResizeStart, title: T('app.diffResize') }),
1401
+ React.createElement('div', { className: 'pg2-drawer-head' },
1402
+ React.createElement('span', { className: 'pg2-drawer-file', title: pin.file }, pin.file),
1403
+ editMsg ? React.createElement('span', { className: 'pg2-edit-msg' + (editMsg.ok ? '' : ' err') }, editMsg.text) : null,
1404
+ React.createElement('button', { className: 'pg2-drawer-open', onClick: onEdit, title: T('app.openFile') }, T('app.openFile')),
1405
+ React.createElement('button', { className: 'pg2-drawer-close', onClick: onClose, title: T('app.diffClose') }, '✕'),
1406
+ ),
1407
+ React.createElement('div', { className: 'pg2-drawer-body' }, body),
1408
+ );
1409
+ }
1410
+ // shell.overlay 入口:审批弹窗 + 右侧对比抽屉共享 pin 状态
1411
+ // 错误边界:任一子组件 render 崩溃只降级该区域,不影响审批弹窗/抽屉存活
1412
+ // 按 payload 对象引用生成稳定 resetKey(WeakMap:对象被回收后条目随之消失)
1413
+ const payloadKeys = new WeakMap();
1414
+ let payloadKeySeq = 0;
1415
+ function payloadKey(o) {
1416
+ let k = payloadKeys.get(o);
1417
+ if (k === undefined) { k = 'd' + (++payloadKeySeq); payloadKeys.set(o, k) }
1418
+ return k;
1419
+ }
1420
+ class PGErrorBoundary extends React.Component {
1421
+ constructor(props) {
1422
+ super(props);
1423
+ this.state = { err: null };
1424
+ }
1425
+ static getDerivedStateFromError(err) {
1426
+ return { err };
1427
+ }
1428
+ componentDidCatch(err) {
1429
+ console.error('[permgate] ui error:', err);
1430
+ }
1431
+ componentDidUpdate(prevProps) {
1432
+ // resetKey 变化(新 pin/新 payload 数据)时解除错误锁死:
1433
+ // 一次渲染崩溃不应让该子树对后续有效数据永久显示兜底错误。
1434
+ if (this.state.err && this.props.resetKey !== prevProps.resetKey) {
1435
+ this.setState({ err: null });
1436
+ }
1437
+ }
1438
+ render() {
1439
+ if (this.state.err) {
1440
+ // 只显示通用 fallback,不把原始异常消息(可能含内部路径/文件片段)渲染进界面;
1441
+ // 完整错误已由 componentDidCatch 写入 console。
1442
+ return React.createElement('div', { className: 'pg2-err' }, this.props.fallback || T('app.diffErr'));
1443
+ }
1444
+ return this.props.children;
1445
+ }
1446
+ }
1447
+ function OverlayRoot() {
1448
+ const [pin, setPin] = React.useState(null);
1449
+ return React.createElement('div', null,
1450
+ React.createElement(PGErrorBoundary, { fallback: T('app.uiErr') },
1451
+ React.createElement(ApprovalOverlay, { onOpenFile: (id, file) => setPin({ id, file }) }),
1452
+ ),
1453
+ pin ? React.createElement(PGErrorBoundary, { resetKey: pin.id, fallback: T('app.uiErr') },
1454
+ React.createElement(CompareDrawer, { pin, onClose: () => setPin(null) }),
1455
+ ) : null,
1456
+ );
1457
+ }
1458
+
1459
+ function DockBar(props) {
1460
+ // 槽为 session 作用域:props.sessionId 是当前会话(标准 props),
1461
+ // 状态按会话查询,切换对话后各 DockBar 只反映自己的会话
1462
+ const sessionId = props && props.sessionId;
1463
+ const [status, setStatus] = React.useState(null);
1464
+ useLocaleTick();
1465
+ const refresh = () => { call('permgate:status', { sessionId: sessionId || undefined }).then(setStatus).catch(() => {}); };
1466
+ React.useEffect(() => {
1467
+ refresh();
1468
+ const off = subscribeEvents((ev) => {
1469
+ if (ev.type === 'status' || ev.type === 'refresh') refresh();
1470
+ });
1471
+ return () => off();
1472
+ }, []);
1473
+ // 仅当前会话选中「自定义审查」时显示分类徽标(只读展示,无开关)
1474
+ if (!status || status.activeForSession !== true) return null;
1475
+ const eff = status.effective || {};
1476
+ const chip = (label, m) => React.createElement('span', { key: label, style: { color: MODE_COLORS[m] || '#888' } }, label + ':' + modeLabel(m));
1477
+ return React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, color: 'rgba(128,128,128,0.95)', padding: '2px 0' } },
1478
+ React.createElement('span', { style: { fontWeight: 600 } }, T('dock.title')),
1479
+ React.createElement('span', { style: { display: 'flex', gap: 6, flexWrap: 'wrap' } },
1480
+ chip(catShort('directory'), eff.directory), chip(catShort('command'), eff.command), chip(catShort('read'), eff.read), chip(catShort('edit'), eff.edit), chip(catShort('subagent'), eff.subagent), chip(catShort('doomloop'), eff.doomloop),
1481
+ ),
1482
+ React.createElement('span', { style: { cursor: 'pointer', padding: '0 4px' }, onClick: refresh, title: T('dock.refresh') }, '↻'),
1483
+ );
1484
+ }
1485
+
1486
+ function Panel(props) {
1487
+ // settings.section 是 root 作用域槽:不直接给 sessionId,但注入 useSessions
1488
+ // 标准 hook。用它订阅当前活动会话(SessionListState.current),会话切换时
1489
+ // 选择器变化触发重渲染 + 重新查询,面板始终显示当前会话的项目配置。
1490
+ const sessionId = (props && typeof props.useSessions === 'function') ? props.useSessions((st) => (st ? st.current : undefined)) : undefined;
1491
+ const [status, setStatus] = React.useState(null);
1492
+ const [busy, setBusy] = React.useState(false);
1493
+ const [msg, setMsg] = React.useState('');
1494
+ const [tab, setTab] = React.useState('global');
1495
+ const [cats, setCats] = React.useState({ global: {}, project: {} });
1496
+ const [quickSel, setQuickSel] = React.useState({});
1497
+ const [exVals, setExVals] = React.useState({ directory: '', command: '', read: '', edit: '' });
1498
+ const [exAction, setExAction] = React.useState('allow');
1499
+ const [exReasonVal, setExReasonVal] = React.useState('');
1500
+ const [newTool, setNewTool] = React.useState('');
1501
+ const [form, setForm] = React.useState({ action: 'deny', tool: '', path: '', args: '', reason: '' });
1502
+ const [confirm, setConfirm] = React.useState(null);
1503
+ const [excCollapsed, setExcCollapsed] = React.useState({});
1504
+
1505
+ React.useEffect(() => {
1506
+ if (confirm === null) return undefined;
1507
+ const id = setTimeout(() => setConfirm(null), 4000);
1508
+ return () => clearTimeout(id);
1509
+ }, [confirm]);
1510
+
1511
+ const applyStatus = (s) => {
1512
+ if (!s) return;
1513
+ setStatus(s);
1514
+ const cs = { global: {}, project: {} };
1515
+ for (const t of ['global', 'project']) {
1516
+ const block = s.categories && s.categories[t] ? s.categories[t] : null;
1517
+ for (const c of CATS) {
1518
+ cs[t][c] = block && block[c] ? block[c].mode : (t === 'project' ? 'inherit' : 'ask');
1519
+ }
1520
+ }
1521
+ setCats(cs);
1522
+ const qs = {};
1523
+ const gq = s.quickTools ? s.quickTools.global : {};
1524
+ const pq = s.quickTools ? s.quickTools.project : {};
1525
+ const names = {};
1526
+ for (const t of QUICK_PRESET) names[t] = 1;
1527
+ for (const k of Object.keys(gq)) names[k] = 1;
1528
+ for (const k of Object.keys(pq)) names[k] = 1;
1529
+ for (const t of Object.keys(names)) {
1530
+ qs[t] = { g: gq[t] || 'allow', p: pq[t] && pq[t] !== 'inherit' ? pq[t] : 'inherit' };
1531
+ }
1532
+ setQuickSel(qs);
1533
+ };
1534
+
1535
+ const refresh = () => {
1536
+ call('permgate:status', { sessionId: sessionId || undefined }).then(applyStatus).catch((e) => setMsg(String((e && e.message) || e)));
1537
+ };
1538
+ // 会话切换(useSessions 选择器变化)与 SSE 联动都会触发重查,
1539
+ // 保证切换会话后面板立即显示当前会话的项目配置
1540
+ React.useEffect(() => {
1541
+ refresh();
1542
+ const off = subscribeEvents((ev) => {
1543
+ if (ev.type === 'status' || ev.type === 'refresh') refresh();
1544
+ });
1545
+ return () => off();
1546
+ }, [sessionId]);
1547
+
1548
+ const invoke = (method, args, done) => {
1549
+ setBusy(true);
1550
+ setMsg('');
1551
+ call(method, Object.assign({}, args || {}, { sessionId: sessionId || undefined })).then((r) => {
1552
+ if (r && r.error) { setMsg(String(r.error)); return; }
1553
+ setMsg(T('panel.saved'));
1554
+ applyStatus(r && r.status ? r.status : r);
1555
+ if (done) done(r);
1556
+ }).catch((e) => setMsg(String((e && e.message) || e))).then(() => setBusy(false));
1557
+ };
1558
+
1559
+ const confirmDelete = (key, fn) => {
1560
+ if (busy) return;
1561
+ if (confirm === key) { setConfirm(null); fn(); }
1562
+ else setConfirm(key);
1563
+ };
1564
+
1565
+ const changeCat = (c) => (e) => {
1566
+ const mode = e.target.value;
1567
+ setCats(Object.assign({}, cats, { [tab]: Object.assign({}, cats[tab], { [c]: mode }) }));
1568
+ invoke('permgate:set-categories', { [tab]: { [c]: mode } }, () => setMsg(tab === 'global' ? T('panel.savedToGlobal') : T('panel.savedToProject')));
1569
+ };
1570
+
1571
+ const changeQuick = (tool) => (e) => {
1572
+ const mode = e.target.value;
1573
+ const key = tab === 'global' ? 'g' : 'p';
1574
+ setQuickSel(Object.assign({}, quickSel, { [tool]: Object.assign({}, quickSel[tool], { [key]: mode }) }));
1575
+ invoke('permgate:set-quick', { target: tab, tool, action: mode });
1576
+ };
1577
+
1578
+ const addQuick = () => {
1579
+ if (!newTool || !String(newTool).trim()) { setMsg(T('panel.needTool')); return; }
1580
+ invoke('permgate:set-quick', { target: tab, tool: String(newTool).trim(), action: 'allow' }, () => setNewTool(''));
1581
+ };
1582
+
1583
+ const addException = (c) => {
1584
+ const v = String(exVals[c] || '').trim();
1585
+ if (!v) { setMsg(T('panel.needValue')); return; }
1586
+ setExVals(Object.assign({}, exVals, { [c]: '' }));
1587
+ setExReasonVal('');
1588
+ invoke('permgate:add-exception', { target: tab, category: c, match: v, action: exAction, reason: exAction === 'deny' ? (String(exReasonVal || '').trim() || undefined) : undefined });
1589
+ };
1590
+
1591
+ const removeException = (c, id) => invoke('permgate:remove-exception', { target: tab, category: c, id });
1592
+
1593
+ const setFormKey = (key) => (e) => setForm(Object.assign({}, form, { [key]: e.target.value }));
1594
+ const addRule = () => invoke('permgate:add-rule', { target: tab, action: form.action, tool: form.tool || undefined, path: form.path || undefined, args: form.args || undefined, reason: form.reason || undefined });
1595
+ const removeRule = (id) => invoke('permgate:remove-rule', { target: tab, id });
1596
+ const reload = () => invoke('permgate:reload', {});
1597
+ const openConfig = () => invoke('permgate:open-config', {}, () => setMsg(T('panel.openConfigDone')));
1598
+
1599
+ const badge = (action) => React.createElement('span', { style: { color: MODE_COLORS[action] || '#888', fontWeight: 600, marginRight: 6 } }, modeLabel(action));
1600
+
1601
+ const sel = (v, onChange, options, disabled) => React.createElement('select', { value: v, onChange, className: 'pg-field', style: { padding: '2px 6px' }, disabled: !!disabled },
1602
+ options.map((m) => React.createElement('option', { key: m, value: m }, modeLabel(m))),
1603
+ );
1604
+
1605
+ const tabBtn = (t, label) => React.createElement('button', { className: 'pg-tab' + (tab === t ? ' pg-tab-on' : ''), onClick: () => setTab(t) }, label);
1606
+
1607
+ const catBlock = (c) => {
1608
+ const mode = cats[tab] ? (cats[tab][c] || (tab === 'project' ? 'inherit' : 'ask')) : (tab === 'project' ? 'inherit' : 'ask');
1609
+ const block = status && status.categories && status.categories[tab] ? status.categories[tab] : null;
1610
+ const excList = block && block[c] && Array.isArray(block[c].exceptions) ? block[c].exceptions : [];
1611
+ // 例外折叠:列表超过阈值默认收起(长列表不再撑爆面板),添加行始终可见
1612
+ const excCollapsedState = excCollapsed[c] === undefined ? excList.length > EXC_COLLAPSE_THRESHOLD : excCollapsed[c];
1613
+ const toggleExc = () => setExcCollapsed(Object.assign({}, excCollapsed, { [c]: !excCollapsedState }));
1614
+ return React.createElement('div', { key: c, style: { border: '1px solid rgba(128,128,128,0.3)', borderRadius: 8, padding: 10, marginBottom: 10 } },
1615
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 } },
1616
+ React.createElement('span', { style: { fontSize: 13, fontWeight: 600 } }, catLabel(c)),
1617
+ sel(mode, changeCat(c), tab === 'project' ? ALL_MODES : MODES, busy),
1618
+ ),
1619
+ EXC_CATS.indexOf(c) !== -1 ? React.createElement('div', { style: { marginTop: 2 } },
1620
+ React.createElement('div', { style: { fontSize: 12, color: 'rgba(128,128,128,0.85)', marginBottom: 4 } },
1621
+ T('panel.excCount').replace('{n}', String(excList.length)) + (c === 'command' ? T('panel.excCmdHint') : T('panel.excPathHint')),
1622
+ ),
1623
+ excCollapsedState
1624
+ ? React.createElement('div', { style: { fontSize: 12, color: '#1f6feb', cursor: 'pointer', userSelect: 'none', padding: '2px 0' }, onClick: toggleExc, title: T('panel.excExpand') },
1625
+ '> ' + T('panel.excListLink') + '(' + String(excList.length) + ' ' + T('panel.excUnit') + ')',
1626
+ )
1627
+ : (excList.length ? React.createElement('div', null,
1628
+ // 展开/折叠开关固定在占位行位置:展开后原地变为「▾ 折叠」,条目列在下方
1629
+ React.createElement('div', { style: { fontSize: 12, color: '#1f6feb', cursor: 'pointer', userSelect: 'none', padding: '2px 0', marginBottom: 2 }, onClick: toggleExc, title: T('panel.excCollapse') },
1630
+ '▾ ' + T('panel.excCollapse'),
1631
+ ),
1632
+ excList.map((r) => React.createElement('div', { key: r.id, style: rowStyle },
1633
+ badge(r.action),
1634
+ React.createElement('span', { style: { fontFamily: 'monospace', fontSize: 12 } }, r.path || r.match || ''),
1635
+ r.action === 'deny' && r.reason ? React.createElement('span', { style: { fontSize: 12, color: '#c62828', marginLeft: 6 } }, '「' + r.reason + '」') : null,
1636
+ React.createElement('div', { style: { display: 'flex', gap: 6, marginLeft: 'auto' } },
1637
+ React.createElement('button', { className: 'pg-btn pg-btn-danger' + (confirm === 'exc:' + c + ':' + r.id ? ' pg-btn-confirm' : ''), disabled: busy, onClick: () => confirmDelete('exc:' + c + ':' + r.id, () => removeException(c, r.id)) }, confirm === 'exc:' + c + ':' + r.id ? T('panel.confirmDel') : T('panel.del')),
1638
+ confirm === 'exc:' + c + ':' + r.id ? React.createElement('button', { className: 'pg-btn', disabled: busy, onClick: () => setConfirm(null) }, T('panel.cancel')) : null,
1639
+ ),
1640
+ )),
1641
+ ) : React.createElement('div', { style: { fontSize: 12, color: 'rgba(128,128,128,0.7)' } }, T('panel.excNone'))),
1642
+ React.createElement('div', { style: { display: 'flex', gap: 6, marginTop: 6, flexWrap: 'wrap' } },
1643
+ React.createElement('input', { className: 'pg-field', placeholder: c === 'command' ? T('panel.excPh') : T('panel.excPathPh'), value: exVals[c] || '', onChange: (e) => setExVals(Object.assign({}, exVals, { [c]: e.target.value })), disabled: busy }),
1644
+ React.createElement('select', { className: 'pg-field', style: { padding: '2px 6px' }, value: exAction, onChange: (e) => setExAction(e.target.value), disabled: busy },
1645
+ React.createElement('option', { value: 'allow' }, T('panel.allow')), React.createElement('option', { value: 'deny' }, T('panel.deny')),
1646
+ ),
1647
+ exAction === 'deny' ? React.createElement('input', { className: 'pg-field', style: { maxWidth: 220 }, placeholder: T('panel.excReasonPh'), title: T('panel.excReason'), value: exReasonVal, onChange: (e) => setExReasonVal(e.target.value), disabled: busy }) : null,
1648
+ React.createElement('button', { className: 'pg-btn', disabled: busy, onClick: () => addException(c) }, T('panel.addExc')),
1649
+ ),
1650
+ ) : null,
1651
+ );
1652
+ };
1653
+
1654
+ const quickTools = Object.keys(quickSel);
1655
+ const quickRow = (t) => {
1656
+ const key = tab === 'global' ? 'g' : 'p';
1657
+ const mode = quickSel[t] ? (quickSel[t][key] || (tab === 'project' ? 'inherit' : 'allow')) : (tab === 'project' ? 'inherit' : 'allow');
1658
+ return React.createElement('div', { key: t, style: rowStyle },
1659
+ React.createElement('span', { style: { fontFamily: 'monospace', fontSize: 13, width: 150 } }, t),
1660
+ sel(mode, changeQuick(t), tab === 'project' ? ALL_MODES : MODES, busy),
1661
+ );
1662
+ };
1663
+
1664
+ const ruleRow = (r) => {
1665
+ const matchers = [];
1666
+ if (r.tool) matchers.push('tool=' + r.tool);
1667
+ if (r.path) matchers.push('path=' + r.path);
1668
+ if (r.args) matchers.push('args="' + r.args + '"');
1669
+ return React.createElement('div', { key: r.id, style: rowStyle },
1670
+ badge(r.action),
1671
+ React.createElement('span', { style: { fontFamily: 'monospace', fontSize: 12 } }, matchers.join(' ') || T('panel.all')),
1672
+ r.reason ? React.createElement('span', { style: { fontSize: 12, color: 'rgba(128,128,128,0.9)' } }, '(' + r.reason + ')') : null,
1673
+ React.createElement('div', { style: { display: 'flex', gap: 6, marginLeft: 'auto' } },
1674
+ React.createElement('button', { className: 'pg-btn pg-btn-danger' + (confirm === 'rule:' + r.id ? ' pg-btn-confirm' : ''), disabled: busy, onClick: () => confirmDelete('rule:' + r.id, () => removeRule(r.id)) }, confirm === 'rule:' + r.id ? T('panel.confirmDel') : T('panel.del')),
1675
+ confirm === 'rule:' + r.id ? React.createElement('button', { className: 'pg-btn', disabled: busy, onClick: () => setConfirm(null) }, T('panel.cancel')) : null,
1676
+ ),
1677
+ );
1678
+ };
1679
+
1680
+ const customList = status && status.custom ? status.custom[tab] : null;
1681
+ const eff = status ? status.effective : null;
1682
+ const decisions = status && status.recentDecisions ? status.recentDecisions : [];
1683
+ const stats = status && status.stats ? status.stats : { deny: 0, ask: 0 };
1684
+ const sandbox = status && status.sandbox ? status.sandbox : { global: 'workspace-write', project: 'inherit', effective: 'workspace-write' };
1685
+ const presetName = status && status.preset ? status.preset : null;
1686
+ const activeForSession = status ? status.activeForSession === true : false;
1687
+
1688
+ const sandboxLabel = (m) => (m === 'workspace-write' ? T('sandbox.ww') : m === 'danger-full-access' ? T('sandbox.fa') : T('sandbox.inherit'));
1689
+ const sandboxVal = tab === 'global' ? sandbox.global : sandbox.project;
1690
+ const sandboxOptions = tab === 'global' ? ['workspace-write', 'danger-full-access'] : ['workspace-write', 'danger-full-access', 'inherit'];
1691
+ const changeSandbox = (e) => {
1692
+ const mode = e.target.value;
1693
+ invoke('permgate:set-sandbox', { target: tab, mode }, () => setMsg(T('panel.sandboxSaved').replace('{t}', T(tab === 'global' ? 'panel.tabGlobal' : 'panel.tabProject')).replace('{v}', sandboxLabel(mode))));
1694
+ };
1695
+
1696
+ return React.createElement('div', { style: { maxWidth: 760, fontFamily: FONT, color: 'var(--dsw-alias-label-primary, #24292f)' } },
1697
+ React.createElement('div', { style: card },
1698
+ React.createElement('div', { style: h() }, T('panel.status')),
1699
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8, flexWrap: 'wrap' } },
1700
+ React.createElement('span', { style: { fontSize: 13, fontWeight: 600 } }, T('panel.sessionPerm')),
1701
+ React.createElement('span', { style: { fontSize: 13, color: activeForSession ? '#2e7d32' : '#888', fontWeight: 600 } }, presetName === 'custom-review' ? T('panel.customReview') : (presetName || T('panel.none'))),
1702
+ React.createElement('span', { style: small }, activeForSession ? T('panel.active') : T('panel.inactive')),
1703
+ ),
1704
+ React.createElement('div', { style: { fontSize: 13, marginBottom: 6 } },
1705
+ T('panel.sandboxEff') + ' ',
1706
+ React.createElement('span', { style: { color: sandbox.effective === 'danger-full-access' ? '#e65100' : '#2e7d32', fontWeight: 600 } }, sandboxLabel(sandbox.effective)),
1707
+ React.createElement('span', { style: small }, sandbox.effective === 'workspace-write' ? T('panel.sandboxDesc.ww') : T('panel.sandboxDesc.fa')),
1708
+ ),
1709
+ React.createElement('div', { style: { fontSize: 13, marginBottom: 6 } },
1710
+ T('panel.effCats'),
1711
+ eff ? CATS.map((c) => React.createElement('span', { key: c, style: { color: MODE_COLORS[eff[c]] || '#888', fontWeight: 600, marginRight: 8 } }, (catLabel(c) + ':' + modeLabel(eff[c])))) : '…',
1712
+ ),
1713
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' } },
1714
+ React.createElement('span', { style: small }, T('panel.config') + (status ? status.configPath : '…')),
1715
+ React.createElement('button', { className: 'pg-btn', disabled: busy, onClick: openConfig }, T('panel.openConfig')),
1716
+ React.createElement('button', { className: 'pg-btn', disabled: busy, onClick: reload }, T('panel.reload')),
1717
+ ),
1718
+ React.createElement('div', { style: small }, T('panel.project') + (status ? status.projectKey : '…')),
1719
+ React.createElement('div', { style: small }, T('panel.stats').replace('{d}', String(stats.deny)).replace('{a}', String(stats.ask))),
1720
+ status && status.loadError ? React.createElement('div', { style: { color: '#c62828', fontSize: 12 } }, T('panel.loadErr') + status.loadError) : null,
1721
+ status && status.saveError ? React.createElement('div', { style: { color: '#c62828', fontSize: 12 } }, T('panel.saveErr') + status.saveError) : null,
1722
+ ),
1723
+ React.createElement('div', { style: { display: 'flex', marginBottom: 12, borderBottom: '1px solid rgba(128,128,128,0.35)' } },
1724
+ tabBtn('global', T('panel.tabGlobal')),
1725
+ tabBtn('project', T('panel.tabProject')),
1726
+ ),
1727
+ React.createElement('div', { style: { fontSize: 12, color: 'rgba(128,128,128,0.85)', marginBottom: 8 } },
1728
+ tab === 'global' ? T('panel.editGlobal') : T('panel.editProject').replace('{p}', status ? status.projectKey : ''),
1729
+ ),
1730
+ React.createElement('div', { style: card },
1731
+ React.createElement('div', { style: h() }, T('panel.sandbox')),
1732
+ React.createElement('div', { style: { fontSize: 12, color: 'rgba(128,128,128,0.85)', marginBottom: 6 } },
1733
+ sandboxVal === 'inherit' ? T('sandbox.desc.inherit').replace('{g}', sandboxLabel(sandbox.global)) : (sandboxVal === 'workspace-write' ? T('sandbox.desc.ww') : T('sandbox.desc.fa')),
1734
+ ),
1735
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } },
1736
+ React.createElement('select', { className: 'pg-field', style: { padding: '2px 6px' }, value: sandboxVal, onChange: changeSandbox, disabled: busy },
1737
+ sandboxOptions.map((m) => React.createElement('option', { key: m, value: m }, sandboxLabel(m))),
1738
+ ),
1739
+ ),
1740
+ ),
1741
+ CATS.map(catBlock),
1742
+ React.createElement('div', { style: card },
1743
+ React.createElement('div', { style: h() }, T('panel.quick')),
1744
+ React.createElement('div', { style: { fontSize: 12, color: 'rgba(128,128,128,0.8)', marginBottom: 6 } }, T('panel.quickHint')),
1745
+ quickTools.map(quickRow),
1746
+ React.createElement('div', { style: { display: 'flex', gap: 6, marginTop: 6, flexWrap: 'wrap' } },
1747
+ React.createElement('input', { className: 'pg-field', placeholder: T('panel.quickAdd'), value: newTool, onChange: (e) => setNewTool(e.target.value), disabled: busy }),
1748
+ React.createElement('button', { className: 'pg-btn', disabled: busy, onClick: addQuick }, T('panel.quickAddBtn')),
1749
+ ),
1750
+ ),
1751
+ React.createElement('div', { style: card },
1752
+ React.createElement('div', { style: h() }, T('panel.rules')),
1753
+ React.createElement('div', { style: { fontSize: 12, color: 'rgba(128,128,128,0.8)', marginBottom: 8 } }, T('panel.rulesHint')),
1754
+ React.createElement('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center', marginBottom: 8 } },
1755
+ React.createElement('select', { value: form.action, onChange: setFormKey('action'), className: 'pg-field', disabled: busy },
1756
+ React.createElement('option', { value: 'deny' }, T('panel.actionDeny')), React.createElement('option', { value: 'ask' }, T('panel.actionAsk')), React.createElement('option', { value: 'allow' }, T('panel.actionAllow')),
1757
+ ),
1758
+ React.createElement('input', { placeholder: T('panel.toolPh'), value: form.tool, onChange: setFormKey('tool'), className: 'pg-field', disabled: busy }),
1759
+ React.createElement('input', { placeholder: T('panel.pathPh'), value: form.path, onChange: setFormKey('path'), className: 'pg-field', disabled: busy }),
1760
+ React.createElement('input', { placeholder: T('panel.argsPh'), value: form.args, onChange: setFormKey('args'), className: 'pg-field', disabled: busy }),
1761
+ React.createElement('input', { placeholder: T('panel.reasonPh'), value: form.reason, onChange: setFormKey('reason'), className: 'pg-field', disabled: busy }),
1762
+ React.createElement('button', { className: 'pg-btn', disabled: busy, onClick: addRule }, tab === 'global' ? T('panel.addToGlobal') : T('panel.addToProject')),
1763
+ ),
1764
+ React.createElement('div', { style: { marginTop: 8 } },
1765
+ React.createElement('div', { style: { fontSize: 12, fontWeight: 600, marginBottom: 4 } }, T('panel.ruleList').replace('{t}', T(tab === 'global' ? 'panel.tabGlobal' : 'panel.tabProject')).replace('{n}', String(customList ? customList.length : 0))),
1766
+ customList && customList.length ? customList.map(ruleRow) : React.createElement('div', { style: { fontSize: 12, color: 'rgba(128,128,128,0.8)' } }, T('panel.noRules')),
1767
+ ),
1768
+ ),
1769
+ React.createElement('div', { style: card },
1770
+ React.createElement('div', { style: h() }, T('panel.decisions')),
1771
+ decisions.length ? decisions.map((d, i) => React.createElement('div', { key: i, style: rowStyle },
1772
+ badge(d.action),
1773
+ React.createElement('span', { style: { fontFamily: 'monospace', fontSize: 12 } }, d.tool),
1774
+ React.createElement('span', { style: { fontSize: 12, color: 'rgba(128,128,128,0.9)' } }, d.reason || ''),
1775
+ )) : React.createElement('div', { style: { fontSize: 12, color: 'rgba(128,128,128,0.8)' } }, T('panel.noDecisions')),
1776
+ ),
1777
+ msg ? React.createElement('div', { style: { fontSize: 12, color: '#1e88e5', marginBottom: 8 } }, msg) : null,
1778
+ );
1779
+ }
1780
+
1781
+ function apply(ctx) {
1782
+ LC = ctx;
1783
+ // 中英文适配:注册 permgate 字典并绑定翻译函数(t 调用时读取当前语言)。
1784
+ // register 与 bind 分开容错:register 失败(如重复激活)时 bind 仍要生效,
1785
+ // 否则 T 会退化为纯中文回退函数,整个插件界面卡在中文。
1786
+ try {
1787
+ if (ctx.locale && typeof ctx.locale.register === 'function') {
1788
+ ctx.locale.register('permgate', { zh: I18N.zh, en: I18N.en });
1789
+ }
1790
+ } catch (e) {}
1791
+ try {
1792
+ if (ctx.locale && typeof ctx.locale.bind === 'function') {
1793
+ T = ctx.locale.bind('permgate');
1794
+ }
1795
+ } catch (e) {}
1796
+ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css="dsh-permgate"]') === null) {
1797
+ const tag = document.createElement('style');
1798
+ tag.dataset.plugin = 'dsh-permgate';
1799
+ tag.dataset.pluginCss = 'dsh-permgate';
1800
+ tag.textContent = CSS;
1801
+ document.head.appendChild(tag);
1802
+ ctx.effect(() => () => { try { tag.remove(); } catch (e) {} });
1803
+ }
1804
+ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css="dsh-permgate-diff"]') === null) {
1805
+ const tag = document.createElement('style');
1806
+ tag.dataset.plugin = 'dsh-permgate';
1807
+ tag.dataset.pluginCss = 'dsh-permgate-diff';
1808
+ tag.textContent = DIFF_CSS;
1809
+ document.head.appendChild(tag);
1810
+ ctx.effect(() => () => { try { tag.remove(); } catch (e) {} });
1811
+ }
1812
+ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css="dsh-permgate-diff2"]') === null) {
1813
+ const tag = document.createElement('style');
1814
+ tag.dataset.plugin = 'dsh-permgate';
1815
+ tag.dataset.pluginCss = 'dsh-permgate-diff2';
1816
+ tag.textContent = DIFF2_CSS;
1817
+ document.head.appendChild(tag);
1818
+ ctx.effect(() => () => { try { tag.remove(); } catch (e) {} });
1819
+ }
1820
+ // 权限选择器兼容层:图标徽标为纯 CSS(按 aria-label 选择器,零 JS 依赖,
1821
+ // 跟随语言自动显示);预设名文本交换由 MutationObserver 驱动(childList +
1822
+ // characterData + attributes 全覆盖)+ locale 订阅,无定时器。安装仅在
1823
+ // documentElement 就绪时进行,最多重试 10 次后放弃(有界,非常驻定时器)。
1824
+ ctx.effect(() => {
1825
+ let install = null;
1826
+ let tries = 0;
1827
+ let tid = null;
1828
+ const attempt = () => {
1829
+ if (install) return;
1830
+ if (typeof document !== 'undefined' && document.documentElement) {
1831
+ try { install = pgInstallCompat(); } catch (e) {}
1832
+ return;
1833
+ }
1834
+ tries += 1;
1835
+ if (tries < 10) {
1836
+ try { tid = setTimeout(attempt, 500); } catch (e) {}
1837
+ }
1838
+ };
1839
+ attempt();
1840
+ return () => {
1841
+ if (tid !== null) { try { clearTimeout(tid); } catch (e) {} }
1842
+ if (install) { try { install(); } catch (e) {} }
1843
+ };
1844
+ }, 'permgate: permission selector compat');
1845
+ const slots = ctx.slots;
1846
+ // 注意:槽注册必须直接传组件(不能包一层 () => createElement(X, null) 工厂 —
1847
+ // 那样会丢弃槽系统注入的 props,DockBar 就拿不到 sessionId,状态无法按会话区分)
1848
+ slots.inject('settings.section', () => slots.register(
1849
+ // label 用 thunk:每次投影时重新求值,跟随当前语言(locale.bind 读取活动语言)
1850
+ { name: 'settings.section', id: 'permgate', order: 30, label: () => T('settings.title') },
1851
+ Panel,
1852
+ ));
1853
+ slots.inject('conversation.composer.dock', () => slots.register(
1854
+ { name: 'conversation.composer.dock', id: 'permgate', order: 10 },
1855
+ DockBar,
1856
+ ));
1857
+ slots.inject('shell.overlay', () => slots.register(
1858
+ { name: 'shell.overlay', id: 'permgate-approval', order: 100 },
1859
+ OverlayRoot,
1860
+ ));
1861
+ }
1862
+
1863
+ exports.apply = apply;
1864
+ exports.inject = ['slots', 'locale'];
1865
+ return module.exports;
1866
+ }
1867
+ });