ccdesk 0.2.5 → 0.2.6

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.
@@ -0,0 +1 @@
1
+ @font-face{font-family:Hanken Grotesk Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/hanken-grotesk-cyrillic-ext-wght-normal-D_UHSL_T.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Hanken Grotesk Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/hanken-grotesk-vietnamese-wght-normal-CHiFlh_0.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Hanken Grotesk Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/hanken-grotesk-latin-ext-wght-normal-Dg-wlmqe.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Hanken Grotesk Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/hanken-grotesk-latin-wght-normal-CaVRRdDk.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-cyrillic-ext-wght-normal-EocZY2iu.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--paper:#fbfbf7;--panel:#fff;--sunk:#f1f0ea;--ink:#141414;--ink-2:#4a4a45;--muted:#6b6a63;--rule:#dcdad1;--rule-strong:#141414;--sel:#fff3c4;--sel-ink:#141414;--hover:#f4f3ec;--focus:#0072b2;--ok:#1d6b47;--warn:#8a5a00;--warn-bg:#fff1d6;--bad:#a3361c;--bad-bg:#fde7e0;--on-bad:#fff;--line-managed:#b84f00;--line-user:#0072b2;--line-project:#007a5a;--line-local:#e69f00;--line-memory:#cc79a7;--line-plugin:#5f5f5a;--on-managed:#fff;--on-user:#fff;--on-project:#fff;--on-local:#141414;--on-memory:#141414;--on-plugin:#fff;--sans:"Hanken Grotesk Variable", "Helvetica Neue", Helvetica, "Nimbus Sans", "Liberation Sans", Arial, sans-serif;--mono:"JetBrains Mono Variable", ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;--row:32px;--head:92px;--rail:248px;--inspector:clamp(440px, 30vw, 600px);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--paper:#131312;--panel:#1a1a18;--sunk:#222220;--ink:#ece8de;--ink-2:#c7c2b6;--muted:#938f84;--rule:#2c2b28;--rule-strong:#ece8de;--sel:#33302a;--sel-ink:#fff6e0;--hover:#1d1d1b;--focus:#f2c14e;--ok:#6fcf97;--warn:#f2c14e;--warn-bg:#2b2512;--bad:#ff8a73;--bad-bg:#371b15;--on-bad:#131312;--line-managed:#ff7a3d;--line-user:#4aa8e8;--line-project:#2fbf8f;--line-local:#f2b233;--line-memory:#e391bd;--line-plugin:#8d95a5;--on-managed:#131312;--on-user:#131312;--on-project:#131312;--on-plugin:#131312;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}}:root[data-theme=dark]{--paper:#131312;--panel:#1a1a18;--sunk:#222220;--ink:#ece8de;--ink-2:#c7c2b6;--muted:#938f84;--rule:#2c2b28;--rule-strong:#ece8de;--sel:#33302a;--sel-ink:#fff6e0;--hover:#1d1d1b;--focus:#f2c14e;--ok:#6fcf97;--warn:#f2c14e;--warn-bg:#2b2512;--bad:#ff8a73;--bad-bg:#371b15;--on-bad:#131312;--line-managed:#ff7a3d;--line-user:#4aa8e8;--line-project:#2fbf8f;--line-local:#f2b233;--line-memory:#e391bd;--line-plugin:#8d95a5;--on-managed:#131312;--on-user:#131312;--on-project:#131312;--on-plugin:#131312;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}*{box-sizing:border-box}html,body,#root{height:100%}body{background:var(--paper);color:var(--ink);font:15px / 1.45 var(--sans);font-variant-numeric:tabular-nums;-webkit-font-smoothing:antialiased;margin:0}::selection{background:var(--sel);color:var(--sel-ink)}:focus-visible{outline:2px solid var(--focus);outline-offset:2px}*{scrollbar-width:thin;scrollbar-color:var(--rule) transparent}button,input{font:inherit;color:inherit}button{cursor:pointer}.mono,code,pre,kbd{font-family:var(--mono);font-size:13.5px}kbd{border:1px solid var(--rule);color:var(--muted);background:var(--panel);border-bottom-width:2px;border-radius:4px;padding:0 5px;font-size:12px}.muted{color:var(--muted)}.small{font-size:13.5px}.icon{flex-shrink:0;display:block}.app{grid-template-columns:var(--rail) minmax(0, 1fr);height:100%;display:grid;overflow:hidden}.main{flex-direction:column;min-width:0;display:flex;overflow:hidden}.workspace{grid-template-columns:minmax(0, 1fr) var(--inspector);height:100%;min-height:0;display:grid}.workspace.no-details{grid-template-columns:minmax(0,1fr)}.work-main{min-width:0;padding:0 28px 64px;overflow-y:auto;container-type:inline-size}.rail{background:var(--panel);border-right:1px solid var(--rule);flex-direction:column;gap:2px;padding:14px 10px 32px;display:flex;overflow-y:auto}.brand{letter-spacing:-.01em;align-items:center;gap:8px;padding:2px 6px 14px;font-size:16px;font-weight:700;display:flex}.brand-link{color:inherit;align-items:center;gap:8px;text-decoration:none;display:inline-flex}.brand-link:hover span{text-underline-offset:3px;text-decoration:underline}.brand .text-size{margin-left:auto}.brand .text-size button{justify-content:center;width:24px;padding:0}.brand .text-size button.smaller{font-size:11px}.brand .text-size button.larger{font-size:15px}html[data-text-size=smaller] #root{zoom:.9}html[data-text-size=larger] #root{zoom:1.15}.rail-group{color:var(--muted);justify-content:space-between;align-items:center;margin:14px 6px 4px;font-size:13px;font-weight:600;display:flex}.rail-group.toggle{text-align:left;background:0 0;border:0;padding:0}.rail-group.toggle:hover{color:var(--ink)}.rail-list{margin:0;padding:0;list-style:none}.rail-item{text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;width:100%;height:30px;padding:0 8px;font-size:14.5px;display:flex}.rail-item:hover{background:var(--hover)}.rail-item.current{background:var(--sel);color:var(--sel-ink);font-weight:600}.rail-item.gone .rail-label{text-decoration:line-through;-webkit-text-decoration-color:var(--muted);text-decoration-color:var(--muted)}.rail-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.rail-count{font-family:var(--mono);color:var(--muted);font-size:12.5px}.rail-item.current .rail-count{color:inherit;opacity:.75}.rail-filter,.filter,.palette-input{border:1px solid var(--rule);background:var(--paper);height:30px;color:var(--muted);border-radius:6px;align-items:center;gap:6px;padding:0 8px;display:flex}.rail-filter{margin:12px 0 0}.rail-filter input,.filter input,.palette-input input{min-width:0;color:var(--ink);background:0 0;border:0;outline:0;flex:1}.rail-filter:focus-within,.filter:focus-within{border-color:var(--focus)}input::placeholder{color:var(--muted)}.dir-head{z-index:3;background:var(--paper);height:var(--head);grid-template-rows:32px 28px;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px 16px;padding:16px 0 8px;display:grid;position:sticky;top:0}.dir-title{align-items:center;gap:10px;min-width:0;display:flex}.dir-title h1{letter-spacing:-.01em;white-space:nowrap;text-overflow:ellipsis;min-width:0;margin:0;font-size:19px;font-weight:700;overflow:hidden}.dir-title .flag,.session-line{color:var(--muted);align-items:center;gap:8px;margin:4px 0 12px;font-size:14px;display:flex}.section-index{scrollbar-width:none;grid-column:1/-1;gap:2px;display:flex;overflow-x:auto}.section-index a{height:26px;color:var(--ink-2);white-space:nowrap;border-radius:6px;align-items:center;gap:6px;padding:0 6px;font-size:14px;text-decoration:none;display:inline-flex}.section-index a:hover{background:var(--hover);color:var(--ink)}.section-index a.empty{color:var(--muted)}.section-index kbd{padding:0 4px}@container (width<=1100px){.section-index kbd{display:none}}@container (width<=840px){.section-index .seg-count{display:none}}@container (width<=680px){.section-index a{padding:0 3px;font-size:12.5px}}.seg-count{font-family:var(--mono);color:var(--muted);font-size:12px}[aria-pressed=true] .seg-count{color:inherit;opacity:.75}.tt-section{scroll-margin-top:var(--head)}.via-agents{vertical-align:middle;font-family:var(--mono);margin-left:8px}.vis-personal{color:var(--muted)}.toolbar{align-items:center;gap:8px;display:flex}.filter{width:240px}@container (width<=960px){.filter{width:140px}.palette-label{display:none}}.palette-button{border:1px solid var(--rule);background:var(--panel);border-radius:6px;align-items:center;gap:8px;height:30px;padding:0 10px;font-size:14px;display:flex}.palette-button:hover{border-color:var(--ink-2)}.route{border-top:2px solid var(--rule-strong);border-bottom:1px solid var(--rule);margin:4px 0 8px}.route-stops{margin:0;padding:0;list-style:none;display:flex;position:relative}.stop{flex:2 1 0;min-width:0}.stop.idle{flex:1 1 0}.route-stops:before{content:"";background:var(--ink);height:3px;position:absolute;top:25px;left:22px;right:22px}.stop button{text-align:left;background:0 0;border:0;border-radius:6px;grid-template-rows:22px auto auto;justify-items:start;gap:4px;width:100%;padding:14px 8px 12px;display:grid;position:relative}.stop button:hover{background:var(--hover)}.stop button.on{background:var(--sunk)}.stop-node{background:var(--paper);border-radius:999px;padding:0 6px 0 0;display:flex}.stop button:hover .stop-node,.stop button.on .stop-node{background:0 0}.stop-name{font-size:14.5px;font-weight:700}.stop-facts{font-family:var(--mono);color:var(--muted);font-size:12.5px}.stop.idle .stop-name{color:var(--muted);font-weight:500}.stop.lit .bullet{box-shadow:0 0 0 2px var(--paper), 0 0 0 4px var(--ink)}.stop button.on .stop-name{text-underline-offset:3px;text-decoration:underline;text-decoration-thickness:2px}.bullet{width:22px;height:22px;font:700 11.5px / 1 var(--sans);background:var(--bullet);color:var(--bullet-ink);border:2px solid var(--bullet);border-radius:999px;flex-shrink:0;place-items:center;transition:box-shadow .16s cubic-bezier(.16,1,.3,1);display:inline-grid}.bullet.hollow{background:var(--paper);color:var(--bullet)}.bullet.hollow.bullet-local,.bullet.hollow.bullet-memory{color:var(--ink)}.bullet-managed{--bullet:var(--line-managed);--bullet-ink:var(--on-managed)}.bullet-user{--bullet:var(--line-user);--bullet-ink:var(--on-user)}.bullet-project{--bullet:var(--line-project);--bullet-ink:var(--on-project)}.bullet-local{--bullet:var(--line-local);--bullet-ink:var(--on-local)}.bullet-memory{--bullet:var(--line-memory);--bullet-ink:var(--on-memory)}.bullet-plugin{--bullet:var(--line-plugin);--bullet-ink:var(--on-plugin)}.tt .bullet{vertical-align:middle}.tt .bullet,.palette .bullet,.layers .bullet{width:20px;height:20px;font-size:10.5px}.tt-section{margin-top:26px}.tt-head{border-top:2px solid var(--rule-strong);flex-wrap:wrap;align-items:center;gap:10px;padding:8px 0 6px;display:flex}.tt-head h2{letter-spacing:-.005em;margin:0;font-size:15px;font-weight:700}.tt-count{font-family:var(--mono);color:var(--muted);font-size:13px}.tt-empty{color:var(--muted);margin:4px 0 0;padding:6px 0}.tt{border-collapse:collapse;table-layout:fixed;width:100%}.tt th{top:var(--head);z-index:1;background:var(--paper);text-align:left;color:var(--muted);border-bottom:1px solid var(--rule-strong);white-space:nowrap;height:26px;padding:0 8px;font-size:13px;font-weight:600;position:sticky}.tt td{height:var(--row);border-bottom:1px solid var(--rule);white-space:nowrap;text-overflow:ellipsis;vertical-align:middle;padding:0 8px;overflow:hidden}.tt tbody tr{cursor:pointer}.tt tbody tr:hover td{background:var(--hover)}.tt tbody tr.selected td{background:var(--sel);color:var(--sel-ink)}.tt tbody tr.selected .row-name{font-weight:700}.tt tbody tr.dim td{color:var(--muted)}.tt td.num{text-align:right;font-family:var(--mono);color:var(--muted);font-size:13px}.tt td.num.used{color:var(--ink)}.tt th:first-child,.tt td:first-child{padding-left:2px}.row-name{font:inherit;text-overflow:ellipsis;white-space:nowrap;text-align:left;vertical-align:middle;background:0 0;border:0;max-width:100%;padding:0;overflow:hidden}.via{vertical-align:middle;color:var(--muted);margin-left:6px;display:inline-flex}.value,.why,.desc{color:var(--ink-2)}.tt-instructions th:first-child,.tt-instructions th:nth-child(2){width:40px}.tt-instructions th:nth-child(3){width:32%}.tt-instructions th:nth-child(4){width:86px}.tt-instructions th:nth-child(6){text-align:right;width:58px}.tt-instructions th:nth-child(7){width:108px}.tt-settings th:first-child{width:76px}.tt-settings th:nth-child(2),.tt-settings th:nth-child(4){width:30%}.tt-memory th:first-child{width:40px}.tt-memory th:nth-child(2){width:24%}.tt-memory th:nth-child(3){width:98px}.tt-memory th:nth-child(5){width:200px}.tt-memory th:nth-child(6){text-align:right;width:82px}.tt-hooks th:first-child,.tt-permissions th:first-child,.tt-mcp th:first-child,.tt-extensions th:first-child{width:40px}.tt-hooks th:nth-child(2){width:140px}.tt-hooks th:nth-child(3){width:26%}.tt-permissions th:nth-child(2){width:120px}.tt-permissions th:nth-child(4){width:34%}.tt-mcp th:nth-child(2){width:26%}.tt-mcp th:nth-child(3){width:92px}.tt-extensions th:nth-child(2){width:104px}.tt-extensions th:nth-child(3){width:30%}.tt-tools th:first-child{width:40px}.tt-tools th:nth-child(2){width:190px}.tt-tools th:nth-child(3){width:150px}.tt-tools th:nth-child(4){width:190px}.tt-tools th:nth-child(5){text-align:right;width:64px}.tt-dirs th:nth-child(2),.tt-dirs th:nth-child(3){text-align:right;width:96px}.tt-dirs th:nth-child(4){text-align:right;width:110px}.tt-shared th:first-child{width:40px}.tt-shared th:nth-child(2){width:30%}.tt-shared th:nth-child(3){width:130px}.bullets>*+*{margin-left:3px}.beat{color:var(--muted)}.beaten{font-family:var(--mono);align-items:center;gap:4px;margin-right:10px;font-size:13px;display:inline-flex}.beaten s{text-decoration-thickness:1.5px}.status{font-size:13.5px;font-weight:600}.status-loaded{color:var(--ok)}.status-skipped{color:var(--muted)}.status-on-match{color:var(--ink-2)}.vis{font-size:13.5px}.vis-tracked{color:var(--ok);font-weight:600}.vis-untracked{color:var(--warn)}.vis-ignored,.vis-no-git{color:var(--muted)}.type{align-items:center;gap:6px;font-size:14px;display:inline-flex}.type-dot{background:var(--ink-2);flex-shrink:0;width:8px;height:8px}.type-user{border-radius:999px}.type-feedback{clip-path:polygon(50% 0,100% 100%,0 100%)}.type-reference{transform:rotate(45deg)scale(.85)}.type-unknown{box-shadow:inset 0 0 0 1.5px var(--muted);background:0 0}.checks>*+*{margin-left:4px}.flag{background:var(--sunk);color:var(--ink-2);white-space:nowrap;border-radius:4px;align-items:center;gap:4px;padding:0 6px;font-size:12.5px;line-height:18px;display:inline-flex}.flag .icon{width:13px;height:13px}.flag.warn{background:var(--warn-bg);color:var(--warn)}.flag.bad{background:var(--bad-bg);color:var(--bad)}.editor .flag{white-space:normal;overflow-wrap:anywhere}.segmented{border:1px solid var(--rule);scrollbar-width:none;border-radius:6px;min-width:0;max-width:100%;margin:0 0 0 6px;padding:0;display:inline-flex;overflow-x:auto}.segmented button{white-space:nowrap;border:0;border-right:1px solid var(--rule);background:var(--panel);flex:none;align-items:center;gap:5px;height:24px;padding:0 9px;font-size:13.5px;display:inline-flex}.segmented button:last-child{border-right:0}.segmented button[aria-pressed=true]{background:var(--ink);color:var(--paper)}.segmented.small button{height:22px;font-size:13px}.inspector{border-left:1px solid var(--rule);background:var(--panel);min-width:0;overflow-y:auto}.insp-head{z-index:2;background:var(--panel);border-bottom:2px solid var(--rule-strong);align-items:center;gap:10px;padding:16px 18px 12px;display:flex;position:sticky;top:0}.insp-head h2{overflow-wrap:anywhere;flex:1;min-width:0;margin:0;font-size:16px;font-weight:700}.insp-body{padding:14px 18px 40px}.verdict{color:var(--ink-2);margin:0 0 14px}.verdict.ok{color:var(--ink)}.verdict.off{color:var(--muted)}.fields{grid-template-columns:104px minmax(0,1fr);gap:6px 12px;margin:0 0 16px;font-size:14px;display:grid}.fields dt{color:var(--muted)}.fields dd{min-width:0;margin:0}.path{flex-wrap:wrap;align-items:center;gap:2px 6px;max-width:100%;display:inline-flex}.path .mono{overflow-wrap:anywhere}.copied{color:var(--ok);flex-basis:100%;font-size:13px}.wrap{overflow-wrap:anywhere;white-space:normal}.icon-button{width:28px;height:28px;color:var(--muted);background:0 0;border:0;border-radius:6px;flex-shrink:0;place-items:center;display:inline-grid}.icon-button:hover{background:var(--hover);color:var(--ink)}.icon-button.small{width:22px;height:22px}.sub{margin:0 0 16px}.sub h3,.sub-title{margin:0 0 6px;font-size:14px;font-weight:700}.plain{gap:4px;margin:0;padding:0;list-style:none;display:grid}.plain li{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.layers{border-collapse:collapse;width:100%;margin-bottom:14px}.layers th{text-align:left;width:40px;padding:4px 0}.layers td{border-bottom:1px solid var(--rule);overflow-wrap:anywhere;padding:4px 8px}.layers tr.wins td{font-weight:600}.layers tr.lost td{color:var(--muted);text-decoration:line-through}.layers tr.unset td{color:var(--muted)}.contents{border-top:1px solid var(--rule-strong);margin-top:8px}.contents-bar{justify-content:space-between;align-items:center;padding:8px 0;font-size:14px;font-weight:700;display:flex}pre.raw{background:var(--sunk);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:6px;margin:0 0 12px;padding:12px;line-height:1.5;overflow-x:auto}.prose{overflow-wrap:anywhere;max-width:68ch;font-size:14.5px;line-height:1.6}.prose h1,.prose h2,.prose h3{margin:18px 0 6px;font-size:14px}.prose p,.prose ul,.prose ol{margin:8px 0}.prose ul,.prose ol{padding-left:20px}.prose code{background:var(--sunk);border-radius:3px;padding:1px 4px}.prose pre{background:var(--sunk);border-radius:6px;padding:10px;overflow-x:auto}.prose pre code{background:0 0;padding:0}.prose a,.link{color:var(--focus);text-underline-offset:2px}.prose table{border-collapse:collapse;display:block;overflow-x:auto}.prose td,.prose th{border:1px solid var(--rule);padding:3px 6px}.link{text-align:left;background:0 0;border:0;padding:0;text-decoration:underline}.actions{margin:4px 0 16px}.quiet-danger{border:1px solid var(--rule);background:var(--panel);height:30px;color:var(--bad);border-radius:6px;align-items:center;gap:6px;padding:0 10px;font-size:14px;display:inline-flex}.quiet-danger:hover{border-color:var(--bad)}.confirm{border:1px solid var(--bad);background:var(--bad-bg);border-radius:8px;padding:12px}.confirm p{margin:0 0 10px;font-size:14px}.confirm-buttons{gap:8px;display:flex}.confirm-buttons button{border:1px solid var(--rule);background:var(--panel);border-radius:6px;height:30px;padding:0 12px;font-size:14px}.confirm-buttons button.danger{background:var(--bad);border-color:var(--bad);color:var(--on-bad);font-weight:600}button:disabled{opacity:.6;cursor:progress}.page{height:100%;padding:22px 28px 64px;overflow-y:auto}.page-head h1{letter-spacing:-.015em;margin:0 0 4px;font-size:22px}.lede{color:var(--ink-2);max-width:72ch;margin:0}.page .tt th{top:-22px}.stores>*+*{margin-left:12px}.palette-backdrop{z-index:20;background:color-mix(in srgb, var(--paper) 55%, transparent);place-items:start center;padding-top:12vh;display:grid;position:fixed;inset:0}.palette{background:var(--panel);border:1px solid var(--rule-strong);border-radius:10px;width:min(640px,100vw - 32px);animation:.18s cubic-bezier(.16,1,.3,1) rise;overflow:hidden;box-shadow:0 18px 48px -12px #00000059}.palette-input{border:0;border-bottom:1px solid var(--rule);background:var(--panel);border-radius:0;height:46px;padding:0 14px}.palette-results{max-height:50vh;margin:0;padding:6px;list-style:none;overflow-y:auto}.palette-results [role=option]{height:var(--row);cursor:pointer;white-space:nowrap;border-radius:6px;align-items:center;gap:10px;padding:0 10px;display:flex;overflow:hidden}.palette-results [role=option].active{background:var(--sel);color:var(--sel-ink)}.palette-empty{color:var(--muted);padding:8px 10px}.toast-region{z-index:30;position:fixed}.toast{z-index:30;background:var(--ink);color:var(--paper);border-radius:8px;align-items:center;gap:14px;padding:8px 8px 8px 14px;font-size:14.5px;animation:.22s cubic-bezier(.16,1,.3,1) rise;display:flex;position:fixed;bottom:22px;left:50%;transform:translate(-50%);box-shadow:0 12px 32px -10px #0006}.toast button{border:1px solid color-mix(in srgb, var(--paper) 40%, transparent);color:inherit;background:0 0;border-radius:6px;align-items:center;gap:6px;height:28px;padding:0 10px;font-weight:600;display:inline-flex}@keyframes rise{0%{opacity:0;translate:0 6px}}.loading{color:var(--muted);flex-direction:column;gap:12px;padding:48px 28px;display:flex}.loading-line{background:linear-gradient(90deg, var(--ink) 0 30%, var(--rule) 30% 100%);background-size:200% 100%;width:220px;height:3px;animation:1.2s cubic-bezier(.65,0,.35,1) infinite travel;display:block}@keyframes travel{0%{background-position:100% 0}to{background-position:-40% 0}}.problem{max-width:64ch;padding:48px 28px}.problem h1{margin:0 0 8px;font-size:18px}.tt-note{color:var(--muted);font-size:13.5px}.perm{font-size:13.5px;font-weight:600}.perm-allow{color:var(--ok)}.perm-ask{color:var(--warn)}.perm-deny{color:var(--bad)}.fix{flex-wrap:wrap;align-items:center;gap:6px;display:inline-flex}.fix .head-action{height:22px;margin-left:0;padding:0 8px}.tt-head .fix{margin-left:4px}.view-switch{flex-shrink:0;margin-left:0}.view-switch button{height:28px}.filter.hidden{display:none}.section-index.hidden{visibility:hidden}.context{border-top:2px solid var(--rule-strong);margin-top:4px;padding-top:12px}.context-summary{margin:0 0 4px;font-size:15px}.context>.muted{max-width:80ch;margin:0 0 12px}.context-large{white-space:normal;margin:0 0 12px;padding:6px 10px;line-height:1.5}.context-bar{gap:2px;height:8px;margin:0 0 8px;display:flex}.context-bar span{border-radius:2px;min-width:3px}.bar-managed{background:var(--line-managed)}.bar-user{background:var(--line-user)}.bar-project{background:var(--line-project)}.bar-local{background:var(--line-local)}.bar-memory{background:var(--line-memory)}.context-list{margin:0;padding:0;list-style:none}.context-list li{border-bottom:1px solid var(--rule)}.context-list summary{min-height:var(--row);cursor:pointer;grid-template-columns:28px 26px minmax(0,1fr) 84px 64px 64px;align-items:center;gap:8px;padding:6px 4px;list-style:none;display:grid}.context-list summary::-webkit-details-marker{display:none}.context-list summary:hover{background:var(--hover)}.context-list summary .num{font-family:var(--mono);text-align:right;color:var(--ink-2);font-size:13px}.context-list summary .num:first-child{text-align:left;color:var(--muted)}.context-name{flex-direction:column;min-width:0;display:flex}.context-name .mono{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.context-list details[open] summary{background:var(--sunk)}.context-list pre.raw{max-height:60vh;margin:8px 0 12px;overflow-y:auto}.context-hidden{margin:8px 4px 12px}.sharing{flex-wrap:wrap;gap:6px 24px;margin:0 0 4px;font-size:13.5px;display:flex}.sharing-group{align-items:baseline;gap:8px;min-width:0;display:flex}.sharing dt{white-space:nowrap}.sharing dd{color:var(--ink-2);flex-wrap:wrap;gap:2px 14px;margin:0;display:flex}.sharing dd span{white-space:nowrap}.problems{background:var(--warn-bg);color:var(--warn);border-radius:6px;align-items:flex-start;gap:8px;margin:0 0 12px;padding:8px 12px;font-size:13.5px;display:flex}.problems ul{overflow-wrap:anywhere;margin:0}@media (prefers-reduced-motion:reduce){*,:before{transition:none!important;animation:none!important}}@media (width>=1181px) and (width<=1400px){:root{--rail:220px;--inspector:360px}.tt-instructions :is(th,td):nth-child(6){display:none}}@media (width<=1180px){.workspace{grid-template-columns:minmax(0,1fr)}.inspector{width:min(var(--inspector), 100vw);z-index:10;position:fixed;top:0;bottom:0;right:0;box-shadow:-16px 0 40px -16px #00000059}.rail-keys{display:none}}@media (width<=760px){.app{grid-template-rows:auto minmax(0,1fr);grid-template-columns:minmax(0,1fr)}.rail{border-right:0;border-bottom:1px solid var(--rule);max-height:34vh}.work-main,.page{padding-left:16px;padding-right:16px}.filter{flex:160px;width:auto}.toolbar{flex-wrap:wrap;width:100%}.section-index{flex-wrap:wrap}.sharing-group{flex-direction:column;gap:2px}.dir-head{grid-template-rows:auto;grid-template-columns:minmax(0,1fr);height:auto;padding-top:18px;position:static}.dir-title{flex-wrap:wrap}.tt-section{scroll-margin-top:0}.palette-button{display:none}.context-list summary{grid-template-columns:22px 26px minmax(0,1fr) 56px}.context-list summary .num:nth-child(4),.context-list summary .num:last-child{display:none}.route-stops{flex-direction:column}.route-stops:before{width:3px;height:auto;inset:22px auto 22px 19px}.stop button{grid-template-rows:auto;grid-template-columns:30px auto 1fr;align-items:center;padding:6px 8px}.tt th:nth-child(n){width:auto}.tt th:first-child:nth-child(n),.tt-instructions th:nth-child(2):nth-child(n){width:40px}.tt-settings th:first-child:nth-child(n){width:64px}.tt-compare,.tt-compare :is(thead,tbody){display:block}.tt-compare tr{grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.tt-compare th:first-child{display:none}.tt-compare td:first-child{border-bottom:0;grid-column:1/-1;padding-bottom:0}.tt-instructions th:first-child,.tt-instructions td:first-child,.tt-instructions :is(th,td):is(:nth-child(5),:nth-child(6)),.tt-settings :is(th,td):nth-child(4),.tt-memory :is(th,td):is(:nth-child(3),:nth-child(4),:nth-child(6)),.tt-permissions :is(th,td):nth-child(4),.tt-tools :is(th,td):is(:nth-child(4),:nth-child(6)),.tt-hooks :is(th,td):nth-child(4),.tt-mcp :is(th,td):nth-child(4),.tt-extensions :is(th,td):nth-child(4),.tt-shared :is(th,td):nth-child(n+4){display:none}.tt-instructions th:nth-child(4):nth-child(n){width:72px}.tt-instructions th:nth-child(7):nth-child(n){width:84px}.tt th{box-shadow:none;position:static}}.head-action{border:1px solid var(--rule);background:var(--panel);border-radius:6px;align-items:center;gap:6px;height:26px;margin-left:auto;padding:0 10px;font-size:13.5px;display:inline-flex}.head-action:hover{border-color:var(--ink-2)}.editor{gap:10px;margin:0 0 18px;display:grid}.editor>p{margin:0}.hint{color:var(--ink-2);font-size:14px}.editor .segmented{justify-self:start;margin-left:0}.field{color:var(--muted);gap:4px;font-size:13.5px;display:grid}.field input:not([type=checkbox]),.field select,.field textarea,.path-form input{border:1px solid var(--rule);background:var(--paper);width:100%;color:var(--ink);border-radius:6px;padding:6px 8px;font-size:14px}.field input:not([type=checkbox]),.field select,.path-form input{height:32px}.field textarea{resize:vertical;line-height:1.5}fieldset.field{border:0;min-width:0;margin:0;padding:0}.object-fields{border-left:1px solid var(--rule);gap:12px;padding-left:12px;display:grid}.object-field{gap:4px;display:grid}.object-field .flag{flex-wrap:wrap;justify-self:start;gap:4px 8px;padding:4px 8px}.about summary{cursor:pointer}.about summary:hover{color:var(--ink-2)}.field input:focus,.field select:focus,.field textarea:focus,.path-form input:focus{outline:2px solid var(--focus);outline-offset:-1px}.check{color:var(--ink);align-items:center;gap:8px;font-size:14px;display:inline-flex}.check input{width:16px;height:16px;accent-color:var(--ink);margin:0}.editor-buttons{flex-wrap:wrap;gap:8px;display:flex}.editor-buttons button,.path-form button{border:1px solid var(--rule);background:var(--panel);border-radius:6px;height:30px;padding:0 12px;font-size:14px}.editor-buttons button:hover,.path-form button:hover{border-color:var(--ink-2)}.editor-buttons button.primary{background:var(--ink);border-color:var(--ink);color:var(--paper);font-weight:600}.key-picker{position:relative}.key-results{border:1px solid var(--rule);background:var(--panel);border-radius:6px;max-height:280px;margin-top:4px;padding:4px;overflow-y:auto}.key-results [role=option]{cursor:pointer;border-radius:4px;gap:1px;padding:6px 8px;font-size:13.5px;display:grid}.key-results [role=option] .muted{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.key-results [role=option].active{background:var(--sel);color:var(--sel-ink)}.review{border-top:1px solid var(--rule-strong);gap:8px;padding-top:10px;display:grid}.suggestions{border:0;flex-wrap:wrap;align-items:center;gap:4px;margin:0;padding:0;display:flex}.suggestions .muted{margin-right:4px}.suggestions button{border:1px solid var(--rule);background:var(--panel);border-radius:4px;height:24px;padding:0 8px;font-size:13px}.suggestions button[aria-pressed=true]{background:var(--ink);border-color:var(--ink);color:var(--paper)}.docs-link{align-items:center;gap:4px;display:inline-flex}.facet-select{border:1px solid var(--rule);background:var(--panel);max-width:260px;height:26px;color:var(--ink);border-radius:6px;padding:0 6px;font-size:13.5px}.catalog-files{white-space:nowrap}.catalog-files .bullet+.bullet{margin-left:2px}.docs-text>:first-child{margin-top:0}.docs-text>:last-child{margin-bottom:0}.tool-switch-rule{flex-wrap:wrap;align-items:center;gap:6px;padding:4px 0;display:flex}.tool-switch-rule .head-action{margin-left:auto}.tt-head .memory-review{flex-basis:100%}.memory-review-file{gap:4px;display:grid}.editor-buttons button.danger{background:var(--bad);border-color:var(--bad);color:var(--on-bad);font-weight:600}.review .sub-title{overflow-wrap:anywhere;margin:0}pre.diff{background:var(--sunk);border-radius:6px;max-height:360px;margin:0;padding:8px 0;line-height:1.5;overflow-x:auto}pre.diff span{white-space:pre;padding:0 10px;display:block}.diff-add{background:color-mix(in srgb, var(--ok) 16%, transparent);color:var(--ink)}.diff-remove{background:color-mix(in srgb, var(--bad) 16%, transparent);color:var(--ink);text-decoration:line-through;-webkit-text-decoration-color:color-mix(in srgb, var(--bad) 60%, transparent);text-decoration-color:color-mix(in srgb, var(--bad) 60%, transparent)}.diff-same{color:var(--muted)}.rail-empty{color:var(--muted);margin:2px 8px;font-size:14px}.rail-keys{color:var(--muted);gap:6px;margin:auto 8px 0;padding:24px 0 0;font-size:13px;list-style:none;display:grid}.visually-hidden{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.tt th .sort{height:100%;font:inherit;color:inherit;background:0 0;border:0;align-items:center;gap:4px;padding:0;display:inline-flex}.tt th .sort:hover,.tt th[aria-sort] .sort{color:var(--ink)}.sort-mark{font-family:var(--mono);opacity:0;font-size:12px;transition:opacity .12s}.tt th .sort:hover .sort-mark,.tt th .sort:focus-visible .sort-mark,.sort-mark.on{opacity:1}.show-more{border:0;border-bottom:1px solid var(--rule);text-align:left;background:0 0;width:100%;margin:0;padding:7px 8px;font-size:13.5px;font-weight:600;display:block}.show-more:hover{background:var(--hover)}.tt-head .filter{height:28px;margin-left:auto}.tool-outcome{font-size:13.5px}.tool-denied{color:var(--bad);font-weight:600}.tool-asks{color:var(--warn)}.tool-allowed{color:var(--ok);font-weight:600}.tool-free{color:var(--muted)}.diff-skip{color:var(--muted);font-style:italic}.tt-settings.no-overrides :is(th,td):nth-child(4){display:none}.head-action+.head-action{margin-left:0}.tt-compare th:first-child{width:28%}.tt-compare tbody tr{cursor:default}.tt-compare td{vertical-align:top;height:auto;padding-top:5px;padding-bottom:5px}.cmp-value,.cmp-from{text-overflow:ellipsis;display:block;overflow:hidden}.cmp-from{color:var(--muted);font-size:13px}.cmp-from>*+*{margin-left:8px}.change-day{max-width:980px;margin-top:22px}.change-day h2{border-top:2px solid var(--rule-strong);margin:0 0 4px;padding-top:8px;font-size:14px;font-weight:700}.changes{grid-template-columns:minmax(0,1fr);max-width:980px;display:grid}.changes .change{border-top:1px solid var(--rule);grid-template-columns:minmax(0,1fr);gap:10px;padding:10px 0;display:grid}.change-row{grid-template-columns:11rem minmax(0,1fr) 6rem 9rem;grid-template-areas:"what file when action";align-items:baseline;gap:6px 12px;font-size:14px;display:grid}.change-what{grid-area:what;font-weight:600}.change-file{grid-area:file;gap:2px;min-width:0;display:grid}.change-file .muted{font-size:12.5px}.change-row .num{grid-area:when}.change-row .head-action{grid-area:action;justify-self:end;margin-left:0}@media (width<=720px){.change-row{grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"what when""file file""action action"}.change-row .head-action{justify-self:start}}.path-tester{border-top:1px solid var(--rule);gap:8px;max-width:760px;margin-top:28px;padding-top:14px;display:grid}.path-tester .sub-title{margin:0}.path-form{gap:8px;display:flex}.path-form input{flex:1;min-width:0}.path-matches{gap:6px;font-size:14px;display:grid}.path-matches li{flex-wrap:wrap;align-items:baseline;gap:4px 10px;display:flex}
@@ -6,8 +6,8 @@
6
6
  <meta name="color-scheme" content="light dark" />
7
7
  <title>ccdesk</title>
8
8
  <link rel="icon" href="/logo.png" type="image/png" />
9
- <script type="module" crossorigin src="/assets/index-CGpaJLzd.js"></script>
10
- <link rel="stylesheet" crossorigin href="/assets/index-DM3TASgn.css">
9
+ <script type="module" crossorigin src="/assets/index-D3snAAMI.js"></script>
10
+ <link rel="stylesheet" crossorigin href="/assets/index-DcIoz4qK.css">
11
11
  </head>
12
12
  <body>
13
13
  <div id="root"></div>
@@ -14,4 +14,10 @@ export declare function formatter(home: string): Formatter;
14
14
  export declare function ago(iso: string | null): string;
15
15
  /** A one-line value for a table cell: JSON, minus the quotes on plain keys, with spaces to read by. */
16
16
  export declare function preview(value: unknown): string;
17
+ /**
18
+ * A description's first sentence and the rest, which is empty for a single sentence. A sentence
19
+ * ends at a full stop followed by a space and a capital or a quote, as in "true. Default", but not
20
+ * at the one in "e.g." or "i.e.", or one listed as a character, as in "_ - . , only".
21
+ */
22
+ export declare function firstSentence(text: string): [first: string, rest: string];
17
23
  export declare function plural(n: number, word: string, many?: string): string;
@@ -42,6 +42,15 @@ function compact(value) {
42
42
  }
43
43
  return JSON.stringify(value) ?? String(value);
44
44
  }
45
+ /**
46
+ * A description's first sentence and the rest, which is empty for a single sentence. A sentence
47
+ * ends at a full stop followed by a space and a capital or a quote, as in "true. Default", but not
48
+ * at the one in "e.g." or "i.e.", or one listed as a character, as in "_ - . , only".
49
+ */
50
+ export function firstSentence(text) {
51
+ const [, first, rest] = /^(.+?(?<!\b(?:e\.g|i\.e))\.)\s+(?=[A-Z"'`])(.+)$/s.exec(text) ?? [];
52
+ return first && rest ? [first, rest] : [text, ""];
53
+ }
45
54
  export function plural(n, word, many = `${word}s`) {
46
55
  return `${n} ${n === 1 ? word : many}`;
47
56
  }
@@ -1,11 +1,11 @@
1
1
  export { diffLines, withContext } from "./diff.ts";
2
- export { ago, type Formatter, formatter, plural, preview } from "./format.ts";
2
+ export { ago, type Formatter, firstSentence, formatter, plural, preview } from "./format.ts";
3
3
  export { LINES, type Line, lineName } from "./lines.ts";
4
- export { valueHere } from "./sections/catalog.ts";
5
4
  export { MATCH_KIND } from "./path-test.ts";
5
+ export { valueHere } from "./sections/catalog.ts";
6
6
  export { buildRows, SECTIONS } from "./sections/index.ts";
7
7
  export { isSuggested, unused } from "./sections/permissions.ts";
8
8
  export { callsOf, outcome } from "./sections/tools.ts";
9
9
  export type { Column, EntryKind, Facet, Row, RowOf, SectionId, ViewContext, ViewSection, } from "./sections/types.ts";
10
- export { type FieldKind, fieldKind, valuesIn } from "./settings.ts";
10
+ export { allowedValues, asForm, asJson, type Draft, type DraftObject, editKind, expects, type FieldKind, type Form, fieldKind, followFile, formFor, fromDraft, parseJson, type ReplacedList, replacedBuiltIns, toDraft, valuesIn, } from "./settings.ts";
11
11
  export { sessionsHere, suggestedRules } from "./usage.ts";
@@ -2,12 +2,12 @@
2
2
  // columns, filter text and labels, and the formatting they use. The page in src/ui adds the cells
3
3
  // and the inspector; a terminal UI can draw the same rows. See ARCHITECTURE.md.
4
4
  export { diffLines, withContext } from "./diff.js";
5
- export { ago, formatter, plural, preview } from "./format.js";
5
+ export { ago, firstSentence, formatter, plural, preview } from "./format.js";
6
6
  export { LINES, lineName } from "./lines.js";
7
- export { valueHere } from "./sections/catalog.js";
8
7
  export { MATCH_KIND } from "./path-test.js";
8
+ export { valueHere } from "./sections/catalog.js";
9
9
  export { buildRows, SECTIONS } from "./sections/index.js";
10
10
  export { isSuggested, unused } from "./sections/permissions.js";
11
11
  export { callsOf, outcome } from "./sections/tools.js";
12
- export { fieldKind, valuesIn } from "./settings.js";
12
+ export { allowedValues, asForm, asJson, editKind, expects, fieldKind, followFile, formFor, fromDraft, parseJson, replacedBuiltIns, toDraft, valuesIn, } from "./settings.js";
13
13
  export { sessionsHere, suggestedRules } from "./usage.js";
@@ -1,7 +1,86 @@
1
1
  import { type SettingSchema } from "../shared/types.ts";
2
- /** How a setting is edited: typed controls where the schema allows it, JSON otherwise. */
3
- export type FieldKind = "boolean" | "enum" | "number" | "string" | "lines" | "json";
2
+ /**
3
+ * How a setting is edited: typed controls where the schema allows it, JSON otherwise. An object
4
+ * gets a form when each property it names has a control of its own.
5
+ */
6
+ export type FieldKind = "boolean" | "enum" | "number" | "string" | "lines" | "choices" | "object" | "json";
7
+ /**
8
+ * A value as a form holds it: text for inputs, a boolean for a checkbox, the values picked from a
9
+ * list of choices, and an object form's properties by name. A property of an object form may be
10
+ * left unset, so its boolean is "true", "false" or "".
11
+ */
12
+ export type Draft = string | boolean | string[] | DraftObject;
13
+ export interface DraftObject {
14
+ [name: string]: Draft;
15
+ }
4
16
  export declare function fieldKind(property: SettingSchema | undefined): FieldKind;
17
+ /**
18
+ * The control a value is edited with: its setting's field kind, or JSON when that control cannot
19
+ * hold the value exactly as the file has it, such as an object with a property the schema does not
20
+ * name or a value of another type. Saving from a control so never changes what you did not touch.
21
+ */
22
+ export declare function editKind(property: SettingSchema | undefined, value: unknown): FieldKind;
23
+ /** A value being edited: its control and what that control holds. */
24
+ export interface Form {
25
+ kind: FieldKind;
26
+ draft: Draft;
27
+ }
28
+ /** The form for a value as the file has it. */
29
+ export declare function formFor(property: SettingSchema | undefined, value: unknown): Form;
30
+ /**
31
+ * The form once the value it is reviewed against changes from `was` to `now`: Claude Code or an
32
+ * editor wrote the file, or you picked another file. A form that still holds the old value follows
33
+ * the new one, as does one that holds the new value, as after saving. One you have edited is kept,
34
+ * control and all, to be reviewed against the new value. That is a `conflict` when the same file
35
+ * changed under you; picking another file means writing your value there.
36
+ */
37
+ export declare function followFile(form: Form, property: SettingSchema | undefined, was: unknown, now: unknown, otherFile?: boolean): {
38
+ form: Form;
39
+ conflict: boolean;
40
+ };
41
+ /** The form's value as JSON, to write what the controls cannot, such as an empty list. */
42
+ export declare function asJson(form: Form, property: SettingSchema | undefined): Form | {
43
+ error: string;
44
+ };
45
+ /** JSON back in the setting's controls, when they can hold the value exactly. */
46
+ export declare function asForm(form: Form, property: SettingSchema | undefined): Form | {
47
+ error: string;
48
+ };
49
+ /** An autoMode list that drops Claude Code's built-in entries; an empty one keeps none at all. */
50
+ export interface ReplacedList {
51
+ list: string;
52
+ empty: boolean;
53
+ }
54
+ /**
55
+ * The lists in a setting's value that replace Claude Code's built-in entries instead of adding to
56
+ * them: autoMode lists without "$defaults", empty ones included. A list left out keeps the
57
+ * built-in ones. Read from the value, so the form and JSON warn alike.
58
+ */
59
+ export declare function replacedBuiltIns(key: string, value: unknown): ReplacedList[];
60
+ /** The values a schema allows, when it lists them: its enum, or its const as a list of one. */
61
+ export declare function allowedValues(schema: SettingSchema): unknown[] | undefined;
62
+ /** A value as the control for `kind` starts out. `inObject` is for a property of an object form. */
63
+ export declare function toDraft(kind: FieldKind, property: SettingSchema | undefined, value: unknown, inObject?: boolean): Draft;
64
+ /**
65
+ * The value a draft stands for, or what to fix. A property of an object form left empty is unset,
66
+ * its value undefined.
67
+ */
68
+ export declare function fromDraft(kind: FieldKind, property: SettingSchema | undefined, draft: Draft, inObject?: boolean): {
69
+ value: unknown;
70
+ } | {
71
+ error: string;
72
+ };
73
+ /** JSON typed into a form, or why it does not parse. */
74
+ export declare function parseJson(text: string): {
75
+ value: unknown;
76
+ } | {
77
+ error: string;
78
+ };
79
+ /**
80
+ * What a value must be, in JSON's terms: "true or false, or an object with commit, pr and
81
+ * sessionUrl". Shown beside a JSON field, so you know the shape before the review checks it.
82
+ */
83
+ export declare function expects(schema: SettingSchema): string;
5
84
  /**
6
85
  * The string values a settings reference type lists, such as theme's "dark" and "light": offered as
7
86
  * choices where the schema allows any string. In a bulleted list only the value opening each bullet
@@ -2,18 +2,283 @@ import {} from "../shared/types.js";
2
2
  export function fieldKind(property) {
3
3
  if (!property)
4
4
  return "json";
5
- if (property.enum?.every((v) => typeof v === "string"))
5
+ if (allowedValues(property)?.every((v) => typeof v === "string"))
6
6
  return "enum";
7
- if (property.type === "boolean")
7
+ const type = typeOf(property);
8
+ if (type === "boolean")
8
9
  return "boolean";
9
- if (property.type === "number" || property.type === "integer")
10
+ if (type === "number" || type === "integer")
10
11
  return "number";
11
- if (property.type === "string")
12
+ if (type === "string")
12
13
  return "string";
13
- if (property.type === "array" && property.items?.type === "string" && !property.items.enum)
14
- return "lines";
14
+ if (type === "array" && property.items && typeOf(property.items) === "string") {
15
+ const values = allowedValues(property.items);
16
+ if (!values)
17
+ return "lines";
18
+ if (values.every((v) => typeof v === "string"))
19
+ return "choices";
20
+ }
21
+ const properties = Object.values(property.properties ?? {});
22
+ if (type === "object" && properties.length > 0 && properties.every(hasOwnControl))
23
+ return "object";
15
24
  return "json";
16
25
  }
26
+ /**
27
+ * The control a value is edited with: its setting's field kind, or JSON when that control cannot
28
+ * hold the value exactly as the file has it, such as an object with a property the schema does not
29
+ * name or a value of another type. Saving from a control so never changes what you did not touch.
30
+ */
31
+ export function editKind(property, value) {
32
+ const kind = fieldKind(property);
33
+ if (kind === "json" || value === undefined)
34
+ return kind;
35
+ const back = fromDraft(kind, property, toDraft(kind, property, value));
36
+ return "value" in back && JSON.stringify(back.value) === JSON.stringify(value) ? kind : "json";
37
+ }
38
+ /** The form for a value as the file has it. */
39
+ export function formFor(property, value) {
40
+ const kind = editKind(property, value);
41
+ return { kind, draft: toDraft(kind, property, value) };
42
+ }
43
+ /**
44
+ * The form once the value it is reviewed against changes from `was` to `now`: Claude Code or an
45
+ * editor wrote the file, or you picked another file. A form that still holds the old value follows
46
+ * the new one, as does one that holds the new value, as after saving. One you have edited is kept,
47
+ * control and all, to be reviewed against the new value. That is a `conflict` when the same file
48
+ * changed under you; picking another file means writing your value there.
49
+ */
50
+ export function followFile(form, property, was, now, otherFile = false) {
51
+ if (!holds(form, property, was) && !holds(form, property, now))
52
+ return { form, conflict: !otherFile };
53
+ return { form: formFor(property, now), conflict: false };
54
+ }
55
+ /**
56
+ * Whether a form stands for a file's value: it is that value's form, such as the empty field for a
57
+ * key the file lacks, or it holds that value in another control, such as JSON you switched to.
58
+ */
59
+ function holds(form, property, value) {
60
+ const own = formFor(property, value);
61
+ if (own.kind === form.kind && JSON.stringify(own.draft) === JSON.stringify(form.draft))
62
+ return true;
63
+ const held = fromDraft(form.kind, property, form.draft);
64
+ return "value" in held && JSON.stringify(held.value) === JSON.stringify(value);
65
+ }
66
+ /** The form's value as JSON, to write what the controls cannot, such as an empty list. */
67
+ export function asJson(form, property) {
68
+ const parsed = fromDraft(form.kind, property, form.draft);
69
+ if ("error" in parsed)
70
+ return parsed;
71
+ return { kind: "json", draft: toDraft("json", property, parsed.value) };
72
+ }
73
+ /** JSON back in the setting's controls, when they can hold the value exactly. */
74
+ export function asForm(form, property) {
75
+ const parsed = fromDraft("json", property, form.draft);
76
+ if ("error" in parsed)
77
+ return parsed;
78
+ const kind = editKind(property, parsed.value);
79
+ if (kind === "json")
80
+ return { error: "The form cannot hold this value exactly; edit it as JSON." };
81
+ return { kind, draft: toDraft(kind, property, parsed.value) };
82
+ }
83
+ /**
84
+ * autoMode's lists, each of which replaces Claude Code's built-in entries unless it includes
85
+ * "$defaults", even when it is empty (docs: settings-reference#automode,
86
+ * auto-mode-config#override-the-block-and-allow-rules).
87
+ */
88
+ const AUTO_MODE_LISTS = ["allow", "soft_deny", "environment", "hard_deny"];
89
+ /**
90
+ * The lists in a setting's value that replace Claude Code's built-in entries instead of adding to
91
+ * them: autoMode lists without "$defaults", empty ones included. A list left out keeps the
92
+ * built-in ones. Read from the value, so the form and JSON warn alike.
93
+ */
94
+ export function replacedBuiltIns(key, value) {
95
+ if (key !== "autoMode" || !isObject(value))
96
+ return [];
97
+ return AUTO_MODE_LISTS.flatMap((list) => {
98
+ const entries = value[list];
99
+ if (!Array.isArray(entries) || entries.includes("$defaults"))
100
+ return [];
101
+ return [{ list, empty: entries.length === 0 }];
102
+ });
103
+ }
104
+ /** The values a schema allows, when it lists them: its enum, or its const as a list of one. */
105
+ export function allowedValues(schema) {
106
+ if (schema.enum)
107
+ return schema.enum;
108
+ return "const" in schema ? [schema.const] : undefined;
109
+ }
110
+ /** The schema's one type, taking "string or null" as string: no control writes null. */
111
+ function typeOf(schema) {
112
+ if (!Array.isArray(schema.type))
113
+ return schema.type;
114
+ const types = schema.type.filter((t) => t !== "null");
115
+ return types.length === 1 ? types[0] : undefined;
116
+ }
117
+ /** A property an object form can show: one with a control, not JSON or another object. */
118
+ function hasOwnControl(property) {
119
+ const kind = fieldKind(property);
120
+ return kind !== "json" && kind !== "object";
121
+ }
122
+ /** A value as the control for `kind` starts out. `inObject` is for a property of an object form. */
123
+ export function toDraft(kind, property, value, inObject = false) {
124
+ if (kind === "boolean") {
125
+ if (!inObject)
126
+ return value === true;
127
+ return typeof value === "boolean" ? String(value) : "";
128
+ }
129
+ if (kind === "choices")
130
+ return Array.isArray(value) ? value.map(String) : [];
131
+ if (kind === "object")
132
+ return objectDraft(property ?? {}, value);
133
+ if (value === undefined)
134
+ return "";
135
+ if (kind === "lines")
136
+ return Array.isArray(value) ? value.join("\n") : "";
137
+ if (kind === "json")
138
+ return JSON.stringify(value, null, 2);
139
+ return String(value);
140
+ }
141
+ /**
142
+ * Each property the schema names, in the file's order and then the schema's, so a save leaves the
143
+ * file's order alone. A new object starts with the one value a required property allows, such as
144
+ * statusLine's type "command".
145
+ */
146
+ function objectDraft(schema, value) {
147
+ const properties = schema.properties ?? {};
148
+ const file = isObject(value) ? value : {};
149
+ const names = [...new Set([...Object.keys(file), ...Object.keys(properties)])];
150
+ const draft = {};
151
+ for (const name of names) {
152
+ // Only the schema's own names: a file's key such as "constructor" is not a property.
153
+ const property = Object.hasOwn(properties, name) ? properties[name] : undefined;
154
+ if (!property)
155
+ continue;
156
+ const only = allowedValues(property);
157
+ const start = value === undefined && schema.required?.includes(name) && only?.length === 1
158
+ ? only[0]
159
+ : file[name];
160
+ draft[name] = toDraft(fieldKind(property), property, start, true);
161
+ }
162
+ return draft;
163
+ }
164
+ /**
165
+ * The value a draft stands for, or what to fix. A property of an object form left empty is unset,
166
+ * its value undefined.
167
+ */
168
+ export function fromDraft(kind, property, draft, inObject = false) {
169
+ if (inObject && (draft === "" || (Array.isArray(draft) && draft.length === 0)))
170
+ return { value: undefined };
171
+ // A draft's shape follows from its kind (see toDraft); text drafts are told apart by kind.
172
+ if (typeof draft === "boolean")
173
+ return { value: draft };
174
+ if (Array.isArray(draft)) {
175
+ const allowed = (property?.items && allowedValues(property.items)) ?? [];
176
+ return draft.every((v) => allowed.includes(v)) ? { value: draft } : { error: "Pick a value." };
177
+ }
178
+ if (typeof draft === "object")
179
+ return objectFromDraft(property ?? {}, draft);
180
+ if (kind === "boolean")
181
+ return { value: draft === "true" };
182
+ if (kind === "number") {
183
+ const n = Number(draft);
184
+ return draft.trim() === "" || Number.isNaN(n) ? { error: "Enter a number." } : { value: n };
185
+ }
186
+ if (kind === "lines")
187
+ return {
188
+ value: draft
189
+ .split("\n")
190
+ .map((l) => l.trim())
191
+ .filter(Boolean),
192
+ };
193
+ if (kind === "enum")
194
+ return property && allowedValues(property)?.includes(draft)
195
+ ? { value: draft }
196
+ : { error: "Pick a value." };
197
+ if (kind === "json")
198
+ return jsonFromDraft(property, draft);
199
+ // A text field holds one line, so text with a line break is edited as JSON.
200
+ if (/[\r\n]/.test(draft))
201
+ return { error: "Text with a line break is edited as JSON." };
202
+ return { value: draft };
203
+ }
204
+ function objectFromDraft(schema, draft) {
205
+ const value = {};
206
+ for (const [name, part] of Object.entries(draft)) {
207
+ const property = schema.properties?.[name];
208
+ const parsed = fromDraft(fieldKind(property), property, part, true);
209
+ if ("error" in parsed)
210
+ return { error: `${name}: ${parsed.error}` };
211
+ if (parsed.value !== undefined)
212
+ value[name] = parsed.value;
213
+ }
214
+ return { value };
215
+ }
216
+ /** JSON typed for a setting, or why it does not parse, with how to write text where text is allowed. */
217
+ function jsonFromDraft(property, draft) {
218
+ const parsed = parseJson(draft);
219
+ if (!("error" in parsed))
220
+ return parsed;
221
+ const text = draft.trim();
222
+ const quote = property && text !== "" && takesText(property)
223
+ ? `. Text goes in double quotes: ${JSON.stringify(text)}`
224
+ : "";
225
+ return { error: `Not valid JSON: ${parsed.error}${quote}` };
226
+ }
227
+ function takesText(schema) {
228
+ if (schema.anyOf)
229
+ return schema.anyOf.some(takesText);
230
+ return [schema.type ?? []].flat().includes("string");
231
+ }
232
+ /** JSON typed into a form, or why it does not parse. */
233
+ export function parseJson(text) {
234
+ try {
235
+ return { value: JSON.parse(text) };
236
+ }
237
+ catch (error) {
238
+ return { error: error instanceof Error ? error.message : String(error) };
239
+ }
240
+ }
241
+ /**
242
+ * What a value must be, in JSON's terms: "true or false, or an object with commit, pr and
243
+ * sessionUrl". Shown beside a JSON field, so you know the shape before the review checks it.
244
+ */
245
+ export function expects(schema) {
246
+ if (schema.anyOf)
247
+ return schema.anyOf.map(expects).join(", or ");
248
+ const values = allowedValues(schema);
249
+ if (values)
250
+ return `one of ${values.map((v) => JSON.stringify(v)).join(", ")}`;
251
+ const types = [schema.type ?? []].flat();
252
+ if (types.length === 0)
253
+ return "any value";
254
+ return types.map((type) => typeExpected(type, schema)).join(" or ");
255
+ }
256
+ function typeExpected(type, schema) {
257
+ if (type === "boolean")
258
+ return "true or false";
259
+ if (type === "number" || type === "integer")
260
+ return "a number";
261
+ if (type === "null")
262
+ return "null";
263
+ if (type === "array")
264
+ return schema.items ? `a list, each ${expects(schema.items)}` : "a list";
265
+ if (type !== "object")
266
+ return `a ${type}`;
267
+ const names = Object.keys(schema.properties ?? {});
268
+ if (names.length > 0)
269
+ return `an object with ${listOf(names)}`;
270
+ const each = schema.additionalProperties;
271
+ return typeof each === "object" ? `an object mapping each name to ${expects(each)}` : "an object";
272
+ }
273
+ /** Names as a sentence lists them, the first four and how many more. */
274
+ function listOf(names) {
275
+ if (names.length > 4)
276
+ return `${names.slice(0, 4).join(", ")} and ${names.length - 4} more`;
277
+ return names.length > 1 ? `${names.slice(0, -1).join(", ")} and ${names.at(-1)}` : `${names[0]}`;
278
+ }
279
+ function isObject(value) {
280
+ return typeof value === "object" && value !== null && !Array.isArray(value);
281
+ }
17
282
  /**
18
283
  * The string values a settings reference type lists, such as theme's "dark" and "light": offered as
19
284
  * choices where the schema allows any string. In a bulleted list only the value opening each bullet
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccdesk",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "See and change everything Claude Code loads on your machine: instruction files, settings, permissions, hooks, MCP servers, subagents, skills, plugins and auto memory",
5
5
  "keywords": [
6
6
  "claude-code",