outcrop 0.1.0 → 0.1.2
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/README.md +1 -0
- package/dist/cli.js +1137 -270
- package/docs/AGENTS.md +1 -0
- package/docs/API.md +36 -1
- package/docs/SPEC.md +9 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -42,7 +42,7 @@ function writeSettings(file, s) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// src/styles.gen.css
|
|
45
|
-
var styles_gen_default = "*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\"\"}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-ui);font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.detail-body h1,.detail-body h2,.detail-body h3,.detail-body h4{margin-top:1.25rem;margin-bottom:.375rem;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.detail-body h1:first-child,.detail-body h2:first-child,.detail-body h3:first-child,.detail-body h4:first-child{margin-top:0}.detail-body h1:is(.dark *),.detail-body h2:is(.dark *),.detail-body h3:is(.dark *),.detail-body h4:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body p{margin-top:.375rem;margin-bottom:.375rem;font-size:13px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body p:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body ul{margin-top:.375rem;margin-bottom:.375rem}.detail-body ul>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.detail-body ul{padding-left:1rem;font-size:13px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body ul:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body ul:not(.ordered)>li{list-style-type:disc}.detail-body li.check{margin-left:-1rem;display:flex;list-style-type:none;align-items:flex-start;gap:.5rem}.detail-body li.check .box{margin-top:.125rem;display:flex;height:15px;width:15px;flex-shrink:0;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));font-size:10px;line-height:1;--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.detail-body li.check .box:hover{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.detail-body li.check .box:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.detail-body li.check .box:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.detail-body li.check.done{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));text-decoration-line:line-through}.detail-body li.check.done:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body li.check.done .box{border-color:rgba(16,185,129,.5);--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.detail-body li.check.done .box:is(.dark *){background-color:rgba(16,185,129,.1)}.detail-body ul.ordered>li{list-style-type:none}.detail-body code{border-radius:.25rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:1px .25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body code:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body pre{margin-top:.5rem;margin-bottom:.5rem;overflow-x:auto;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body pre:is(.dark *){background-color:rgba(39,39,42,.8);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body .ref{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.detail-body .ref:hover{text-decoration-line:underline}.detail-body .ref:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.detail-body table{margin-top:.5rem;margin-bottom:.5rem;width:100%;border-collapse:collapse;font-size:12.5px}.detail-body th{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding-top:.25rem;padding-bottom:.25rem;padding-right:.75rem;text-align:left;font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.detail-body th:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body td{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding-top:.375rem;padding-bottom:.375rem;padding-right:.75rem;vertical-align:top;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body td:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body a.mdlink{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.detail-body a.mdlink:hover{text-decoration-line:underline}.detail-body a.mdlink:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.doc-body h1{margin-bottom:.75rem;margin-top:.5rem;font-size:21px;font-weight:600;letter-spacing:-.025em;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body h1:is(.dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.doc-body h2{margin-bottom:.5rem;margin-top:1.75rem;border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding-bottom:.375rem;font-size:16px;font-weight:600;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body h2:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.doc-body h3{margin-bottom:.375rem;margin-top:1.25rem;font-size:13.5px;font-weight:600;--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.doc-body h3:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.doc-body h4{margin-bottom:.25rem;margin-top:1rem;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-body h4:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body p{margin-top:.5rem;margin-bottom:.5rem;font-size:13.5px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body p:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body ul{margin-top:.5rem;margin-bottom:.5rem}.doc-body ul>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.doc-body ul{padding-left:1.25rem;font-size:13.5px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body ul:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body ul:not(.ordered)>li{list-style-type:disc}.doc-body ul.ordered>li{list-style-type:none}.doc-body li.check{margin-left:-1.25rem;display:flex;list-style-type:none;align-items:flex-start;gap:.5rem}.doc-body li.check .box{margin-top:.25rem;display:flex;height:14px;width:14px;flex-shrink:0;align-items:center;justify-content:center;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));font-size:9px;line-height:1;--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.doc-body li.check .box:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.doc-body li.check.done{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));text-decoration-line:line-through}.doc-body li.check.done:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body code{border-radius:.25rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:1px .25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body code:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body pre{margin-top:.75rem;margin-bottom:.75rem;overflow-x:auto;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body pre:is(.dark *){background-color:rgba(39,39,42,.8);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body table{margin-top:.75rem;margin-bottom:.75rem;width:100%;border-collapse:collapse;font-size:13px}.doc-body th{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));padding-top:.375rem;padding-bottom:.375rem;padding-right:1rem;text-align:left;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-body th:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body td{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;vertical-align:top;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body td:is(.dark *){border-color:rgba(39,39,42,.6);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body img{margin-left:auto;margin-right:auto;display:inline-block;height:auto;max-width:100%}.doc-body [align=center]{text-align:center}.doc-body [align=right]{text-align:right}.doc-body .ref,.doc-body a.mdlink{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.doc-body .ref:hover,.doc-body a.mdlink:hover{text-decoration-line:underline}.doc-body .ref:is(.dark *),.doc-body a.mdlink:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.doc-body .ref{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12.5px}.doc-body .mermaid{margin-top:1rem;margin-bottom:1rem;display:flex;justify-content:center;overflow-x:auto}.doc-body strong{font-weight:600;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body strong:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.detail-body hr,.doc-body hr{margin-top:1.5rem;margin-bottom:1.5rem;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.detail-body hr:is(.dark *),.doc-body hr:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.doc-folder>summary{display:flex;cursor:pointer;list-style-type:none;align-items:center;gap:.375rem;border-radius:.375rem;padding:.25rem .5rem;font-size:13.5px;font-weight:600;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-folder>summary:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.doc-folder>summary:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.doc-folder>summary:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.doc-folder>summary::-webkit-details-marker{display:none}.doc-folder>summary:before{content:\"›\";display:inline-block;width:.5rem;flex-shrink:0;font-size:13px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-folder>summary:is(.dark *):before{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.doc-folder[open]>summary:before{transform:rotate(90deg)}.doc-folder-count{margin-left:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:10px;font-weight:400;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-folder-count:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.doc-folder+.doc-folder{margin-top:.375rem}.doc-link{margin-left:9px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-radius:.375rem;border-left-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding:.25rem .5rem .25rem .625rem;font-size:12px;--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-link:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.doc-link:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-link:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.doc-link.active{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));font-weight:500;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-link.active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}#doc-outline.empty{display:none}.outline-link{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-left-width:2px;border-color:transparent;padding:.25rem .25rem .25rem .625rem;font-size:12px;line-height:1.375;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.outline-link:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.outline-link:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.outline-link:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.outline-link.lvl-2{padding-left:1.25rem}.outline-link.lvl-3{padding-left:2rem;font-size:11.5px}.outline-link.active{--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));font-weight:500;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.outline-link.active:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}#doc-sidebar.collapsed,#doc-sidebar.collapsed+.sidebar-resizer{display:none!important}#search-results mark{border-radius:.125rem;--tw-bg-opacity:1;background-color:rgb(var(--accent-200)/var(--tw-bg-opacity,1));padding-left:.125rem;padding-right:.125rem;font-weight:500;--tw-text-opacity:1;color:rgb(var(--accent-950)/var(--tw-text-opacity,1))}#search-results mark:is(.dark *){background-color:rgb(var(--accent-600)/.5);--tw-text-opacity:1;color:rgb(var(--accent-100)/var(--tw-text-opacity,1))}::highlight(doc-search){background-color:rgb(var(--accent-300));color:rgb(var(--accent-950))}.dark ::highlight(doc-search){background-color:rgb(var(--accent-600));color:#fff}::highlight(doc-match){background-color:rgb(var(--accent-500));color:#fff}.dark ::highlight(doc-match){background-color:rgb(var(--accent-300));color:rgb(var(--accent-950))}.swatch{height:1.5rem;width:1.5rem;border-radius:9999px;border-width:2px;border-color:transparent;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgb(228 228 231/var(--tw-ring-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.swatch:hover{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.swatch:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(63 63 70/var(--tw-ring-opacity,1))}.swatch.active{--tw-border-opacity:1;border-color:rgb(24 24 27/var(--tw-border-opacity,1))}.swatch.active:is(.dark *){--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.swatch-violet{background-color:#8b5cf6}.swatch-blue{background-color:#3b82f6}.swatch-emerald{background-color:#10b981}.swatch-amber{background-color:#f59e0b}.swatch-rose{background-color:#f43f5e}.pick{flex:1 1 0%;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:.25rem .5rem;font-size:12px;--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.pick:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.pick:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.pick:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.pick.active{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--accent-700)/var(--tw-text-opacity,1))}.pick.active:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));background-color:rgb(var(--accent-500)/.1);--tw-text-opacity:1;color:rgb(var(--accent-300)/var(--tw-text-opacity,1))}.theme-opt.theme-active{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--accent-700)/var(--tw-text-opacity,1))}.theme-opt.theme-active:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));background-color:rgb(var(--accent-500)/.1);--tw-text-opacity:1;color:rgb(var(--accent-300)/var(--tw-text-opacity,1))}.set-group{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.set-group:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.set-group>summary{display:flex;cursor:pointer;list-style-type:none;align-items:center;gap:.5rem;padding-top:.625rem;padding-bottom:.625rem;font-size:12.5px;font-weight:500;--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.set-group>summary:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.set-group>summary::-webkit-details-marker{display:none}.set-group>summary:after{content:\"›\";margin-left:auto;font-size:15px;line-height:1;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.set-group>summary:is(.dark *):after{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.set-group[open]>summary:after{transform:rotate(90deg)}.set-sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:400;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.set-sub:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.set-row{display:flex;align-items:flex-start;gap:.75rem;padding-top:.375rem;padding-bottom:.375rem}.set-label{width:72px;flex-shrink:0;padding-top:.375rem;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.set-label:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}#sheet.fullscreen{max-width:none}#sheet.fullscreen #sheet-content{margin-left:auto;margin-right:auto;max-width:42rem}.task-card{width:100%;cursor:pointer;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));padding:.625rem .75rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.task-card:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.task-card:focus-visible{outline-style:solid;outline-width:2px;outline-offset:1px;outline-color:rgb(var(--accent-500)/1)}.task-card:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.task-card:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.card-id{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:11px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-id:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.card-title{font-size:13px;font-weight:500;line-height:1.375;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.card-title:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.card-progress{margin-left:auto;display:inline-flex;align-items:center;gap:.375rem;font-size:10px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-progress:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.epic-chip{border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:1px .375rem;font-size:10px;font-weight:500;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.epic-chip:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.epic-chip:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.epic-chip:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.prio{display:inline-flex;align-items:center;gap:.25rem;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.prio:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.prio-label{font-size:10px;font-weight:500;letter-spacing:.025em}.dep-chip{display:inline-flex;align-items:center;gap:.25rem;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:1px .375rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:10px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.dep-chip:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dep-chip:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dep-chip:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dep-chip.done{border-color:transparent;--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.dep-chip.done:hover{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.dep-chip.done:is(.dark *){background-color:rgba(16,185,129,.1);--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.dep-chip.done:hover:is(.dark *){background-color:rgba(16,185,129,.2)}.backlink-row{display:flex;width:100%;align-items:center;gap:.5rem;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding:.375rem .625rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.backlink-row:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.backlink-row:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.backlink-row:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.epic-row{display:flex;width:100%;cursor:pointer;align-items:center;gap:.5rem;border-radius:.375rem;padding:.375rem .5rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.epic-row:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.epic-row:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.epic-row.epic-active{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.epic-row.epic-active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}#sidebar>*{flex-shrink:0}#sidebar.collapsed,#sidebar.collapsed+.sidebar-resizer{display:none!important}section.drop-target{background-color:rgb(var(--accent-50)/.6);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-inset:inset;--tw-ring-opacity:1;--tw-ring-color:rgb(var(--accent-300)/var(--tw-ring-opacity,1))}section.drop-target:is(.dark *){background-color:rgb(var(--accent-500)/.05);--tw-ring-color:rgb(var(--accent-500)/0.4)}.task-card.\\!dragging,.task-card.dragging{opacity:.4}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.-top-0\\.5{top:-.125rem}.bottom-3{bottom:.75rem}.bottom-5{bottom:1.25rem}.left-1{left:.25rem}.left-1\\/2{left:50%}.left-3{left:.75rem}.left-44{left:11rem}.right-0{right:0}.right-3{right:.75rem}.right-4{right:1rem}.right-5{right:1.25rem}.top-0{top:0}.top-4{top:1rem}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.z-\\[60\\]{z-index:60}.mx-auto{margin-left:auto;margin-right:auto}.-ml-0\\.5{margin-left:-.125rem}.mb-1{margin-bottom:.25rem}.mb-1\\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-2\\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-8{margin-bottom:2rem}.ml-3{margin-left:.75rem}.ml-auto{margin-left:auto}.mr-1\\.5{margin-right:.375rem}.mr-16{margin-right:4rem}.mt-1{margin-top:.25rem}.mt-1\\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1\\.5{height:.375rem}.h-12{height:3rem}.h-2{height:.5rem}.h-3\\.5{height:.875rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-\\[calc\\(\\(100vh\\/var\\(--zoom\\)\\)-3rem\\)\\]{height:calc(100vh/var(--zoom) - 3rem)}.h-\\[calc\\(100vh\\/var\\(--zoom\\)\\)\\]{height:calc(100vh/var(--zoom))}.h-full{height:100%}.min-h-\\[60px\\]{min-height:60px}.w-1{width:.25rem}.w-1\\.5{width:.375rem}.w-2{width:.5rem}.w-3\\.5{width:.875rem}.w-44{width:11rem}.w-56{width:14rem}.w-60{width:15rem}.w-\\[290px\\]{width:290px}.w-full{width:100%}.min-w-0{min-width:0}.max-w-3xl{max-width:48rem}.max-w-\\[460px\\]{max-width:460px}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-x-1\\/2{--tw-translate-x:-50%}.-translate-x-1\\/2,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x:100%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-col-resize{cursor:col-resize}.cursor-grab{cursor:grab}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.resize{resize:both}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\\[90px_1fr\\]{grid-template-columns:90px 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-y-1\\.5{row-gap:.375rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-zinc-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(228 228 231/var(--tw-divide-opacity,1))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.border{border-width:1px}.border-y{border-top-width:1px}.border-b,.border-y{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-rose-300{--tw-border-opacity:1;border-color:rgb(253 164 175/var(--tw-border-opacity,1))}.border-rose-500\\/70{border-color:rgba(244,63,94,.7)}.border-zinc-100{--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.border-zinc-200{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.bg-accent-50{--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1))}.bg-accent-600{--tw-bg-opacity:1;background-color:rgb(var(--accent-600)/var(--tw-bg-opacity,1))}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.bg-black\\/20{background-color:rgba(0,0,0,.2)}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-rose-50{--tw-bg-opacity:1;background-color:rgb(255 241 242/var(--tw-bg-opacity,1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-zinc-100{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.bg-zinc-300{--tw-bg-opacity:1;background-color:rgb(212 212 216/var(--tw-bg-opacity,1))}.bg-zinc-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.bg-zinc-900{--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.p-0\\.5{padding:.125rem}.p-1{padding:.25rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-96{padding:24rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\\.5{padding-top:.875rem;padding-bottom:.875rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-px{padding-top:1px;padding-bottom:1px}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pl-2\\.5{padding-left:.625rem}.pr-1{padding-right:.25rem}.pr-3{padding-right:.75rem}.pt-1{padding-top:.25rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:var(--font-ui)}.font-serif{font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif}.text-\\[10\\.5px\\]{font-size:10.5px}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.text-\\[12\\.5px\\]{font-size:12.5px}.text-\\[12px\\]{font-size:12px}.text-\\[13px\\]{font-size:13px}.text-\\[16px\\]{font-size:16px}.text-\\[9\\.5px\\]{font-size:9.5px}.text-\\[9px\\]{font-size:9px}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-5{line-height:1.25rem}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-accent-600{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-rose-500{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.text-rose-700{--tw-text-opacity:1;color:rgb(190 18 60/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-zinc-100{--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.text-zinc-300{--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.text-zinc-400{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.text-zinc-500{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.text-zinc-600{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.text-zinc-700{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.text-zinc-800{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.text-zinc-900{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.accent-accent-600{accent-color:rgb(var(--accent-600)/1)}.opacity-0{opacity:0}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-sm,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}:root,html[data-accent=violet]{--accent-50:245 243 255;--accent-100:237 233 254;--accent-200:221 214 254;--accent-300:196 181 253;--accent-400:167 139 250;--accent-500:139 92 246;--accent-600:124 58 237;--accent-700:109 40 217;--accent-950:46 16 101}html[data-accent=blue]{--accent-50:239 246 255;--accent-100:219 234 254;--accent-200:191 219 254;--accent-300:147 197 253;--accent-400:96 165 250;--accent-500:59 130 246;--accent-600:37 99 235;--accent-700:29 78 216;--accent-950:23 37 84}html[data-accent=emerald]{--accent-50:236 253 245;--accent-100:209 250 229;--accent-200:167 243 208;--accent-300:110 231 183;--accent-400:52 211 153;--accent-500:16 185 129;--accent-600:5 150 105;--accent-700:4 120 87;--accent-950:2 44 34}html[data-accent=amber]{--accent-50:255 251 235;--accent-100:254 243 199;--accent-200:253 230 138;--accent-300:252 211 77;--accent-400:251 191 36;--accent-500:245 158 11;--accent-600:217 119 6;--accent-700:180 83 9;--accent-950:69 26 3}html[data-accent=rose]{--accent-50:255 241 242;--accent-100:255 228 230;--accent-200:254 205 211;--accent-300:253 164 175;--accent-400:251 113 133;--accent-500:244 63 94;--accent-600:225 29 72;--accent-700:190 18 60;--accent-950:76 5 25}:root,html[data-font=sans]{--font-ui:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif}html[data-font=serif]{--font-ui:ui-serif,Georgia,Cambria,\"Times New Roman\",Times,serif}html[data-font=mono]{--font-ui:ui-monospace,SFMono-Regular,\"SF Mono\",Menlo,Consolas,\"Liberation Mono\",monospace}:root{--zoom:1}html[data-size=compact]{--zoom:0.9}html[data-size=large]{--zoom:1.15}html[data-size=compact] body,html[data-size=large] body{zoom:var(--zoom)}@media (prefers-reduced-motion:no-preference){html.theme-anim,html.theme-anim *,html.theme-anim :after,html.theme-anim :before{transition:background-color .26s cubic-bezier(.4,0,.2,1),border-color .26s cubic-bezier(.4,0,.2,1),color .26s cubic-bezier(.4,0,.2,1),fill .26s cubic-bezier(.4,0,.2,1),stroke .26s cubic-bezier(.4,0,.2,1),box-shadow .26s cubic-bezier(.4,0,.2,1)!important;transition-delay:0s!important}}.placeholder\\:text-zinc-400::-moz-placeholder{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.placeholder\\:text-zinc-400::placeholder{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.hover\\:border-zinc-300:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.hover\\:bg-accent-400\\/70:hover{background-color:rgb(var(--accent-400)/.7)}.hover\\:bg-accent-500:hover{--tw-bg-opacity:1;background-color:rgb(var(--accent-500)/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-100:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-200:hover{--tw-bg-opacity:1;background-color:rgb(228 228 231/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-50:hover{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.hover\\:text-zinc-600:hover{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.hover\\:text-zinc-900:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.hover\\:underline:hover{text-decoration-line:underline}.focus\\:border-accent-400:focus{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1))}.focus\\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:opacity-100:focus-visible{opacity:1}.active\\:bg-accent-400\\/70:active{background-color:rgb(var(--accent-400)/.7)}.group:hover .group-hover\\:opacity-100{opacity:1}@media (prefers-reduced-motion:no-preference){.motion-safe\\:transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.motion-safe\\:duration-200{transition-duration:.2s}}.dark\\:divide-zinc-800:is(.dark *)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(39 39 42/var(--tw-divide-opacity,1))}.dark\\:border-rose-500\\/40:is(.dark *){border-color:rgba(244,63,94,.4)}.dark\\:border-zinc-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.dark\\:border-zinc-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.dark\\:bg-accent-500\\/10:is(.dark *){background-color:rgb(var(--accent-500)/.1)}.dark\\:bg-amber-500\\/10:is(.dark *){background-color:rgba(245,158,11,.1)}.dark\\:bg-black\\/50:is(.dark *){background-color:rgba(0,0,0,.5)}.dark\\:bg-rose-500\\/10:is(.dark *){background-color:rgba(244,63,94,.1)}.dark\\:bg-zinc-100:is(.dark *){--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-950:is(.dark *){--tw-bg-opacity:1;background-color:rgb(9 9 11/var(--tw-bg-opacity,1))}.dark\\:text-accent-400:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.dark\\:text-amber-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.dark\\:text-rose-300:is(.dark *){--tw-text-opacity:1;color:rgb(253 164 175/var(--tw-text-opacity,1))}.dark\\:text-zinc-100:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.dark\\:text-zinc-200:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.dark\\:text-zinc-300:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dark\\:text-zinc-400:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.dark\\:text-zinc-50:is(.dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.dark\\:text-zinc-500:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dark\\:text-zinc-600:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:text-zinc-700:is(.dark *){--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.dark\\:text-zinc-900:is(.dark *){--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.dark\\:brightness-125:is(.dark *){--tw-brightness:brightness(1.25);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.dark\\:placeholder\\:text-zinc-600:is(.dark *)::-moz-placeholder{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:placeholder\\:text-zinc-600:is(.dark *)::placeholder{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:hover\\:border-zinc-600:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.dark\\:hover\\:bg-accent-500\\/60:hover:is(.dark *){background-color:rgb(var(--accent-500)/.6)}.dark\\:hover\\:bg-zinc-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(63 63 70/var(--tw-bg-opacity,1))}.dark\\:hover\\:bg-zinc-800:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:hover\\:bg-zinc-900:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.dark\\:hover\\:text-zinc-100:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.dark\\:hover\\:text-zinc-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dark\\:focus\\:border-accent-500:focus:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1))}.dark\\:active\\:bg-accent-500\\/60:active:is(.dark *){background-color:rgb(var(--accent-500)/.6)}@media (min-width:640px){.sm\\:block{display:block}}@media (min-width:768px){.md\\:block{display:block}.md\\:flex{display:flex}}@media (min-width:1024px){.lg\\:block{display:block}}.dark\\:\\[\\&\\>option\\]\\:bg-zinc-900>option:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}";
|
|
45
|
+
var styles_gen_default = "*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\"\"}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-ui);font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.detail-body h1,.detail-body h2,.detail-body h3,.detail-body h4{margin-top:1.25rem;margin-bottom:.375rem;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.detail-body h1:first-child,.detail-body h2:first-child,.detail-body h3:first-child,.detail-body h4:first-child{margin-top:0}.detail-body h1:is(.dark *),.detail-body h2:is(.dark *),.detail-body h3:is(.dark *),.detail-body h4:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body p{margin-top:.375rem;margin-bottom:.375rem;font-size:13px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body p:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body ul{margin-top:.375rem;margin-bottom:.375rem}.detail-body ul>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.detail-body ul{padding-left:1rem;font-size:13px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body ul:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body ul:not(.ordered)>li{list-style-type:disc}.detail-body li.check{margin-left:-1rem;display:flex;list-style-type:none;align-items:flex-start;gap:.5rem}.detail-body li.check .box{margin-top:.125rem;display:flex;height:15px;width:15px;flex-shrink:0;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));font-size:10px;line-height:1;--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.detail-body li.check .box:hover{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.detail-body li.check .box:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.detail-body li.check .box:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.detail-body li.check.done{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));text-decoration-line:line-through}.detail-body li.check.done:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body li.check.done .box{border-color:rgba(16,185,129,.5);--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.detail-body li.check.done .box:is(.dark *){background-color:rgba(16,185,129,.1)}.detail-body ul.ordered>li{list-style-type:none}.detail-body code{border-radius:.25rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:1px .25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body code:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body pre{margin-top:.5rem;margin-bottom:.5rem;overflow-x:auto;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body pre:is(.dark *){background-color:rgba(39,39,42,.8);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body .ref{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.detail-body .ref:hover{text-decoration-line:underline}.detail-body .ref:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.detail-body table{margin-top:.5rem;margin-bottom:.5rem;width:100%;border-collapse:collapse;font-size:12.5px}.detail-body th{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding-top:.25rem;padding-bottom:.25rem;padding-right:.75rem;text-align:left;font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.detail-body th:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.detail-body td{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding-top:.375rem;padding-bottom:.375rem;padding-right:.75rem;vertical-align:top;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.detail-body td:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.detail-body a.mdlink{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.detail-body a.mdlink:hover{text-decoration-line:underline}.detail-body a.mdlink:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.doc-body h1{margin-bottom:.75rem;margin-top:.5rem;font-size:21px;font-weight:600;letter-spacing:-.025em;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body h1:is(.dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.doc-body h2{margin-bottom:.5rem;margin-top:1.75rem;border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding-bottom:.375rem;font-size:16px;font-weight:600;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body h2:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.doc-body h3{margin-bottom:.375rem;margin-top:1.25rem;font-size:13.5px;font-weight:600;--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.doc-body h3:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.doc-body h4{margin-bottom:.25rem;margin-top:1rem;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-body h4:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body p{margin-top:.5rem;margin-bottom:.5rem;font-size:13.5px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body p:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body ul{margin-top:.5rem;margin-bottom:.5rem}.doc-body ul>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.doc-body ul{padding-left:1.25rem;font-size:13.5px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body ul:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body ul:not(.ordered)>li{list-style-type:disc}.doc-body ul.ordered>li{list-style-type:none}.doc-body li.check{margin-left:-1.25rem;display:flex;list-style-type:none;align-items:flex-start;gap:.5rem}.doc-body li.check .box{margin-top:.25rem;display:flex;height:14px;width:14px;flex-shrink:0;align-items:center;justify-content:center;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));font-size:9px;line-height:1;--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.doc-body li.check .box:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.doc-body li.check.done{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));text-decoration-line:line-through}.doc-body li.check.done:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body code{border-radius:.25rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:1px .25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body code:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body pre{margin-top:.75rem;margin-bottom:.75rem;overflow-x:auto;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.625;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body pre:is(.dark *){background-color:rgba(39,39,42,.8);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.codeblock{position:relative}.copy-code{position:absolute;right:.5rem;top:.5rem;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1));padding:.25rem;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));opacity:0;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.copy-code:hover{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.copy-code:focus-visible{opacity:1}.copy-code:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.copy-code:hover:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.codeblock:hover .copy-code,.copy-code:focus-visible{opacity:1}@media (hover:none){.copy-code{opacity:1}}.copy-code.copied{border-color:rgba(16,185,129,.4);--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1));opacity:1}.copy-code.copied:is(.dark *){--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.codeblock>pre{margin-top:0;margin-bottom:0}.detail-body .codeblock,.doc-body .codeblock,.doc-body table{margin-top:.75rem;margin-bottom:.75rem}.doc-body table{width:100%;border-collapse:collapse;font-size:13px}.doc-body th{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));padding-top:.375rem;padding-bottom:.375rem;padding-right:1rem;text-align:left;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-body th:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.doc-body td{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;vertical-align:top;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.doc-body td:is(.dark *){border-color:rgba(39,39,42,.6);--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.doc-body img{margin-left:auto;margin-right:auto;display:inline-block;height:auto;max-width:100%}.doc-body [align=center]{text-align:center}.doc-body [align=right]{text-align:right}.doc-body .ref,.doc-body a.mdlink{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.doc-body .ref:hover,.doc-body a.mdlink:hover{text-decoration-line:underline}.doc-body .ref:is(.dark *),.doc-body a.mdlink:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.doc-body .ref{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12.5px}.doc-body .mermaid{margin-top:1rem;margin-bottom:1rem;display:flex;justify-content:center;overflow-x:auto}.doc-body strong{font-weight:600;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-body strong:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.detail-body hr,.doc-body hr{margin-top:1.5rem;margin-bottom:1.5rem;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.detail-body hr:is(.dark *),.doc-body hr:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.doc-folder>summary{display:flex;cursor:pointer;list-style-type:none;align-items:center;gap:.375rem;border-radius:.375rem;padding:.25rem .5rem;font-size:13.5px;font-weight:600;--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-folder>summary:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.doc-folder>summary:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.doc-folder>summary:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.doc-folder>summary::-webkit-details-marker{display:none}.doc-folder>summary:before{content:\"›\";display:inline-block;width:.5rem;flex-shrink:0;font-size:13px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-folder>summary:is(.dark *):before{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.doc-folder[open]>summary:before{transform:rotate(90deg)}.doc-folder-count{margin-left:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:10px;font-weight:400;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-folder-count:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.doc-folder+.doc-folder{margin-top:.375rem}.doc-folder>summary{padding-left:calc(.5rem + var(--doc-depth, 0)*.75rem)}.doc-link{margin-left:9px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-radius:.375rem;border-left-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding:.25rem .5rem .25rem .625rem;font-size:12px;--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-link:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.doc-link:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-link:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.doc-link{padding-left:calc(.625rem + (var(--doc-depth, 1) - 1)*.75rem)}.doc-link.active{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));font-weight:500;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.doc-link.active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}#doc-outline.empty{display:none}.outline-link{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-left-width:2px;border-color:transparent;padding:.25rem .25rem .25rem .625rem;font-size:12px;line-height:1.375;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.outline-link:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.outline-link:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.outline-link:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.outline-link.lvl-2{padding-left:1.25rem}.outline-link.lvl-3{padding-left:2rem;font-size:11.5px}.outline-link.active{--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));font-weight:500;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.outline-link.active:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}#doc-sidebar.collapsed,#doc-sidebar.collapsed+.sidebar-resizer{display:none!important}#search-results mark{border-radius:.125rem;--tw-bg-opacity:1;background-color:rgb(var(--accent-200)/var(--tw-bg-opacity,1));padding-left:.125rem;padding-right:.125rem;font-weight:500;--tw-text-opacity:1;color:rgb(var(--accent-950)/var(--tw-text-opacity,1))}#search-results mark:is(.dark *){background-color:rgb(var(--accent-600)/.5);--tw-text-opacity:1;color:rgb(var(--accent-100)/var(--tw-text-opacity,1))}::highlight(doc-search){background-color:rgb(var(--accent-300));color:rgb(var(--accent-950))}.dark ::highlight(doc-search){background-color:rgb(var(--accent-600));color:#fff}::highlight(doc-match){background-color:rgb(var(--accent-500));color:#fff}.dark ::highlight(doc-match){background-color:rgb(var(--accent-300));color:rgb(var(--accent-950))}.swatch{height:1.5rem;width:1.5rem;border-radius:9999px;border-width:2px;border-color:transparent;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgb(228 228 231/var(--tw-ring-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.swatch:hover{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.swatch:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(63 63 70/var(--tw-ring-opacity,1))}.swatch.active{--tw-border-opacity:1;border-color:rgb(24 24 27/var(--tw-border-opacity,1))}.swatch.active:is(.dark *){--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.swatch-violet{background-color:#8b5cf6}.swatch-blue{background-color:#3b82f6}.swatch-emerald{background-color:#10b981}.swatch-amber{background-color:#f59e0b}.swatch-rose{background-color:#f43f5e}.pick{flex:1 1 0%;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:.25rem .5rem;font-size:12px;--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.pick:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.pick:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.pick:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.pick.active{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--accent-700)/var(--tw-text-opacity,1))}.pick.active:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));background-color:rgb(var(--accent-500)/.1);--tw-text-opacity:1;color:rgb(var(--accent-300)/var(--tw-text-opacity,1))}.theme-opt.theme-active{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--accent-700)/var(--tw-text-opacity,1))}.theme-opt.theme-active:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1));background-color:rgb(var(--accent-500)/.1);--tw-text-opacity:1;color:rgb(var(--accent-300)/var(--tw-text-opacity,1))}.set-group{border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.set-group:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.set-group>summary{display:flex;cursor:pointer;list-style-type:none;align-items:center;gap:.5rem;padding-top:.625rem;padding-bottom:.625rem;font-size:12.5px;font-weight:500;--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.set-group>summary:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.set-group>summary::-webkit-details-marker{display:none}.set-group>summary:after{content:\"›\";margin-left:auto;font-size:15px;line-height:1;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.set-group>summary:is(.dark *):after{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.set-group[open]>summary:after{transform:rotate(90deg)}.set-sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:400;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.set-sub:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.set-row{display:flex;align-items:flex-start;gap:.75rem;padding-top:.375rem;padding-bottom:.375rem}.set-label{width:72px;flex-shrink:0;padding-top:.375rem;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.set-label:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}#sheet.fullscreen{max-width:none}#sheet.fullscreen #sheet-content{margin-left:auto;margin-right:auto;max-width:42rem}.task-card{position:relative;width:100%;cursor:pointer;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));padding:.625rem .75rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.task-card:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.task-card:focus-visible{outline-style:solid;outline-width:2px;outline-offset:1px;outline-color:rgb(var(--accent-500)/1)}.task-card:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.task-card:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.card-group>summary{display:flex;cursor:pointer;list-style-type:none;align-items:center;gap:.375rem;border-radius:.25rem;padding:.25rem .375rem;font-size:11px;font-weight:500;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.card-group>summary:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.card-group>summary:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-group>summary:hover:is(.dark *){background-color:rgba(39,39,42,.6)}.card-group>summary::-webkit-details-marker{display:none}.card-group>summary:before{content:\"›\";display:inline-block;width:.5rem;flex-shrink:0;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.card-group>summary:is(.dark *):before{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.card-group[open]>summary:before{transform:rotate(90deg)}.card-group-count{margin-left:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:10px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-group-count:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.card-group-body{display:flex;flex-direction:column;gap:.5rem;padding-bottom:.25rem;padding-top:.25rem}.card-group-body:empty{min-height:28px}.card-move{position:absolute;right:.25rem;top:.25rem;z-index:10;display:flex;flex-direction:column;opacity:0;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.task-card:focus-within .card-move,.task-card:hover .card-move{opacity:1}.card-move>button{border-radius:.25rem;padding:.125rem;--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.card-move>button:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.card-move>button:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.card-move>button:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}@media (hover:none){.card-move{opacity:1}}.drop-marker{margin-top:-.125rem;margin-bottom:-.125rem;height:.125rem;border-radius:9999px;--tw-bg-opacity:1;background-color:rgb(var(--accent-500)/var(--tw-bg-opacity,1))}.drop-marker:is(.dark *){--tw-bg-opacity:1;background-color:rgb(var(--accent-400)/var(--tw-bg-opacity,1))}.doc-edit{position:absolute;right:2rem;top:1.5rem;display:inline-flex;align-items:center;gap:.375rem;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));padding:.25rem .5rem;font-size:12px;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));opacity:0;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.doc-edit:hover{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.doc-edit:focus-visible{opacity:1}.doc-edit:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.doc-edit:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}#doc-content{position:relative}#doc-content:hover .doc-edit{opacity:1}@media (hover:none){.doc-edit{opacity:1}}.detail-edit{display:inline-flex;align-items:center;gap:.375rem;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:.25rem .5rem;font-size:11.5px;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.detail-edit:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.detail-edit:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.detail-edit:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.md-editor{margin-top:.5rem}.md-editor-area{display:block;width:100%;resize:none;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12.5px;line-height:1.625;--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1));outline:2px solid transparent;outline-offset:2px}.md-editor-area:is(.dark *){border-color:rgb(var(--accent-500)/.6);--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.md-editor-area{min-height:12rem}.md-editor-status{margin-top:.375rem;font-size:11px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.md-editor-status:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.md-editor-status.is-stale{font-weight:500;--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.md-editor-status.is-stale:is(.dark *){--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.card-id{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:11px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-id:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.card-title{font-size:13px;font-weight:500;line-height:1.375;--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.card-title:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.card-progress{margin-left:auto;display:inline-flex;align-items:center;gap:.375rem;font-size:10px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.card-progress:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.epic-chip{border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:1px .375rem;font-size:10px;font-weight:500;--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.epic-chip:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.epic-chip:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.epic-chip:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.prio{display:inline-flex;align-items:center;gap:.25rem;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.prio:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.prio-label{font-size:10px;font-weight:500;letter-spacing:.025em}.dep-chip{display:inline-flex;align-items:center;gap:.25rem;border-radius:.25rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));padding:1px .375rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:10px;--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.dep-chip:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dep-chip:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dep-chip:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dep-chip.done{border-color:transparent;--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.dep-chip.done:hover{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.dep-chip.done:is(.dark *){background-color:rgba(16,185,129,.1);--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.dep-chip.done:hover:is(.dark *){background-color:rgba(16,185,129,.2)}.backlink-row{display:flex;width:100%;align-items:center;gap:.5rem;border-radius:.375rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding:.375rem .625rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.backlink-row:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.backlink-row:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.backlink-row:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.epic-row{display:flex;width:100%;cursor:pointer;align-items:center;gap:.5rem;border-radius:.375rem;padding:.375rem .5rem;text-align:left;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.epic-row:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.epic-row:hover:is(.dark *){background-color:rgba(39,39,42,.7)}.epic-row.epic-active{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.epic-row.epic-active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}#sidebar>*{flex-shrink:0}#sidebar.collapsed,#sidebar.collapsed+.sidebar-resizer{display:none!important}section.drop-target{background-color:rgb(var(--accent-50)/.6);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-inset:inset;--tw-ring-opacity:1;--tw-ring-color:rgb(var(--accent-300)/var(--tw-ring-opacity,1))}section.drop-target:is(.dark *){background-color:rgb(var(--accent-500)/.05);--tw-ring-color:rgb(var(--accent-500)/0.4)}.task-card.\\!dragging,.task-card.dragging{opacity:.4}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.-top-0\\.5{top:-.125rem}.bottom-3{bottom:.75rem}.bottom-5{bottom:1.25rem}.left-1{left:.25rem}.left-1\\/2{left:50%}.left-3{left:.75rem}.left-44{left:11rem}.right-0{right:0}.right-3{right:.75rem}.right-4{right:1rem}.right-5{right:1.25rem}.top-0{top:0}.top-4{top:1rem}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.z-\\[60\\]{z-index:60}.mx-auto{margin-left:auto;margin-right:auto}.-ml-0\\.5{margin-left:-.125rem}.mb-1{margin-bottom:.25rem}.mb-1\\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-2\\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-8{margin-bottom:2rem}.ml-3{margin-left:.75rem}.ml-auto{margin-left:auto}.mr-1\\.5{margin-right:.375rem}.mr-16{margin-right:4rem}.mt-1{margin-top:.25rem}.mt-1\\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1\\.5{height:.375rem}.h-12{height:3rem}.h-2{height:.5rem}.h-3\\.5{height:.875rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-\\[calc\\(\\(100vh\\/var\\(--zoom\\)\\)-3rem\\)\\]{height:calc(100vh/var(--zoom) - 3rem)}.h-\\[calc\\(100vh\\/var\\(--zoom\\)\\)\\]{height:calc(100vh/var(--zoom))}.h-full{height:100%}.min-h-\\[60px\\]{min-height:60px}.w-1{width:.25rem}.w-1\\.5{width:.375rem}.w-2{width:.5rem}.w-3\\.5{width:.875rem}.w-44{width:11rem}.w-56{width:14rem}.w-60{width:15rem}.w-\\[290px\\]{width:290px}.w-full{width:100%}.min-w-0{min-width:0}.max-w-3xl{max-width:48rem}.max-w-\\[460px\\]{max-width:460px}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-x-1\\/2{--tw-translate-x:-50%}.-translate-x-1\\/2,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x:100%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-col-resize{cursor:col-resize}.cursor-grab{cursor:grab}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.resize{resize:both}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\\[90px_1fr\\]{grid-template-columns:90px 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-y-1\\.5{row-gap:.375rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-zinc-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(228 228 231/var(--tw-divide-opacity,1))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.border{border-width:1px}.border-y{border-top-width:1px}.border-b,.border-y{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-rose-300{--tw-border-opacity:1;border-color:rgb(253 164 175/var(--tw-border-opacity,1))}.border-rose-500\\/70{border-color:rgba(244,63,94,.7)}.border-zinc-100{--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.border-zinc-200{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.bg-accent-50{--tw-bg-opacity:1;background-color:rgb(var(--accent-50)/var(--tw-bg-opacity,1))}.bg-accent-600{--tw-bg-opacity:1;background-color:rgb(var(--accent-600)/var(--tw-bg-opacity,1))}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.bg-black\\/20{background-color:rgba(0,0,0,.2)}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-rose-50{--tw-bg-opacity:1;background-color:rgb(255 241 242/var(--tw-bg-opacity,1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-zinc-100{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.bg-zinc-300{--tw-bg-opacity:1;background-color:rgb(212 212 216/var(--tw-bg-opacity,1))}.bg-zinc-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.bg-zinc-900{--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.p-0\\.5{padding:.125rem}.p-1{padding:.25rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-96{padding:24rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\\.5{padding-top:.875rem;padding-bottom:.875rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-px{padding-top:1px;padding-bottom:1px}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pl-2\\.5{padding-left:.625rem}.pr-1{padding-right:.25rem}.pr-3{padding-right:.75rem}.pt-1{padding-top:.25rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:var(--font-ui)}.font-serif{font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif}.text-\\[10\\.5px\\]{font-size:10.5px}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.text-\\[12\\.5px\\]{font-size:12.5px}.text-\\[12px\\]{font-size:12px}.text-\\[13px\\]{font-size:13px}.text-\\[16px\\]{font-size:16px}.text-\\[9\\.5px\\]{font-size:9.5px}.text-\\[9px\\]{font-size:9px}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-5{line-height:1.25rem}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-accent-600{--tw-text-opacity:1;color:rgb(var(--accent-600)/var(--tw-text-opacity,1))}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-rose-500{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.text-rose-700{--tw-text-opacity:1;color:rgb(190 18 60/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-zinc-100{--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.text-zinc-300{--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.text-zinc-400{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.text-zinc-500{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.text-zinc-600{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.text-zinc-700{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.text-zinc-800{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.text-zinc-900{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.accent-accent-600{accent-color:rgb(var(--accent-600)/1)}.opacity-0{opacity:0}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-sm,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}:root,html[data-accent=violet]{--accent-50:245 243 255;--accent-100:237 233 254;--accent-200:221 214 254;--accent-300:196 181 253;--accent-400:167 139 250;--accent-500:139 92 246;--accent-600:124 58 237;--accent-700:109 40 217;--accent-950:46 16 101}html[data-accent=blue]{--accent-50:239 246 255;--accent-100:219 234 254;--accent-200:191 219 254;--accent-300:147 197 253;--accent-400:96 165 250;--accent-500:59 130 246;--accent-600:37 99 235;--accent-700:29 78 216;--accent-950:23 37 84}html[data-accent=emerald]{--accent-50:236 253 245;--accent-100:209 250 229;--accent-200:167 243 208;--accent-300:110 231 183;--accent-400:52 211 153;--accent-500:16 185 129;--accent-600:5 150 105;--accent-700:4 120 87;--accent-950:2 44 34}html[data-accent=amber]{--accent-50:255 251 235;--accent-100:254 243 199;--accent-200:253 230 138;--accent-300:252 211 77;--accent-400:251 191 36;--accent-500:245 158 11;--accent-600:217 119 6;--accent-700:180 83 9;--accent-950:69 26 3}html[data-accent=rose]{--accent-50:255 241 242;--accent-100:255 228 230;--accent-200:254 205 211;--accent-300:253 164 175;--accent-400:251 113 133;--accent-500:244 63 94;--accent-600:225 29 72;--accent-700:190 18 60;--accent-950:76 5 25}:root,html[data-font=sans]{--font-ui:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif}html[data-font=serif]{--font-ui:ui-serif,Georgia,Cambria,\"Times New Roman\",Times,serif}html[data-font=mono]{--font-ui:ui-monospace,SFMono-Regular,\"SF Mono\",Menlo,Consolas,\"Liberation Mono\",monospace}:root{--zoom:1}html[data-size=compact]{--zoom:0.9}html[data-size=large]{--zoom:1.15}html[data-size=compact] body,html[data-size=large] body{zoom:var(--zoom)}@media (prefers-reduced-motion:no-preference){html.theme-anim,html.theme-anim *,html.theme-anim :after,html.theme-anim :before{transition:background-color .26s cubic-bezier(.4,0,.2,1),border-color .26s cubic-bezier(.4,0,.2,1),color .26s cubic-bezier(.4,0,.2,1),fill .26s cubic-bezier(.4,0,.2,1),stroke .26s cubic-bezier(.4,0,.2,1),box-shadow .26s cubic-bezier(.4,0,.2,1)!important;transition-delay:0s!important}}.placeholder\\:text-zinc-400::-moz-placeholder{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.placeholder\\:text-zinc-400::placeholder{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.hover\\:border-zinc-300:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.hover\\:bg-accent-400\\/70:hover{background-color:rgb(var(--accent-400)/.7)}.hover\\:bg-accent-500:hover{--tw-bg-opacity:1;background-color:rgb(var(--accent-500)/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-100:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-200:hover{--tw-bg-opacity:1;background-color:rgb(228 228 231/var(--tw-bg-opacity,1))}.hover\\:bg-zinc-50:hover{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.hover\\:text-zinc-600:hover{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.hover\\:text-zinc-900:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.hover\\:underline:hover{text-decoration-line:underline}.focus\\:border-accent-400:focus{--tw-border-opacity:1;border-color:rgb(var(--accent-400)/var(--tw-border-opacity,1))}.focus\\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:opacity-100:focus-visible{opacity:1}.active\\:bg-accent-400\\/70:active{background-color:rgb(var(--accent-400)/.7)}.group:hover .group-hover\\:opacity-100{opacity:1}@media (prefers-reduced-motion:no-preference){.motion-safe\\:transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.motion-safe\\:duration-200{transition-duration:.2s}}.dark\\:divide-zinc-800:is(.dark *)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(39 39 42/var(--tw-divide-opacity,1))}.dark\\:border-rose-500\\/40:is(.dark *){border-color:rgba(244,63,94,.4)}.dark\\:border-zinc-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.dark\\:border-zinc-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.dark\\:bg-accent-500\\/10:is(.dark *){background-color:rgb(var(--accent-500)/.1)}.dark\\:bg-amber-500\\/10:is(.dark *){background-color:rgba(245,158,11,.1)}.dark\\:bg-black\\/50:is(.dark *){background-color:rgba(0,0,0,.5)}.dark\\:bg-rose-500\\/10:is(.dark *){background-color:rgba(244,63,94,.1)}.dark\\:bg-zinc-100:is(.dark *){--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.dark\\:bg-zinc-950:is(.dark *){--tw-bg-opacity:1;background-color:rgb(9 9 11/var(--tw-bg-opacity,1))}.dark\\:text-accent-400:is(.dark *){--tw-text-opacity:1;color:rgb(var(--accent-400)/var(--tw-text-opacity,1))}.dark\\:text-amber-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.dark\\:text-rose-300:is(.dark *){--tw-text-opacity:1;color:rgb(253 164 175/var(--tw-text-opacity,1))}.dark\\:text-zinc-100:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.dark\\:text-zinc-200:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.dark\\:text-zinc-300:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dark\\:text-zinc-400:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.dark\\:text-zinc-50:is(.dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.dark\\:text-zinc-500:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dark\\:text-zinc-600:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:text-zinc-700:is(.dark *){--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.dark\\:text-zinc-900:is(.dark *){--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.dark\\:brightness-125:is(.dark *){--tw-brightness:brightness(1.25);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.dark\\:placeholder\\:text-zinc-600:is(.dark *)::-moz-placeholder{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:placeholder\\:text-zinc-600:is(.dark *)::placeholder{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\\:hover\\:border-zinc-600:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.dark\\:hover\\:bg-accent-500\\/60:hover:is(.dark *){background-color:rgb(var(--accent-500)/.6)}.dark\\:hover\\:bg-zinc-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(63 63 70/var(--tw-bg-opacity,1))}.dark\\:hover\\:bg-zinc-800:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:hover\\:bg-zinc-900:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.dark\\:hover\\:text-zinc-100:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.dark\\:hover\\:text-zinc-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dark\\:focus\\:border-accent-500:focus:is(.dark *){--tw-border-opacity:1;border-color:rgb(var(--accent-500)/var(--tw-border-opacity,1))}.dark\\:active\\:bg-accent-500\\/60:active:is(.dark *){background-color:rgb(var(--accent-500)/.6)}@media (min-width:640px){.sm\\:block{display:block}}@media (min-width:768px){.md\\:block{display:block}.md\\:flex{display:flex}}@media (min-width:1024px){.lg\\:block{display:block}}.dark\\:\\[\\&\\>option\\]\\:bg-zinc-900>option:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}";
|
|
46
46
|
|
|
47
47
|
// assets/logo.svg
|
|
48
48
|
var logo_default = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="120 12 800 800">
|
|
@@ -515,11 +515,177 @@ var board_default = `(() => {
|
|
|
515
515
|
const el = document.activeElement;
|
|
516
516
|
return e.key === "/" && !e.metaKey && !e.ctrlKey && !e.altKey && !/INPUT|TEXTAREA|SELECT/.test(el?.tagName ?? "") && !el?.isContentEditable;
|
|
517
517
|
}
|
|
518
|
+
function initCopyCode() {
|
|
519
|
+
document.addEventListener("click", (e) => {
|
|
520
|
+
const btn = e.target.closest(".copy-code");
|
|
521
|
+
const pre = btn?.previousElementSibling;
|
|
522
|
+
if (!btn || !(pre instanceof HTMLElement))
|
|
523
|
+
return;
|
|
524
|
+
copyText(pre.innerText).then((ok) => {
|
|
525
|
+
showToast(ok ? "Copied" : "Copy failed");
|
|
526
|
+
if (!ok)
|
|
527
|
+
return;
|
|
528
|
+
btn.classList.add("copied");
|
|
529
|
+
setTimeout(() => btn.classList.remove("copied"), 1200);
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
async function copyText(text) {
|
|
534
|
+
try {
|
|
535
|
+
if (navigator.clipboard) {
|
|
536
|
+
await navigator.clipboard.writeText(text);
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
} catch {}
|
|
540
|
+
const ta = document.createElement("textarea");
|
|
541
|
+
ta.value = text;
|
|
542
|
+
ta.setAttribute("readonly", "");
|
|
543
|
+
ta.className = "fixed opacity-0";
|
|
544
|
+
document.body.append(ta);
|
|
545
|
+
ta.select();
|
|
546
|
+
const ok = document.execCommand("copy");
|
|
547
|
+
ta.remove();
|
|
548
|
+
return ok;
|
|
549
|
+
}
|
|
550
|
+
var editing = null;
|
|
551
|
+
var isEditing = () => editing !== null;
|
|
552
|
+
var SAVE_DEBOUNCE_MS = 700;
|
|
553
|
+
async function startEditing(target) {
|
|
554
|
+
if (editing)
|
|
555
|
+
return;
|
|
556
|
+
const loaded = await target.load().catch(() => null);
|
|
557
|
+
if (!loaded)
|
|
558
|
+
return showToast("Could not read the source");
|
|
559
|
+
editing = target;
|
|
560
|
+
let rev = loaded.rev;
|
|
561
|
+
let saved = loaded.body;
|
|
562
|
+
let timer;
|
|
563
|
+
const box = document.createElement("div");
|
|
564
|
+
box.className = "md-editor";
|
|
565
|
+
const area = document.createElement("textarea");
|
|
566
|
+
area.className = "md-editor-area";
|
|
567
|
+
area.value = loaded.body;
|
|
568
|
+
area.spellcheck = false;
|
|
569
|
+
const status = document.createElement("div");
|
|
570
|
+
status.className = "md-editor-status";
|
|
571
|
+
status.textContent = "Editing — ⌘S to save, Esc to finish";
|
|
572
|
+
box.append(area, status);
|
|
573
|
+
target.view.hidden = true;
|
|
574
|
+
target.view.insertAdjacentElement("afterend", box);
|
|
575
|
+
const fit = () => {
|
|
576
|
+
area.style.height = "auto";
|
|
577
|
+
area.style.height = area.scrollHeight + "px";
|
|
578
|
+
};
|
|
579
|
+
fit();
|
|
580
|
+
area.focus();
|
|
581
|
+
let inflight = null;
|
|
582
|
+
async function flush() {
|
|
583
|
+
clearTimeout(timer);
|
|
584
|
+
while (inflight)
|
|
585
|
+
await inflight.catch(() => false);
|
|
586
|
+
if (area.value === saved)
|
|
587
|
+
return true;
|
|
588
|
+
const run = save();
|
|
589
|
+
inflight = run;
|
|
590
|
+
try {
|
|
591
|
+
return await run;
|
|
592
|
+
} finally {
|
|
593
|
+
if (inflight === run)
|
|
594
|
+
inflight = null;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
async function save() {
|
|
598
|
+
const sending = area.value;
|
|
599
|
+
status.textContent = "Saving…";
|
|
600
|
+
const res = await target.save(sending, rev);
|
|
601
|
+
if (res.stale) {
|
|
602
|
+
status.textContent = "Changed on disk elsewhere — copy your text, then reopen";
|
|
603
|
+
status.classList.add("is-stale");
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
if (!res.ok) {
|
|
607
|
+
status.textContent = "Save failed";
|
|
608
|
+
status.classList.add("is-stale");
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
saved = sending;
|
|
612
|
+
rev = res.rev ?? rev;
|
|
613
|
+
status.classList.remove("is-stale");
|
|
614
|
+
status.textContent = area.value === saved ? "Saved" : "Editing…";
|
|
615
|
+
return true;
|
|
616
|
+
}
|
|
617
|
+
area.addEventListener("input", () => {
|
|
618
|
+
fit();
|
|
619
|
+
status.textContent = "Editing…";
|
|
620
|
+
clearTimeout(timer);
|
|
621
|
+
timer = setTimeout(() => void flush(), SAVE_DEBOUNCE_MS);
|
|
622
|
+
});
|
|
623
|
+
async function stop() {
|
|
624
|
+
if (!editing)
|
|
625
|
+
return;
|
|
626
|
+
if (!await flush())
|
|
627
|
+
return;
|
|
628
|
+
editing = null;
|
|
629
|
+
pendingStop = null;
|
|
630
|
+
box.remove();
|
|
631
|
+
target.view.hidden = false;
|
|
632
|
+
target.done();
|
|
633
|
+
}
|
|
634
|
+
area.addEventListener("keydown", (e) => {
|
|
635
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "s") {
|
|
636
|
+
e.preventDefault();
|
|
637
|
+
flush();
|
|
638
|
+
}
|
|
639
|
+
if (e.key === "Escape") {
|
|
640
|
+
e.preventDefault();
|
|
641
|
+
stop();
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
area.addEventListener("blur", () => void flush());
|
|
645
|
+
pendingStop = stop;
|
|
646
|
+
}
|
|
647
|
+
var pendingStop = null;
|
|
648
|
+
function stopEditing() {
|
|
649
|
+
pendingStop?.();
|
|
650
|
+
}
|
|
651
|
+
function initBodyEditor() {
|
|
652
|
+
document.addEventListener("click", (e) => {
|
|
653
|
+
if (!e.target.closest?.("[data-edit-body]"))
|
|
654
|
+
return;
|
|
655
|
+
const root = sheetContent.querySelector(".detail-root");
|
|
656
|
+
const view = sheetContent.querySelector(".detail-body");
|
|
657
|
+
if (!root || !view || isEditing())
|
|
658
|
+
return;
|
|
659
|
+
const id = root.dataset.id;
|
|
660
|
+
startEditing({
|
|
661
|
+
view,
|
|
662
|
+
load: () => fetch("/api/items/" + encodeURIComponent(id)).then((r) => r.ok ? r.json() : Promise.reject()).then((t) => ({ body: t.body, rev: t.rev })),
|
|
663
|
+
save: async (body, rev) => {
|
|
664
|
+
const res = await fetch("/api/items/" + encodeURIComponent(id), {
|
|
665
|
+
method: "PATCH",
|
|
666
|
+
headers: { "content-type": "application/json" },
|
|
667
|
+
body: JSON.stringify({ body, rev })
|
|
668
|
+
});
|
|
669
|
+
if (res.status === 409)
|
|
670
|
+
return { ok: false, stale: true };
|
|
671
|
+
if (!res.ok)
|
|
672
|
+
return { ok: false };
|
|
673
|
+
return { ok: true, rev: (await res.json()).rev };
|
|
674
|
+
},
|
|
675
|
+
done: () => {
|
|
676
|
+
clearPaneCache();
|
|
677
|
+
openSheet(id);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
}
|
|
518
682
|
function initChrome() {
|
|
519
683
|
initResizer();
|
|
520
684
|
initTheme();
|
|
521
685
|
initSheet();
|
|
522
686
|
initCheckboxes();
|
|
687
|
+
initCopyCode();
|
|
688
|
+
initBodyEditor();
|
|
523
689
|
}
|
|
524
690
|
|
|
525
691
|
// src/client/board.ts
|
|
@@ -540,6 +706,11 @@ var board_default = `(() => {
|
|
|
540
706
|
for (const r of document.querySelectorAll("[data-filter-epic]")) {
|
|
541
707
|
r.classList.toggle("epic-active", (r.dataset.filterEpic || null) === epic);
|
|
542
708
|
}
|
|
709
|
+
for (const g of document.querySelectorAll(".card-group")) {
|
|
710
|
+
const shown = g.querySelectorAll(".task-card:not(.hidden)").length;
|
|
711
|
+
g.classList.toggle("hidden", shown === 0);
|
|
712
|
+
g.querySelector(".card-group-count").textContent = String(shown);
|
|
713
|
+
}
|
|
543
714
|
for (const s of document.querySelectorAll("section[data-col]")) {
|
|
544
715
|
s.querySelector(".col-count").textContent = String(s.querySelectorAll(".task-card:not(.hidden)").length);
|
|
545
716
|
}
|
|
@@ -584,6 +755,7 @@ var board_default = `(() => {
|
|
|
584
755
|
document.getElementById("app").innerHTML = doc.getElementById("app").innerHTML;
|
|
585
756
|
document.getElementById("header-meta").innerHTML = doc.getElementById("header-meta").innerHTML;
|
|
586
757
|
applySidebarState();
|
|
758
|
+
applyGroupFolds();
|
|
587
759
|
applyFilter(activeEpic);
|
|
588
760
|
clearPaneCache();
|
|
589
761
|
if (sheetIsOpen() && location.hash.length > 1) {
|
|
@@ -604,6 +776,8 @@ var board_default = `(() => {
|
|
|
604
776
|
return;
|
|
605
777
|
if (target.closest("[data-open]"))
|
|
606
778
|
return;
|
|
779
|
+
if (target.closest("[data-move]"))
|
|
780
|
+
return;
|
|
607
781
|
const row = target.closest("[data-filter-epic]");
|
|
608
782
|
if (row) {
|
|
609
783
|
applyFilter(row.dataset.filterEpic === activeEpic ? null : row.dataset.filterEpic || null);
|
|
@@ -633,17 +807,73 @@ var board_default = `(() => {
|
|
|
633
807
|
return;
|
|
634
808
|
}
|
|
635
809
|
const target = e.target;
|
|
810
|
+
if (e.altKey && (e.key === "ArrowUp" || e.key === "ArrowDown")) {
|
|
811
|
+
const card = target.closest(".task-card");
|
|
812
|
+
if (card) {
|
|
813
|
+
e.preventDefault();
|
|
814
|
+
moveCard(card, e.key === "ArrowUp" ? "up" : "down");
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
636
818
|
if ((e.key === "Enter" || e.key === " ") && target instanceof HTMLElement && target.getAttribute("role") === "button") {
|
|
637
819
|
e.preventDefault();
|
|
638
820
|
target.click();
|
|
639
821
|
}
|
|
640
822
|
});
|
|
823
|
+
var groupsCollapsedKey = "boardCollapsedGroups";
|
|
824
|
+
function collapsedGroups() {
|
|
825
|
+
try {
|
|
826
|
+
return new Set(JSON.parse(localStorage.getItem(groupsCollapsedKey) || "[]"));
|
|
827
|
+
} catch {
|
|
828
|
+
return new Set;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
function applyGroupFolds() {
|
|
832
|
+
const shut = collapsedGroups();
|
|
833
|
+
for (const d of document.querySelectorAll(".card-group"))
|
|
834
|
+
d.open = !shut.has(d.dataset.group);
|
|
835
|
+
}
|
|
836
|
+
document.addEventListener("toggle", (e) => {
|
|
837
|
+
const d = e.target.closest(".card-group");
|
|
838
|
+
if (!d)
|
|
839
|
+
return;
|
|
840
|
+
const shut = collapsedGroups();
|
|
841
|
+
if (d.open)
|
|
842
|
+
shut.delete(d.dataset.group);
|
|
843
|
+
else
|
|
844
|
+
shut.add(d.dataset.group);
|
|
845
|
+
localStorage.setItem(groupsCollapsedKey, JSON.stringify([...shut]));
|
|
846
|
+
}, true);
|
|
847
|
+
function visibleSiblings(card) {
|
|
848
|
+
const body = card.closest(".card-group-body");
|
|
849
|
+
return body ? [...body.querySelectorAll(".task-card:not(.hidden)")] : [card];
|
|
850
|
+
}
|
|
851
|
+
function moveCard(card, dir) {
|
|
852
|
+
const sibs = visibleSiblings(card);
|
|
853
|
+
const at = sibs.indexOf(card);
|
|
854
|
+
if (at === -1 || dir === "up" && at === 0 || dir === "down" && at === sibs.length - 1)
|
|
855
|
+
return;
|
|
856
|
+
const target = sibs[dir === "up" ? at - 1 : at + 1];
|
|
857
|
+
target.insertAdjacentElement(dir === "up" ? "beforebegin" : "afterend", card);
|
|
858
|
+
card.focus();
|
|
859
|
+
patchItem(card.dataset.taskId, { move: dir }, card.dataset.rev);
|
|
860
|
+
}
|
|
861
|
+
document.addEventListener("click", (e) => {
|
|
862
|
+
const btn = e.target.closest?.("[data-move]");
|
|
863
|
+
if (!btn)
|
|
864
|
+
return;
|
|
865
|
+
moveCard(btn.closest(".task-card"), btn.dataset.move);
|
|
866
|
+
});
|
|
641
867
|
var dragId = null;
|
|
642
868
|
var dragRev = null;
|
|
643
869
|
var dragStatus = null;
|
|
870
|
+
var dragEpic = null;
|
|
871
|
+
var marker = document.createElement("div");
|
|
872
|
+
marker.className = "drop-marker";
|
|
644
873
|
function clearDropTargets() {
|
|
645
874
|
for (const s of document.querySelectorAll("section.drop-target"))
|
|
646
875
|
s.classList.remove("drop-target");
|
|
876
|
+
marker.remove();
|
|
647
877
|
}
|
|
648
878
|
document.addEventListener("dragstart", (e) => {
|
|
649
879
|
const card = e.target.closest?.("[data-task-id]");
|
|
@@ -652,6 +882,7 @@ var board_default = `(() => {
|
|
|
652
882
|
dragId = card.dataset.taskId;
|
|
653
883
|
dragRev = card.dataset.rev;
|
|
654
884
|
dragStatus = card.dataset.status;
|
|
885
|
+
dragEpic = card.dataset.epic;
|
|
655
886
|
e.dataTransfer.effectAllowed = "move";
|
|
656
887
|
card.classList.add("dragging");
|
|
657
888
|
});
|
|
@@ -660,23 +891,54 @@ var board_default = `(() => {
|
|
|
660
891
|
clearDropTargets();
|
|
661
892
|
dragId = null;
|
|
662
893
|
});
|
|
894
|
+
function dropAnchor(body, y) {
|
|
895
|
+
const cards = [...body.querySelectorAll(".task-card:not(.dragging):not(.hidden)")];
|
|
896
|
+
for (const c of cards) {
|
|
897
|
+
const box = c.getBoundingClientRect();
|
|
898
|
+
if (y < box.top + box.height / 2)
|
|
899
|
+
return { id: c.dataset.taskId, where: "before" };
|
|
900
|
+
}
|
|
901
|
+
const last = cards[cards.length - 1];
|
|
902
|
+
return last ? { id: last.dataset.taskId, where: "after" } : null;
|
|
903
|
+
}
|
|
663
904
|
document.addEventListener("dragover", (e) => {
|
|
905
|
+
if (!dragId)
|
|
906
|
+
return;
|
|
664
907
|
const col = e.target.closest?.("section[data-col]");
|
|
665
|
-
if (col
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
908
|
+
if (!col)
|
|
909
|
+
return;
|
|
910
|
+
e.preventDefault();
|
|
911
|
+
e.dataTransfer.dropEffect = "move";
|
|
912
|
+
if (!col.classList.contains("drop-target")) {
|
|
913
|
+
clearDropTargets();
|
|
914
|
+
col.classList.add("drop-target");
|
|
672
915
|
}
|
|
916
|
+
const body = e.target.closest?.(".card-group-body");
|
|
917
|
+
if (!body)
|
|
918
|
+
return marker.remove();
|
|
919
|
+
const anchor = dropAnchor(body, e.clientY);
|
|
920
|
+
if (!anchor)
|
|
921
|
+
return body.append(marker);
|
|
922
|
+
const el = body.querySelector(\`[data-task-id="\${anchor.id}"]\`);
|
|
923
|
+
el.insertAdjacentElement(anchor.where === "before" ? "beforebegin" : "afterend", marker);
|
|
673
924
|
});
|
|
674
925
|
document.addEventListener("drop", (e) => {
|
|
675
926
|
const col = e.target.closest?.("section[data-col]");
|
|
676
927
|
if (col && dragId) {
|
|
677
928
|
e.preventDefault();
|
|
929
|
+
const body = e.target.closest?.(".card-group-body");
|
|
930
|
+
const patch = {};
|
|
678
931
|
if (col.dataset.col !== dragStatus)
|
|
679
|
-
|
|
932
|
+
patch.status = col.dataset.col;
|
|
933
|
+
if (body && body.dataset.dropEpic !== dragEpic)
|
|
934
|
+
patch.epic = body.dataset.dropEpic;
|
|
935
|
+
if (body) {
|
|
936
|
+
const anchor = dropAnchor(body, e.clientY);
|
|
937
|
+
if (anchor && anchor.id !== dragId)
|
|
938
|
+
patch[anchor.where] = anchor.id;
|
|
939
|
+
}
|
|
940
|
+
if (Object.keys(patch).length)
|
|
941
|
+
patchItem(dragId, patch, dragRev);
|
|
680
942
|
}
|
|
681
943
|
clearDropTargets();
|
|
682
944
|
dragId = null;
|
|
@@ -687,6 +949,7 @@ var board_default = `(() => {
|
|
|
687
949
|
});
|
|
688
950
|
connectLive(() => void refreshApp(), 120);
|
|
689
951
|
applySidebarState();
|
|
952
|
+
applyGroupFolds();
|
|
690
953
|
if (location.hash.length > 1)
|
|
691
954
|
openSheet(location.hash.slice(1));
|
|
692
955
|
})();
|
|
@@ -1100,11 +1363,177 @@ var docs_default = `(() => {
|
|
|
1100
1363
|
const el = document.activeElement;
|
|
1101
1364
|
return e.key === "/" && !e.metaKey && !e.ctrlKey && !e.altKey && !/INPUT|TEXTAREA|SELECT/.test(el?.tagName ?? "") && !el?.isContentEditable;
|
|
1102
1365
|
}
|
|
1366
|
+
function initCopyCode() {
|
|
1367
|
+
document.addEventListener("click", (e) => {
|
|
1368
|
+
const btn = e.target.closest(".copy-code");
|
|
1369
|
+
const pre = btn?.previousElementSibling;
|
|
1370
|
+
if (!btn || !(pre instanceof HTMLElement))
|
|
1371
|
+
return;
|
|
1372
|
+
copyText(pre.innerText).then((ok) => {
|
|
1373
|
+
showToast(ok ? "Copied" : "Copy failed");
|
|
1374
|
+
if (!ok)
|
|
1375
|
+
return;
|
|
1376
|
+
btn.classList.add("copied");
|
|
1377
|
+
setTimeout(() => btn.classList.remove("copied"), 1200);
|
|
1378
|
+
});
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
async function copyText(text) {
|
|
1382
|
+
try {
|
|
1383
|
+
if (navigator.clipboard) {
|
|
1384
|
+
await navigator.clipboard.writeText(text);
|
|
1385
|
+
return true;
|
|
1386
|
+
}
|
|
1387
|
+
} catch {}
|
|
1388
|
+
const ta = document.createElement("textarea");
|
|
1389
|
+
ta.value = text;
|
|
1390
|
+
ta.setAttribute("readonly", "");
|
|
1391
|
+
ta.className = "fixed opacity-0";
|
|
1392
|
+
document.body.append(ta);
|
|
1393
|
+
ta.select();
|
|
1394
|
+
const ok = document.execCommand("copy");
|
|
1395
|
+
ta.remove();
|
|
1396
|
+
return ok;
|
|
1397
|
+
}
|
|
1398
|
+
var editing = null;
|
|
1399
|
+
var isEditing = () => editing !== null;
|
|
1400
|
+
var SAVE_DEBOUNCE_MS = 700;
|
|
1401
|
+
async function startEditing(target) {
|
|
1402
|
+
if (editing)
|
|
1403
|
+
return;
|
|
1404
|
+
const loaded = await target.load().catch(() => null);
|
|
1405
|
+
if (!loaded)
|
|
1406
|
+
return showToast("Could not read the source");
|
|
1407
|
+
editing = target;
|
|
1408
|
+
let rev = loaded.rev;
|
|
1409
|
+
let saved = loaded.body;
|
|
1410
|
+
let timer;
|
|
1411
|
+
const box = document.createElement("div");
|
|
1412
|
+
box.className = "md-editor";
|
|
1413
|
+
const area = document.createElement("textarea");
|
|
1414
|
+
area.className = "md-editor-area";
|
|
1415
|
+
area.value = loaded.body;
|
|
1416
|
+
area.spellcheck = false;
|
|
1417
|
+
const status = document.createElement("div");
|
|
1418
|
+
status.className = "md-editor-status";
|
|
1419
|
+
status.textContent = "Editing — ⌘S to save, Esc to finish";
|
|
1420
|
+
box.append(area, status);
|
|
1421
|
+
target.view.hidden = true;
|
|
1422
|
+
target.view.insertAdjacentElement("afterend", box);
|
|
1423
|
+
const fit = () => {
|
|
1424
|
+
area.style.height = "auto";
|
|
1425
|
+
area.style.height = area.scrollHeight + "px";
|
|
1426
|
+
};
|
|
1427
|
+
fit();
|
|
1428
|
+
area.focus();
|
|
1429
|
+
let inflight = null;
|
|
1430
|
+
async function flush() {
|
|
1431
|
+
clearTimeout(timer);
|
|
1432
|
+
while (inflight)
|
|
1433
|
+
await inflight.catch(() => false);
|
|
1434
|
+
if (area.value === saved)
|
|
1435
|
+
return true;
|
|
1436
|
+
const run = save();
|
|
1437
|
+
inflight = run;
|
|
1438
|
+
try {
|
|
1439
|
+
return await run;
|
|
1440
|
+
} finally {
|
|
1441
|
+
if (inflight === run)
|
|
1442
|
+
inflight = null;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
async function save() {
|
|
1446
|
+
const sending = area.value;
|
|
1447
|
+
status.textContent = "Saving…";
|
|
1448
|
+
const res = await target.save(sending, rev);
|
|
1449
|
+
if (res.stale) {
|
|
1450
|
+
status.textContent = "Changed on disk elsewhere — copy your text, then reopen";
|
|
1451
|
+
status.classList.add("is-stale");
|
|
1452
|
+
return false;
|
|
1453
|
+
}
|
|
1454
|
+
if (!res.ok) {
|
|
1455
|
+
status.textContent = "Save failed";
|
|
1456
|
+
status.classList.add("is-stale");
|
|
1457
|
+
return false;
|
|
1458
|
+
}
|
|
1459
|
+
saved = sending;
|
|
1460
|
+
rev = res.rev ?? rev;
|
|
1461
|
+
status.classList.remove("is-stale");
|
|
1462
|
+
status.textContent = area.value === saved ? "Saved" : "Editing…";
|
|
1463
|
+
return true;
|
|
1464
|
+
}
|
|
1465
|
+
area.addEventListener("input", () => {
|
|
1466
|
+
fit();
|
|
1467
|
+
status.textContent = "Editing…";
|
|
1468
|
+
clearTimeout(timer);
|
|
1469
|
+
timer = setTimeout(() => void flush(), SAVE_DEBOUNCE_MS);
|
|
1470
|
+
});
|
|
1471
|
+
async function stop() {
|
|
1472
|
+
if (!editing)
|
|
1473
|
+
return;
|
|
1474
|
+
if (!await flush())
|
|
1475
|
+
return;
|
|
1476
|
+
editing = null;
|
|
1477
|
+
pendingStop = null;
|
|
1478
|
+
box.remove();
|
|
1479
|
+
target.view.hidden = false;
|
|
1480
|
+
target.done();
|
|
1481
|
+
}
|
|
1482
|
+
area.addEventListener("keydown", (e) => {
|
|
1483
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "s") {
|
|
1484
|
+
e.preventDefault();
|
|
1485
|
+
flush();
|
|
1486
|
+
}
|
|
1487
|
+
if (e.key === "Escape") {
|
|
1488
|
+
e.preventDefault();
|
|
1489
|
+
stop();
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
area.addEventListener("blur", () => void flush());
|
|
1493
|
+
pendingStop = stop;
|
|
1494
|
+
}
|
|
1495
|
+
var pendingStop = null;
|
|
1496
|
+
function stopEditing() {
|
|
1497
|
+
pendingStop?.();
|
|
1498
|
+
}
|
|
1499
|
+
function initBodyEditor() {
|
|
1500
|
+
document.addEventListener("click", (e) => {
|
|
1501
|
+
if (!e.target.closest?.("[data-edit-body]"))
|
|
1502
|
+
return;
|
|
1503
|
+
const root = sheetContent.querySelector(".detail-root");
|
|
1504
|
+
const view = sheetContent.querySelector(".detail-body");
|
|
1505
|
+
if (!root || !view || isEditing())
|
|
1506
|
+
return;
|
|
1507
|
+
const id = root.dataset.id;
|
|
1508
|
+
startEditing({
|
|
1509
|
+
view,
|
|
1510
|
+
load: () => fetch("/api/items/" + encodeURIComponent(id)).then((r) => r.ok ? r.json() : Promise.reject()).then((t) => ({ body: t.body, rev: t.rev })),
|
|
1511
|
+
save: async (body, rev) => {
|
|
1512
|
+
const res = await fetch("/api/items/" + encodeURIComponent(id), {
|
|
1513
|
+
method: "PATCH",
|
|
1514
|
+
headers: { "content-type": "application/json" },
|
|
1515
|
+
body: JSON.stringify({ body, rev })
|
|
1516
|
+
});
|
|
1517
|
+
if (res.status === 409)
|
|
1518
|
+
return { ok: false, stale: true };
|
|
1519
|
+
if (!res.ok)
|
|
1520
|
+
return { ok: false };
|
|
1521
|
+
return { ok: true, rev: (await res.json()).rev };
|
|
1522
|
+
},
|
|
1523
|
+
done: () => {
|
|
1524
|
+
clearPaneCache();
|
|
1525
|
+
openSheet(id);
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1103
1530
|
function initChrome() {
|
|
1104
1531
|
initResizer();
|
|
1105
1532
|
initTheme();
|
|
1106
1533
|
initSheet();
|
|
1107
1534
|
initCheckboxes();
|
|
1535
|
+
initCopyCode();
|
|
1536
|
+
initBodyEditor();
|
|
1108
1537
|
}
|
|
1109
1538
|
|
|
1110
1539
|
// src/client/docs.ts
|
|
@@ -1311,8 +1740,42 @@ var docs_default = `(() => {
|
|
|
1311
1740
|
refreshing = false;
|
|
1312
1741
|
}
|
|
1313
1742
|
}
|
|
1314
|
-
var refreshDoc = () => loadDoc(location.pathname + location.search, { conditional: true });
|
|
1743
|
+
var refreshDoc = () => isEditing() ? Promise.resolve() : loadDoc(location.pathname + location.search, { conditional: true });
|
|
1315
1744
|
hooks.refreshView = refreshDoc;
|
|
1745
|
+
function currentDocFile() {
|
|
1746
|
+
return new URLSearchParams(location.search).get("file") || "README.md";
|
|
1747
|
+
}
|
|
1748
|
+
function editDoc() {
|
|
1749
|
+
const view = document.querySelector("#doc-content .doc-body");
|
|
1750
|
+
if (!view)
|
|
1751
|
+
return;
|
|
1752
|
+
const file = currentDocFile();
|
|
1753
|
+
const url = "/api/docs?file=" + encodeURIComponent(file);
|
|
1754
|
+
startEditing({
|
|
1755
|
+
view,
|
|
1756
|
+
load: () => fetch(url).then((r) => r.ok ? r.json() : Promise.reject()),
|
|
1757
|
+
save: async (body, rev) => {
|
|
1758
|
+
const res = await fetch(url, {
|
|
1759
|
+
method: "PATCH",
|
|
1760
|
+
headers: { "content-type": "application/json" },
|
|
1761
|
+
body: JSON.stringify({ body, rev })
|
|
1762
|
+
});
|
|
1763
|
+
if (res.status === 409)
|
|
1764
|
+
return { ok: false, stale: true };
|
|
1765
|
+
if (!res.ok)
|
|
1766
|
+
return { ok: false };
|
|
1767
|
+
return { ok: true, rev: (await res.json()).rev };
|
|
1768
|
+
},
|
|
1769
|
+
done: () => {
|
|
1770
|
+
etag = "";
|
|
1771
|
+
loadDoc(location.pathname + location.search);
|
|
1772
|
+
}
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
document.addEventListener("click", (e) => {
|
|
1776
|
+
if (e.target.closest("#doc-edit"))
|
|
1777
|
+
editDoc();
|
|
1778
|
+
});
|
|
1316
1779
|
function markActiveDoc() {
|
|
1317
1780
|
const current = new URLSearchParams(location.search).get("file") || "README.md";
|
|
1318
1781
|
for (const a of document.querySelectorAll("#doc-sidebar .doc-link")) {
|
|
@@ -1333,6 +1796,7 @@ var docs_default = `(() => {
|
|
|
1333
1796
|
if (here === location.pathname + location.search)
|
|
1334
1797
|
return;
|
|
1335
1798
|
e.preventDefault();
|
|
1799
|
+
stopEditing();
|
|
1336
1800
|
loadDoc(here, { push: true });
|
|
1337
1801
|
});
|
|
1338
1802
|
window.addEventListener("popstate", () => void loadDoc(location.pathname + location.search));
|
|
@@ -1516,6 +1980,13 @@ var docs_default = `(() => {
|
|
|
1516
1980
|
if (slashShouldFocus(e)) {
|
|
1517
1981
|
e.preventDefault();
|
|
1518
1982
|
searchEl.focus();
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
const el = e.target;
|
|
1986
|
+
const typing = el.matches("input, textarea, select") || el.isContentEditable;
|
|
1987
|
+
if (e.key === "e" && !typing && !e.metaKey && !e.ctrlKey && !e.altKey && !isEditing()) {
|
|
1988
|
+
e.preventDefault();
|
|
1989
|
+
editDoc();
|
|
1519
1990
|
}
|
|
1520
1991
|
});
|
|
1521
1992
|
if (localStorage.getItem("docsSidebarCollapsed") === "1")
|
|
@@ -1935,11 +2406,177 @@ var graph_default = `(() => {
|
|
|
1935
2406
|
const el = document.activeElement;
|
|
1936
2407
|
return e.key === "/" && !e.metaKey && !e.ctrlKey && !e.altKey && !/INPUT|TEXTAREA|SELECT/.test(el?.tagName ?? "") && !el?.isContentEditable;
|
|
1937
2408
|
}
|
|
2409
|
+
function initCopyCode() {
|
|
2410
|
+
document.addEventListener("click", (e) => {
|
|
2411
|
+
const btn = e.target.closest(".copy-code");
|
|
2412
|
+
const pre = btn?.previousElementSibling;
|
|
2413
|
+
if (!btn || !(pre instanceof HTMLElement))
|
|
2414
|
+
return;
|
|
2415
|
+
copyText(pre.innerText).then((ok) => {
|
|
2416
|
+
showToast(ok ? "Copied" : "Copy failed");
|
|
2417
|
+
if (!ok)
|
|
2418
|
+
return;
|
|
2419
|
+
btn.classList.add("copied");
|
|
2420
|
+
setTimeout(() => btn.classList.remove("copied"), 1200);
|
|
2421
|
+
});
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
async function copyText(text) {
|
|
2425
|
+
try {
|
|
2426
|
+
if (navigator.clipboard) {
|
|
2427
|
+
await navigator.clipboard.writeText(text);
|
|
2428
|
+
return true;
|
|
2429
|
+
}
|
|
2430
|
+
} catch {}
|
|
2431
|
+
const ta = document.createElement("textarea");
|
|
2432
|
+
ta.value = text;
|
|
2433
|
+
ta.setAttribute("readonly", "");
|
|
2434
|
+
ta.className = "fixed opacity-0";
|
|
2435
|
+
document.body.append(ta);
|
|
2436
|
+
ta.select();
|
|
2437
|
+
const ok = document.execCommand("copy");
|
|
2438
|
+
ta.remove();
|
|
2439
|
+
return ok;
|
|
2440
|
+
}
|
|
2441
|
+
var editing = null;
|
|
2442
|
+
var isEditing = () => editing !== null;
|
|
2443
|
+
var SAVE_DEBOUNCE_MS = 700;
|
|
2444
|
+
async function startEditing(target) {
|
|
2445
|
+
if (editing)
|
|
2446
|
+
return;
|
|
2447
|
+
const loaded = await target.load().catch(() => null);
|
|
2448
|
+
if (!loaded)
|
|
2449
|
+
return showToast("Could not read the source");
|
|
2450
|
+
editing = target;
|
|
2451
|
+
let rev = loaded.rev;
|
|
2452
|
+
let saved = loaded.body;
|
|
2453
|
+
let timer;
|
|
2454
|
+
const box = document.createElement("div");
|
|
2455
|
+
box.className = "md-editor";
|
|
2456
|
+
const area = document.createElement("textarea");
|
|
2457
|
+
area.className = "md-editor-area";
|
|
2458
|
+
area.value = loaded.body;
|
|
2459
|
+
area.spellcheck = false;
|
|
2460
|
+
const status = document.createElement("div");
|
|
2461
|
+
status.className = "md-editor-status";
|
|
2462
|
+
status.textContent = "Editing — ⌘S to save, Esc to finish";
|
|
2463
|
+
box.append(area, status);
|
|
2464
|
+
target.view.hidden = true;
|
|
2465
|
+
target.view.insertAdjacentElement("afterend", box);
|
|
2466
|
+
const fit = () => {
|
|
2467
|
+
area.style.height = "auto";
|
|
2468
|
+
area.style.height = area.scrollHeight + "px";
|
|
2469
|
+
};
|
|
2470
|
+
fit();
|
|
2471
|
+
area.focus();
|
|
2472
|
+
let inflight = null;
|
|
2473
|
+
async function flush() {
|
|
2474
|
+
clearTimeout(timer);
|
|
2475
|
+
while (inflight)
|
|
2476
|
+
await inflight.catch(() => false);
|
|
2477
|
+
if (area.value === saved)
|
|
2478
|
+
return true;
|
|
2479
|
+
const run = save();
|
|
2480
|
+
inflight = run;
|
|
2481
|
+
try {
|
|
2482
|
+
return await run;
|
|
2483
|
+
} finally {
|
|
2484
|
+
if (inflight === run)
|
|
2485
|
+
inflight = null;
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
async function save() {
|
|
2489
|
+
const sending = area.value;
|
|
2490
|
+
status.textContent = "Saving…";
|
|
2491
|
+
const res = await target.save(sending, rev);
|
|
2492
|
+
if (res.stale) {
|
|
2493
|
+
status.textContent = "Changed on disk elsewhere — copy your text, then reopen";
|
|
2494
|
+
status.classList.add("is-stale");
|
|
2495
|
+
return false;
|
|
2496
|
+
}
|
|
2497
|
+
if (!res.ok) {
|
|
2498
|
+
status.textContent = "Save failed";
|
|
2499
|
+
status.classList.add("is-stale");
|
|
2500
|
+
return false;
|
|
2501
|
+
}
|
|
2502
|
+
saved = sending;
|
|
2503
|
+
rev = res.rev ?? rev;
|
|
2504
|
+
status.classList.remove("is-stale");
|
|
2505
|
+
status.textContent = area.value === saved ? "Saved" : "Editing…";
|
|
2506
|
+
return true;
|
|
2507
|
+
}
|
|
2508
|
+
area.addEventListener("input", () => {
|
|
2509
|
+
fit();
|
|
2510
|
+
status.textContent = "Editing…";
|
|
2511
|
+
clearTimeout(timer);
|
|
2512
|
+
timer = setTimeout(() => void flush(), SAVE_DEBOUNCE_MS);
|
|
2513
|
+
});
|
|
2514
|
+
async function stop() {
|
|
2515
|
+
if (!editing)
|
|
2516
|
+
return;
|
|
2517
|
+
if (!await flush())
|
|
2518
|
+
return;
|
|
2519
|
+
editing = null;
|
|
2520
|
+
pendingStop = null;
|
|
2521
|
+
box.remove();
|
|
2522
|
+
target.view.hidden = false;
|
|
2523
|
+
target.done();
|
|
2524
|
+
}
|
|
2525
|
+
area.addEventListener("keydown", (e) => {
|
|
2526
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "s") {
|
|
2527
|
+
e.preventDefault();
|
|
2528
|
+
flush();
|
|
2529
|
+
}
|
|
2530
|
+
if (e.key === "Escape") {
|
|
2531
|
+
e.preventDefault();
|
|
2532
|
+
stop();
|
|
2533
|
+
}
|
|
2534
|
+
});
|
|
2535
|
+
area.addEventListener("blur", () => void flush());
|
|
2536
|
+
pendingStop = stop;
|
|
2537
|
+
}
|
|
2538
|
+
var pendingStop = null;
|
|
2539
|
+
function stopEditing() {
|
|
2540
|
+
pendingStop?.();
|
|
2541
|
+
}
|
|
2542
|
+
function initBodyEditor() {
|
|
2543
|
+
document.addEventListener("click", (e) => {
|
|
2544
|
+
if (!e.target.closest?.("[data-edit-body]"))
|
|
2545
|
+
return;
|
|
2546
|
+
const root = sheetContent.querySelector(".detail-root");
|
|
2547
|
+
const view = sheetContent.querySelector(".detail-body");
|
|
2548
|
+
if (!root || !view || isEditing())
|
|
2549
|
+
return;
|
|
2550
|
+
const id = root.dataset.id;
|
|
2551
|
+
startEditing({
|
|
2552
|
+
view,
|
|
2553
|
+
load: () => fetch("/api/items/" + encodeURIComponent(id)).then((r) => r.ok ? r.json() : Promise.reject()).then((t) => ({ body: t.body, rev: t.rev })),
|
|
2554
|
+
save: async (body, rev) => {
|
|
2555
|
+
const res = await fetch("/api/items/" + encodeURIComponent(id), {
|
|
2556
|
+
method: "PATCH",
|
|
2557
|
+
headers: { "content-type": "application/json" },
|
|
2558
|
+
body: JSON.stringify({ body, rev })
|
|
2559
|
+
});
|
|
2560
|
+
if (res.status === 409)
|
|
2561
|
+
return { ok: false, stale: true };
|
|
2562
|
+
if (!res.ok)
|
|
2563
|
+
return { ok: false };
|
|
2564
|
+
return { ok: true, rev: (await res.json()).rev };
|
|
2565
|
+
},
|
|
2566
|
+
done: () => {
|
|
2567
|
+
clearPaneCache();
|
|
2568
|
+
openSheet(id);
|
|
2569
|
+
}
|
|
2570
|
+
});
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
1938
2573
|
function initChrome() {
|
|
1939
2574
|
initResizer();
|
|
1940
2575
|
initTheme();
|
|
1941
2576
|
initSheet();
|
|
1942
2577
|
initCheckboxes();
|
|
2578
|
+
initCopyCode();
|
|
2579
|
+
initBodyEditor();
|
|
1943
2580
|
}
|
|
1944
2581
|
|
|
1945
2582
|
// src/client/graph.ts
|
|
@@ -2582,6 +3219,7 @@ function parseTask(file) {
|
|
|
2582
3219
|
epic: fm.epic ?? "",
|
|
2583
3220
|
status: fm.status ?? "backlog",
|
|
2584
3221
|
priority: fm.priority ?? "P3",
|
|
3222
|
+
order: fm.order !== undefined && fm.order.trim() !== "" && Number.isFinite(Number(fm.order)) ? Number(fm.order) : null,
|
|
2585
3223
|
depends_on: parseInlineList(fm.depends_on ?? ""),
|
|
2586
3224
|
created: fm.created ?? "",
|
|
2587
3225
|
body,
|
|
@@ -2657,6 +3295,8 @@ function setFrontmatterValue(raw, key, value) {
|
|
|
2657
3295
|
function getFrontmatterValue(raw, key) {
|
|
2658
3296
|
return parseFrontmatter(raw)?.fm[key] ?? "";
|
|
2659
3297
|
}
|
|
3298
|
+
var FENCE = /^([ \t]*)```/;
|
|
3299
|
+
var COPY_BUTTON = `<button type="button" class="copy-code" aria-label="Copy code" title="Copy">` + `<svg width="13" height="13" viewBox="0 0 16 16" fill="none" aria-hidden="true">` + `<rect x="5.5" y="5.5" width="8" height="8" rx="1.5" stroke="currentColor" stroke-width="1.3"/>` + `<path d="M10.5 3.5 A1.5 1.5 0 0 0 9 2.5 H4 A1.5 1.5 0 0 0 2.5 4 v5 a1.5 1.5 0 0 0 1 1.4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>` + `</svg></button>`;
|
|
2660
3300
|
function toggleCheckboxLine(raw, index, checked) {
|
|
2661
3301
|
const m = raw.match(/^(---\n[\s\S]*?\n---\n?)([\s\S]*)$/);
|
|
2662
3302
|
const head = m ? m[1] : "";
|
|
@@ -2665,7 +3305,7 @@ function toggleCheckboxLine(raw, index, checked) {
|
|
|
2665
3305
|
let inCode = false;
|
|
2666
3306
|
let count = 0;
|
|
2667
3307
|
for (let i = 0;i < lines.length; i++) {
|
|
2668
|
-
if (lines[i]
|
|
3308
|
+
if (FENCE.test(lines[i])) {
|
|
2669
3309
|
inCode = !inCode;
|
|
2670
3310
|
continue;
|
|
2671
3311
|
}
|
|
@@ -2760,6 +3400,7 @@ function mdToHtml(md, opts = {}) {
|
|
|
2760
3400
|
const out = [];
|
|
2761
3401
|
let inCode = false;
|
|
2762
3402
|
let codeLang = "";
|
|
3403
|
+
let codeIndent = 0;
|
|
2763
3404
|
let inList = false;
|
|
2764
3405
|
let cbIndex = 0;
|
|
2765
3406
|
let tableBuf = [];
|
|
@@ -2785,21 +3426,23 @@ function mdToHtml(md, opts = {}) {
|
|
|
2785
3426
|
}
|
|
2786
3427
|
};
|
|
2787
3428
|
for (const line of lines) {
|
|
2788
|
-
|
|
3429
|
+
const fence = line.match(FENCE);
|
|
3430
|
+
if (fence) {
|
|
2789
3431
|
flushTable();
|
|
2790
3432
|
closeList();
|
|
2791
3433
|
if (!inCode) {
|
|
2792
|
-
|
|
3434
|
+
codeIndent = fence[1].length;
|
|
3435
|
+
codeLang = line.slice(fence[0].length).trim();
|
|
2793
3436
|
inCode = true;
|
|
2794
|
-
out.push(opts.docMode && codeLang === "mermaid" ? '<div class="mermaid">' : "
|
|
3437
|
+
out.push(opts.docMode && codeLang === "mermaid" ? '<div class="mermaid">' : `<div class="codeblock"><pre>`);
|
|
2795
3438
|
} else {
|
|
2796
|
-
out.push(opts.docMode && codeLang === "mermaid" ? "</div>" :
|
|
3439
|
+
out.push(opts.docMode && codeLang === "mermaid" ? "</div>" : `</pre>${COPY_BUTTON}</div>`);
|
|
2797
3440
|
inCode = false;
|
|
2798
3441
|
}
|
|
2799
3442
|
continue;
|
|
2800
3443
|
}
|
|
2801
3444
|
if (inCode) {
|
|
2802
|
-
out.push(line);
|
|
3445
|
+
out.push(line.slice(0, codeIndent).trim() ? line : line.slice(codeIndent));
|
|
2803
3446
|
continue;
|
|
2804
3447
|
}
|
|
2805
3448
|
if (/^\s*\|/.test(line)) {
|
|
@@ -2853,7 +3496,7 @@ function mdToHtml(md, opts = {}) {
|
|
|
2853
3496
|
flushTable();
|
|
2854
3497
|
closeList();
|
|
2855
3498
|
if (inCode)
|
|
2856
|
-
out.push(
|
|
3499
|
+
out.push(`</pre>${COPY_BUTTON}</div>`);
|
|
2857
3500
|
return out.join(`
|
|
2858
3501
|
`);
|
|
2859
3502
|
}
|
|
@@ -2915,32 +3558,30 @@ function normalizeRel(baseDir, href) {
|
|
|
2915
3558
|
}
|
|
2916
3559
|
var buildBacklinks = memoByVersion(() => {
|
|
2917
3560
|
const map = new Map;
|
|
2918
|
-
for (const
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
map.set(t, arr);
|
|
2943
|
-
}
|
|
3561
|
+
for (const rel of allDocFiles()) {
|
|
3562
|
+
const parsed = parseCached("fm", join(PLANNER_DIR, ...rel.split("/")), (raw) => parseFrontmatter(raw) ?? { fm: {}, body: raw });
|
|
3563
|
+
if (!parsed)
|
|
3564
|
+
continue;
|
|
3565
|
+
const { fm, body } = parsed;
|
|
3566
|
+
const deps = new Set(parseInlineList(fm.depends_on ?? ""));
|
|
3567
|
+
const targets = new Set;
|
|
3568
|
+
for (const m of body.matchAll(/\b(ST-\d{3}|E\d{2})\b/g))
|
|
3569
|
+
targets.add(m[1]);
|
|
3570
|
+
if (fm.epic)
|
|
3571
|
+
targets.add(fm.epic);
|
|
3572
|
+
for (const d of deps)
|
|
3573
|
+
targets.add(d);
|
|
3574
|
+
const baseDir = dirname(rel) === "." ? "" : dirname(rel);
|
|
3575
|
+
for (const m of body.matchAll(/\]\(([^)\s]+\.md)\)/g)) {
|
|
3576
|
+
if (!/^https?:/.test(m[1]))
|
|
3577
|
+
targets.add(normalizeRel(baseDir, m[1]));
|
|
3578
|
+
}
|
|
3579
|
+
targets.delete(fm.id ?? "");
|
|
3580
|
+
targets.delete(rel);
|
|
3581
|
+
for (const t of targets) {
|
|
3582
|
+
const arr = map.get(t) ?? [];
|
|
3583
|
+
arr.push({ file: rel, id: fm.id, title: fm.title ?? basename(rel), isDep: deps.has(t) });
|
|
3584
|
+
map.set(t, arr);
|
|
2944
3585
|
}
|
|
2945
3586
|
}
|
|
2946
3587
|
return map;
|
|
@@ -2948,14 +3589,12 @@ var buildBacklinks = memoByVersion(() => {
|
|
|
2948
3589
|
var buildGraph = memoByVersion(() => {
|
|
2949
3590
|
const nodes = new Map;
|
|
2950
3591
|
const byItemId = new Map;
|
|
2951
|
-
for (const
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
byItemId.set(fm.id, rel);
|
|
2958
|
-
}
|
|
3592
|
+
for (const rel of allDocFiles()) {
|
|
3593
|
+
const parsed = parseCached("fm", join(PLANNER_DIR, ...rel.split("/")), (raw) => parseFrontmatter(raw) ?? { fm: {}, body: raw });
|
|
3594
|
+
const fm = parsed?.fm ?? {};
|
|
3595
|
+
nodes.set(rel, { id: rel, label: fm.title || basename(rel).replace(/\.md$/i, ""), group: docGroup(rel), deg: 0 });
|
|
3596
|
+
if (fm.id)
|
|
3597
|
+
byItemId.set(fm.id, rel);
|
|
2959
3598
|
}
|
|
2960
3599
|
const links = [];
|
|
2961
3600
|
const seen = new Set;
|
|
@@ -2991,73 +3630,93 @@ function backlinksHtml(sources, heading, docMode) {
|
|
|
2991
3630
|
return `<a href="${href}" class="${rowClass}">${inner}</a>`;
|
|
2992
3631
|
}).join("");
|
|
2993
3632
|
return `<div class="mt-6 border-t border-zinc-100 pt-4 dark:border-zinc-800">
|
|
2994
|
-
|
|
2995
|
-
|
|
3633
|
+
<div class="mb-2 text-[11px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">${heading} <span class="font-mono text-[10px] normal-case text-zinc-300 dark:text-zinc-600">${sources.length}</span></div>
|
|
3634
|
+
<div class="space-y-1.5">${rows}</div>
|
|
2996
3635
|
</div>`;
|
|
2997
3636
|
}
|
|
2998
3637
|
function cardHtml(t, byId) {
|
|
2999
3638
|
const deps = t.depends_on.map((d) => depChip(d, byId.get(d)?.status === "done")).join("");
|
|
3000
3639
|
const progress = t.subtasksTotal > 0 ? `<span class="card-progress" title="${t.subtasksDone} of ${t.subtasksTotal} subtasks done">
|
|
3001
|
-
|
|
3002
|
-
|
|
3640
|
+
${progressRing(t.subtasksDone, t.subtasksTotal)}
|
|
3641
|
+
${t.subtasksDone}/${t.subtasksTotal}</span>` : "";
|
|
3003
3642
|
const search = esc(`${t.id} ${t.title} ${t.epic} ${t.body}`.toLowerCase().replace(/\s+/g, " ").slice(0, 2000));
|
|
3004
3643
|
return `<div role="button" tabindex="0" draggable="true" data-task-id="${esc(t.id)}" data-epic="${esc(t.epic)}" data-status="${esc(t.status)}" data-rev="${t.rev}" data-search="${search}"
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
</
|
|
3010
|
-
<
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3644
|
+
class="task-card">
|
|
3645
|
+
<div class="card-move">
|
|
3646
|
+
<button type="button" data-move="up" tabindex="-1" aria-label="Move ${esc(t.id)} up" title="Move up (Alt+↑)">
|
|
3647
|
+
<svg width="11" height="11" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 10 8 6 12 10" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
3648
|
+
</button>
|
|
3649
|
+
<button type="button" data-move="down" tabindex="-1" aria-label="Move ${esc(t.id)} down" title="Move down (Alt+↓)">
|
|
3650
|
+
<svg width="11" height="11" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6 8 10 12 6" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
3651
|
+
</button>
|
|
3652
|
+
</div>
|
|
3653
|
+
<div class="mb-1 flex items-center justify-between gap-2">
|
|
3654
|
+
<span class="card-id">${esc(t.id)}</span>
|
|
3655
|
+
${priorityIcon(t.priority)}
|
|
3656
|
+
</div>
|
|
3657
|
+
<div class="card-title">${esc(t.title)}</div>
|
|
3658
|
+
<div class="mt-2 flex flex-wrap items-center gap-1.5">
|
|
3659
|
+
<button type="button" data-open="${esc(t.epic)}" class="epic-chip" title="open epic ${esc(t.epic)}">${esc(t.epic)}</button>
|
|
3660
|
+
${deps}${progress}
|
|
3661
|
+
</div>
|
|
3015
3662
|
</div>`;
|
|
3016
3663
|
}
|
|
3017
3664
|
function statusSelect(current) {
|
|
3018
3665
|
const options = [...new Set([...settings.columns, current])].map((s) => `<option value="${esc(s)}" ${s === current ? "selected" : ""}>${esc(s.replace("-", " "))}</option>`).join("");
|
|
3019
3666
|
return `<select data-status-select aria-label="Status"
|
|
3020
|
-
|
|
3667
|
+
class="cursor-pointer rounded border border-zinc-200 bg-transparent px-1 py-0.5 text-[12px] capitalize text-zinc-600 hover:border-zinc-300 focus:outline-none dark:border-zinc-700 dark:text-zinc-300 dark:hover:border-zinc-600 dark:[&>option]:bg-zinc-900">${options}</select>`;
|
|
3021
3668
|
}
|
|
3022
3669
|
function taskDetailHtml(t, byId, backlinks) {
|
|
3023
3670
|
const deps = t.depends_on.length ? t.depends_on.map((d) => depChip(d, byId.get(d)?.status === "done")).join(" ") : '<span class="text-zinc-400 dark:text-zinc-500">none</span>';
|
|
3024
3671
|
return `<div class="detail-root" data-id="${esc(t.id)}" data-rev="${t.rev}">
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3672
|
+
<div class="flex items-center gap-2 text-[12px] text-zinc-500 dark:text-zinc-400">
|
|
3673
|
+
${statusIcon(t.status)}${statusSelect(t.status)}
|
|
3674
|
+
<span class="font-mono text-[11px]">${esc(t.id)}</span>
|
|
3675
|
+
<span class="ml-auto mr-16">${priorityIcon(t.priority)}</span>
|
|
3676
|
+
</div>
|
|
3677
|
+
<h2 class="mt-2 text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">${esc(t.title)}</h2>
|
|
3678
|
+
<dl class="mt-4 grid grid-cols-[90px_1fr] items-baseline gap-y-1.5 border-y border-zinc-100 py-3 text-[12px] dark:border-zinc-800">
|
|
3679
|
+
<dt class="text-zinc-400 dark:text-zinc-500">Epic</dt><dd><button type="button" data-open="${esc(t.epic)}" class="font-mono text-accent-600 hover:underline dark:text-accent-400">${esc(t.epic)}</button></dd>
|
|
3680
|
+
<dt class="text-zinc-400 dark:text-zinc-500">Depends on</dt><dd class="flex flex-wrap gap-1">${deps}</dd>
|
|
3681
|
+
<dt class="text-zinc-400 dark:text-zinc-500">Created</dt><dd class="text-zinc-700 dark:text-zinc-300">${esc(t.created)}</dd>
|
|
3682
|
+
<dt class="text-zinc-400 dark:text-zinc-500">File</dt><dd class="font-mono text-[11px] text-zinc-500 dark:text-zinc-400"><a class="hover:underline" href="/docs?file=tasks/${esc(t.file)}">tasks/${esc(t.file)}</a></dd>
|
|
3683
|
+
</dl>
|
|
3684
|
+
<div class="mt-4 flex items-center gap-2">
|
|
3685
|
+
<button type="button" class="detail-edit" data-edit-body title="Edit body (e)">
|
|
3686
|
+
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M11.5 2.5 13.5 4.5 5.5 12.5 2.5 13.5 3.5 10.5 Z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>
|
|
3687
|
+
<span>Edit</span>
|
|
3688
|
+
</button>
|
|
3689
|
+
</div>
|
|
3690
|
+
<div class="detail-body mt-2">${mdToHtml(t.body)}</div>
|
|
3691
|
+
${backlinksHtml(backlinks.get(t.id) ?? [], "Referenced by", false)}
|
|
3039
3692
|
</div>`;
|
|
3040
3693
|
}
|
|
3041
3694
|
function epicDetailHtml(e, tasks, backlinks) {
|
|
3042
3695
|
const stories = tasks.filter((t) => t.epic === e.id);
|
|
3043
3696
|
const storyList = stories.length ? stories.map((t) => `<button type="button" data-open="${esc(t.id)}" class="backlink-row">
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3697
|
+
${statusIcon(t.status, 12)}
|
|
3698
|
+
<span class="font-mono text-[11px] text-zinc-400 dark:text-zinc-500">${esc(t.id)}</span>
|
|
3699
|
+
<span class="truncate text-[12.5px] text-zinc-700 dark:text-zinc-300">${esc(t.title)}</span>
|
|
3700
|
+
</button>`).join("") : '<p class="text-[12px] text-zinc-400">No stories yet.</p>';
|
|
3048
3701
|
const done = stories.filter((t) => t.status === "done").length;
|
|
3049
3702
|
return `<div class="detail-root" data-id="${esc(e.id)}" data-rev="${e.rev}">
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3703
|
+
<div class="flex items-center gap-2 text-[12px] text-zinc-500 dark:text-zinc-400">
|
|
3704
|
+
<span class="rounded bg-zinc-100 px-1.5 py-px text-[10px] font-medium uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400">Epic</span>
|
|
3705
|
+
<span class="font-mono text-[11px]">${esc(e.id)}</span>
|
|
3706
|
+
<span class="text-zinc-300 dark:text-zinc-600">·</span>
|
|
3707
|
+
<span>${esc(e.milestone)}</span>
|
|
3708
|
+
<span class="ml-auto mr-16 inline-flex items-center gap-1.5 text-zinc-400 dark:text-zinc-500">${progressRing(done, stories.length)}<span class="font-mono text-[11px]">${done}/${stories.length} done</span></span>
|
|
3709
|
+
</div>
|
|
3710
|
+
<h2 class="mt-2 text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">${esc(e.title)}</h2>
|
|
3711
|
+
<div class="mt-4 space-y-1.5">${storyList}</div>
|
|
3712
|
+
<div class="mt-5 flex items-center gap-2 border-t border-zinc-100 pt-4 dark:border-zinc-800">
|
|
3713
|
+
<button type="button" class="detail-edit" data-edit-body title="Edit body (e)">
|
|
3714
|
+
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M11.5 2.5 13.5 4.5 5.5 12.5 2.5 13.5 3.5 10.5 Z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>
|
|
3715
|
+
<span>Edit</span>
|
|
3716
|
+
</button>
|
|
3717
|
+
</div>
|
|
3718
|
+
<div class="detail-body mt-2">${mdToHtml(e.body)}</div>
|
|
3719
|
+
${backlinksHtml((backlinks.get(e.id) ?? []).filter((s) => !stories.some((t) => t.id === s.id)), "Referenced by", false)}
|
|
3061
3720
|
</div>`;
|
|
3062
3721
|
}
|
|
3063
3722
|
function itemPaneHtml(id) {
|
|
@@ -3074,24 +3733,24 @@ function sidebarHtml(epics, tasks) {
|
|
|
3074
3733
|
const stories = tasks.filter((t) => t.epic === e.id);
|
|
3075
3734
|
const done = stories.filter((t) => t.status === "done").length;
|
|
3076
3735
|
return `<div role="button" tabindex="0" data-filter-epic="${esc(e.id)}" class="epic-row group" title="${esc(e.title)}: click to filter the board">
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3736
|
+
<span class="text-zinc-400 dark:text-zinc-500">${progressRing(done, stories.length)}</span>
|
|
3737
|
+
<span class="min-w-0 flex-1 truncate text-[12.5px] text-zinc-700 dark:text-zinc-300"><span class="font-mono text-[10.5px] text-zinc-400 dark:text-zinc-500">${esc(e.id)}</span> ${esc(e.title)}</span>
|
|
3738
|
+
<span class="font-mono text-[10px] text-zinc-400 dark:text-zinc-600">${done}/${stories.length}</span>
|
|
3739
|
+
<button type="button" data-open="${esc(e.id)}" aria-label="Open epic ${esc(e.id)}"
|
|
3740
|
+
class="rounded p-0.5 text-zinc-300 opacity-0 transition-opacity hover:bg-zinc-200 hover:text-zinc-600 group-hover:opacity-100 focus-visible:opacity-100 dark:text-zinc-600 dark:hover:bg-zinc-700 dark:hover:text-zinc-300">
|
|
3741
|
+
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M4.5 2.5 H9.5 V7.5 M9.5 2.5 L2.5 9.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
3742
|
+
</button>
|
|
3743
|
+
</div>`;
|
|
3085
3744
|
}).join("");
|
|
3086
3745
|
return `<aside id="sidebar" class="hidden w-60 shrink-0 flex-col gap-0.5 overflow-y-auto border-r border-zinc-200 bg-white px-2.5 py-3 dark:border-zinc-800 dark:bg-zinc-950 md:flex">
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3746
|
+
<div class="mb-1 px-2 text-[10.5px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">Epics</div>
|
|
3747
|
+
<div role="button" tabindex="0" data-filter-epic=""
|
|
3748
|
+
class="epic-row epic-active">
|
|
3749
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true" class="text-zinc-400"><rect x="1" y="1" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="6.6" y="1" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="1" y="6.6" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="6.6" y="6.6" width="4.4" height="4.4" rx="1" stroke="currentColor" stroke-width="1.2"/></svg>
|
|
3750
|
+
<span class="flex-1 text-[12.5px] text-zinc-700 dark:text-zinc-300">All epics</span>
|
|
3751
|
+
<span class="font-mono text-[10px] text-zinc-400 dark:text-zinc-600">${tasks.length}</span>
|
|
3752
|
+
</div>
|
|
3753
|
+
${rows}
|
|
3095
3754
|
</aside>`;
|
|
3096
3755
|
}
|
|
3097
3756
|
function boardColumns(tasks) {
|
|
@@ -3103,11 +3762,11 @@ function settingsPanelHtml() {
|
|
|
3103
3762
|
const hint = "mt-1 text-[11px] leading-relaxed text-zinc-400 dark:text-zinc-500";
|
|
3104
3763
|
const row = (name, control, note = "") => `<div class="set-row"><span class="set-label">${name}</span><div class="min-w-0 flex-1">${control}${note ? `<p class="${hint}">${note}</p>` : ""}</div></div>`;
|
|
3105
3764
|
const group = (title, sub, body, open = false) => `<details class="set-group" name="settings-group"${open ? " open" : ""}>
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3765
|
+
<summary>${title}<span class="set-sub">${sub}</span></summary>
|
|
3766
|
+
<div class="pb-3">${body}</div>
|
|
3767
|
+
</details>`;
|
|
3109
3768
|
const appearance = row("Theme", `<div class="flex gap-1">${["light", "dark"].map((t) => `<button type="button" data-set-theme="${t}"
|
|
3110
|
-
|
|
3769
|
+
class="theme-opt pick capitalize">${t}</button>`).join("")}</div>`) + row("Accent", `<div class="flex flex-wrap gap-1.5">${[
|
|
3111
3770
|
["violet", "swatch swatch-violet"],
|
|
3112
3771
|
["blue", "swatch swatch-blue"],
|
|
3113
3772
|
["emerald", "swatch swatch-emerald"],
|
|
@@ -3127,48 +3786,80 @@ function settingsPanelHtml() {
|
|
|
3127
3786
|
["docs", "Docs", "Markdown viewer and search"],
|
|
3128
3787
|
["graph", "Graph", "Force-directed map of links"]
|
|
3129
3788
|
].map(([v, name, why]) => `
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3789
|
+
<label class="flex items-center gap-2 text-[12.5px] text-zinc-700 dark:text-zinc-300">
|
|
3790
|
+
<input type="checkbox" name="view-${v}" class="h-3.5 w-3.5 accent-accent-600" ${settings.views.includes(v) ? "checked" : ""}>
|
|
3791
|
+
${name} <span class="text-[11px] text-zinc-400 dark:text-zinc-500">${why}</span>
|
|
3792
|
+
</label>`).join("")}</div>`, "Turn off whatever this workspace does not need. At least one has to stay on.") + row("Columns", `<input id="set-columns" name="columns" class="${field}" value="${esc(settings.columns.join(", "))}">`, "Comma separated, in board order. A status used by a task always gets a column even if listed nowhere here, so nothing goes missing.");
|
|
3134
3793
|
const git = row("Commits", `<label class="flex items-center gap-2 text-[12.5px] text-zinc-700 dark:text-zinc-300">
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3794
|
+
<input type="checkbox" name="gitCommit" class="h-3.5 w-3.5 accent-accent-600" ${settings.gitCommit ? "checked" : ""} ${GIT_COMMIT_ENV ? "disabled" : ""}>
|
|
3795
|
+
Commit every board mutation
|
|
3796
|
+
</label>`, GIT_COMMIT_ENV ? 'Forced on by <code class="font-mono">BOARD_GIT_COMMIT=1</code>.' : "Each status change or subtask toggle becomes its own commit.");
|
|
3138
3797
|
return `<template id="detail-settings"><div class="detail-root" data-id="settings" data-views="${settings.views.join(",")}">
|
|
3139
|
-
|
|
3140
|
-
|
|
3798
|
+
<h2 class="text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">Settings</h2>
|
|
3799
|
+
<p class="${hint}">Workspace settings live in <code class="font-mono">${SETTINGS_FILENAME}</code> and are meant to be committed. Appearance is per-browser.</p>
|
|
3141
3800
|
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3801
|
+
<div class="mt-4 border-t border-zinc-100 dark:border-zinc-800">
|
|
3802
|
+
${group("Appearance", "Theme, accent, typeface, size", appearance, true)}
|
|
3803
|
+
${group("Board", "Title, views, columns", board)}
|
|
3804
|
+
${group("Git", GIT_COMMIT_ENV || settings.gitCommit ? "Auto-commit on" : "Auto-commit off", git)}
|
|
3805
|
+
</div>
|
|
3147
3806
|
|
|
3148
|
-
|
|
3807
|
+
<p id="settings-error" class="mt-4 hidden rounded-md border border-rose-300 bg-rose-50 px-2.5 py-1.5 text-[12px] text-rose-700 dark:border-rose-500/40 dark:bg-rose-500/10 dark:text-rose-300" role="alert"></p>
|
|
3149
3808
|
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3809
|
+
<div class="mt-4 flex items-center gap-2 border-t border-zinc-100 pt-4 dark:border-zinc-800">
|
|
3810
|
+
<button id="settings-save" type="button"
|
|
3811
|
+
class="rounded-md bg-accent-600 px-3 py-1.5 text-[12.5px] font-medium text-white transition-colors hover:bg-accent-500">Save</button>
|
|
3812
|
+
<span class="font-mono text-[10.5px] text-zinc-400 dark:text-zinc-600">${esc(SETTINGS_FILENAME)}</span>
|
|
3813
|
+
</div>
|
|
3155
3814
|
</div></template>`;
|
|
3156
3815
|
}
|
|
3816
|
+
function compareTasks(a, b) {
|
|
3817
|
+
if (a.order !== b.order) {
|
|
3818
|
+
if (a.order === null)
|
|
3819
|
+
return 1;
|
|
3820
|
+
if (b.order === null)
|
|
3821
|
+
return -1;
|
|
3822
|
+
return a.order - b.order;
|
|
3823
|
+
}
|
|
3824
|
+
return (PRIORITY_ORDER[a.priority] ?? 9) - (PRIORITY_ORDER[b.priority] ?? 9) || a.id.localeCompare(b.id);
|
|
3825
|
+
}
|
|
3826
|
+
function siblings(tasks, status, epic) {
|
|
3827
|
+
return tasks.filter((t) => t.status === status && t.epic === epic).sort(compareTasks);
|
|
3828
|
+
}
|
|
3829
|
+
function groupByEpic(cards, epics) {
|
|
3830
|
+
const titles = new Map(epics.map((e) => [e.id, e.title]));
|
|
3831
|
+
const byEpic = new Map;
|
|
3832
|
+
for (const t of cards)
|
|
3833
|
+
byEpic.set(t.epic, [...byEpic.get(t.epic) ?? [], t]);
|
|
3834
|
+
return [...byEpic.entries()].sort(([a], [b]) => a === "" ? 1 : b === "" ? -1 : a.localeCompare(b)).map(([epic, group]) => ({
|
|
3835
|
+
epic,
|
|
3836
|
+
label: epic ? `${epic} ${titles.get(epic) ?? ""}`.trim() : "No epic",
|
|
3837
|
+
tasks: group.sort(compareTasks)
|
|
3838
|
+
}));
|
|
3839
|
+
}
|
|
3157
3840
|
function appHtml(tasks, epics) {
|
|
3158
3841
|
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
3159
3842
|
const columns = boardColumns(tasks).map((col) => {
|
|
3160
|
-
const cards = tasks.filter((t) => t.status === col)
|
|
3843
|
+
const cards = tasks.filter((t) => t.status === col);
|
|
3844
|
+
const groups = groupByEpic(cards, epics);
|
|
3845
|
+
const body = groups.length ? groups.map((g) => `<details class="card-group" open data-group="${esc(col)}/${esc(g.epic)}" data-epic="${esc(g.epic)}">
|
|
3846
|
+
<summary><span class="truncate">${esc(g.label)}</span><span class="card-group-count">${g.tasks.length}</span></summary>
|
|
3847
|
+
<div class="card-group-body" data-drop-epic="${esc(g.epic)}">
|
|
3848
|
+
${g.tasks.map((t) => cardHtml(t, byId)).join(`
|
|
3849
|
+
`)}
|
|
3850
|
+
</div>
|
|
3851
|
+
</details>`).join(`
|
|
3852
|
+
`) : '<div class="rounded-md border border-dashed border-zinc-200 py-6 text-center text-[12px] text-zinc-300 dark:border-zinc-800 dark:text-zinc-700">No tasks</div>';
|
|
3161
3853
|
return `<section data-col="${esc(col)}" class="flex w-[290px] shrink-0 flex-col rounded-lg p-1 transition-colors">
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
</section>`;
|
|
3854
|
+
<header class="mb-2.5 flex items-center gap-2 px-1 pt-1">
|
|
3855
|
+
${statusIcon(col)}
|
|
3856
|
+
<h2 class="text-[13px] font-medium capitalize text-zinc-700 dark:text-zinc-300">${esc(col.replace("-", " "))}</h2>
|
|
3857
|
+
<span class="col-count font-mono text-[11px] text-zinc-400 dark:text-zinc-600">${cards.length}</span>
|
|
3858
|
+
</header>
|
|
3859
|
+
<div class="flex min-h-[60px] flex-1 flex-col gap-1.5">
|
|
3860
|
+
${body}
|
|
3861
|
+
</div>
|
|
3862
|
+
</section>`;
|
|
3172
3863
|
}).join(`
|
|
3173
3864
|
`);
|
|
3174
3865
|
return `${sidebarHtml(epics, tasks)}
|
|
@@ -3199,25 +3890,25 @@ var ICON_BUTTON = "rounded p-1 text-zinc-400 hover:bg-zinc-100 hover:text-zinc-6
|
|
|
3199
3890
|
function headerHtml(active, meta, showSidebarToggle) {
|
|
3200
3891
|
return `<header class="flex h-12 items-center gap-3 border-b border-zinc-200 bg-white px-4 dark:border-zinc-800 dark:bg-zinc-950">
|
|
3201
3892
|
${showSidebarToggle ? `<button id="sidebar-toggle" type="button" aria-label="Toggle sidebar" title="Toggle sidebar"
|
|
3202
|
-
|
|
3203
|
-
|
|
3893
|
+
class="hidden ${ICON_BUTTON} md:block">
|
|
3894
|
+
<svg width="15" height="15" viewBox="0 0 16 16" fill="none" aria-hidden="true"><rect x="1.5" y="2.5" width="13" height="11" rx="2" stroke="currentColor" stroke-width="1.4"/><path d="M6 2.5 V13.5" stroke="currentColor" stroke-width="1.4"/></svg>
|
|
3204
3895
|
</button>` : ""}
|
|
3205
3896
|
<span class="flex items-center gap-2 text-[13px] font-semibold tracking-tight">
|
|
3206
|
-
|
|
3897
|
+
<img src="/logo.svg" width="17" height="17" alt="" class="shrink-0 dark:brightness-125">${esc(settings.title)}
|
|
3207
3898
|
</span>
|
|
3208
3899
|
${navTabs(active)}
|
|
3209
3900
|
<span id="live-dot" class="h-1.5 w-1.5 rounded-full bg-emerald-500" title="Live: file changes appear automatically"></span>
|
|
3210
3901
|
<div class="ml-auto flex items-center gap-2.5">
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3902
|
+
<input id="search" type="search" placeholder="Search" autocomplete="off" spellcheck="false"
|
|
3903
|
+
class="hidden w-44 rounded-md border border-zinc-200 bg-zinc-50 px-2.5 py-1 text-[12px] outline-none transition-colors placeholder:text-zinc-400 focus:border-accent-400 dark:border-zinc-800 dark:bg-zinc-900 dark:placeholder:text-zinc-600 dark:focus:border-accent-500 sm:block">
|
|
3904
|
+
<div id="header-meta" class="flex items-center gap-2.5">${meta}</div>
|
|
3905
|
+
<button type="button" data-open="new" aria-label="New item" title="New epic, story or document" class="${ICON_BUTTON}">
|
|
3906
|
+
<svg width="15" height="15" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M8 3.5 V12.5 M3.5 8 H12.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
|
|
3907
|
+
</button>
|
|
3908
|
+
<button id="theme-toggle" type="button" aria-label="Change theme" class="${ICON_BUTTON}"></button>
|
|
3909
|
+
<button type="button" data-open="settings" aria-label="Settings" title="Settings" class="${ICON_BUTTON}">
|
|
3910
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
|
|
3911
|
+
</button>
|
|
3221
3912
|
</div>
|
|
3222
3913
|
</header>`;
|
|
3223
3914
|
}
|
|
@@ -3227,12 +3918,12 @@ var SHEET_HTML = `<div id="backdrop" class="fixed inset-0 z-40 hidden bg-black/2
|
|
|
3227
3918
|
<aside id="sheet" tabindex="-1" role="dialog" aria-modal="true" aria-label="Details"
|
|
3228
3919
|
class="fixed inset-y-0 right-0 z-50 w-full max-w-[460px] translate-x-full overflow-y-auto border-l border-zinc-200 bg-white p-6 shadow-xl outline-none motion-safe:transition-transform motion-safe:duration-200 dark:border-zinc-800 dark:bg-zinc-900">
|
|
3229
3920
|
<div class="absolute right-4 top-4 flex items-center gap-0.5">
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3921
|
+
<button id="sheet-expand" type="button" aria-label="Toggle fullscreen" title="Toggle fullscreen" class="${ICON_BUTTON}">
|
|
3922
|
+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M8.5 1.5 H12.5 V5.5 M12.5 1.5 L8 6 M5.5 12.5 H1.5 V8.5 M1.5 12.5 L6 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
3923
|
+
</button>
|
|
3924
|
+
<button id="sheet-close" type="button" aria-label="Close" title="Close (Esc)" class="${ICON_BUTTON}">
|
|
3925
|
+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M3 3 L11 11 M11 3 L3 11" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
|
|
3926
|
+
</button>
|
|
3236
3927
|
</div>
|
|
3237
3928
|
<div id="sheet-content"></div>
|
|
3238
3929
|
</aside>
|
|
@@ -3260,22 +3951,63 @@ ${SHEET_HTML}
|
|
|
3260
3951
|
<script>${board_default}</script>
|
|
3261
3952
|
</body></html>`;
|
|
3262
3953
|
}
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
const
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3954
|
+
var MAX_DOC_DEPTH = 8;
|
|
3955
|
+
function readDocDir(rel, depth) {
|
|
3956
|
+
const node = {
|
|
3957
|
+
name: rel === "" ? "planner" : basename(rel),
|
|
3958
|
+
path: rel === "" ? "planner" : rel,
|
|
3959
|
+
files: [],
|
|
3960
|
+
dirs: []
|
|
3961
|
+
};
|
|
3962
|
+
let entries;
|
|
3963
|
+
try {
|
|
3964
|
+
entries = readdirSync(join(PLANNER_DIR, rel), { withFileTypes: true });
|
|
3965
|
+
} catch {
|
|
3966
|
+
return node;
|
|
3967
|
+
}
|
|
3968
|
+
for (const e of entries) {
|
|
3969
|
+
if (e.name.startsWith(".") || DOC_EXCLUDE.has(e.name))
|
|
3970
|
+
continue;
|
|
3971
|
+
const child = rel ? `${rel}/${e.name}` : e.name;
|
|
3972
|
+
if (e.isDirectory()) {
|
|
3973
|
+
if (depth >= MAX_DOC_DEPTH)
|
|
3974
|
+
continue;
|
|
3975
|
+
const sub = readDocDir(child, depth + 1);
|
|
3976
|
+
if (sub.files.length || sub.dirs.length)
|
|
3977
|
+
node.dirs.push(sub);
|
|
3978
|
+
} else if (e.name.endsWith(".md")) {
|
|
3979
|
+
node.files.push(child);
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
node.files.sort((a, b) => {
|
|
3983
|
+
if (rel === "") {
|
|
3984
|
+
const ia = ROOT_DOC_ORDER.indexOf(a);
|
|
3985
|
+
const ib = ROOT_DOC_ORDER.indexOf(b);
|
|
3986
|
+
if (ia !== ib)
|
|
3987
|
+
return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
|
|
3988
|
+
}
|
|
3989
|
+
return a.localeCompare(b);
|
|
3269
3990
|
});
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3991
|
+
node.dirs.sort((a, b) => a.name.localeCompare(b.name));
|
|
3992
|
+
return node;
|
|
3993
|
+
}
|
|
3994
|
+
function listDocs() {
|
|
3995
|
+
const root = readDocDir("", 0);
|
|
3996
|
+
const groups = root.files.length ? [{ ...root, dirs: [] }] : [];
|
|
3997
|
+
return [...groups, ...root.dirs];
|
|
3998
|
+
}
|
|
3999
|
+
function docGroup(rel) {
|
|
4000
|
+
const i = rel.indexOf("/");
|
|
4001
|
+
return i === -1 ? "planner" : rel.slice(0, i);
|
|
4002
|
+
}
|
|
4003
|
+
function allDocFiles() {
|
|
4004
|
+
const out = [];
|
|
4005
|
+
const walk = (d) => {
|
|
4006
|
+
out.push(...d.files);
|
|
4007
|
+
d.dirs.forEach(walk);
|
|
4008
|
+
};
|
|
4009
|
+
listDocs().forEach(walk);
|
|
4010
|
+
return out;
|
|
3279
4011
|
}
|
|
3280
4012
|
var RAW_TYPES = {
|
|
3281
4013
|
svg: "image/svg+xml",
|
|
@@ -3356,7 +4088,7 @@ function timelineHtml(rows) {
|
|
|
3356
4088
|
while (+cursor < max) {
|
|
3357
4089
|
const p = pct(+cursor);
|
|
3358
4090
|
months.push(`<div class="absolute inset-y-0 border-l border-zinc-200 dark:border-zinc-800" style="left:${p.toFixed(2)}%"></div>
|
|
3359
|
-
|
|
4091
|
+
<div class="absolute top-0 -translate-x-1/2 text-[9.5px] font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-600" style="left:${p.toFixed(2)}%">${cursor.toLocaleDateString("en", { month: "short" })}</div>`);
|
|
3360
4092
|
cursor.setMonth(cursor.getMonth() + 1);
|
|
3361
4093
|
}
|
|
3362
4094
|
const bars = rows.map((r) => {
|
|
@@ -3367,43 +4099,47 @@ function timelineHtml(rows) {
|
|
|
3367
4099
|
const label = `${r.id} ${r.title}`.trim();
|
|
3368
4100
|
const dates = `${r.start.toISOString().slice(0, 10)} to ${r.end.toISOString().slice(0, 10)}`;
|
|
3369
4101
|
return `<div class="flex h-8 items-center">
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
4102
|
+
<div class="w-44 shrink-0 truncate pr-3 text-[12px] text-zinc-600 dark:text-zinc-400" title="${esc(label)}">
|
|
4103
|
+
${r.epic ? `<a href="/#${esc(r.epic)}" class="font-mono text-[10.5px] text-accent-600 hover:underline dark:text-accent-400">${esc(r.epic)}</a> ` : ""}<span class="font-medium text-zinc-800 dark:text-zinc-200">${esc(r.id)}</span> ${esc(r.title)}
|
|
4104
|
+
</div>
|
|
4105
|
+
<div class="relative h-5 flex-1">
|
|
4106
|
+
<div class="track absolute inset-y-0 hidden"></div>
|
|
4107
|
+
<div class="absolute inset-y-0 overflow-hidden whitespace-nowrap rounded-md px-2 text-[10px] font-medium leading-5 text-white shadow-sm" style="left:${left.toFixed(2)}%;width:${width.toFixed(2)}%;background:${color}" title="${esc(label)}: ${dates} (${status})">${esc(r.title)}</div>
|
|
4108
|
+
</div>
|
|
4109
|
+
</div>`;
|
|
3378
4110
|
}).join("");
|
|
3379
4111
|
const todayMark = today >= min && today <= max ? `<div class="absolute inset-y-0 z-10 border-l-2 border-rose-500/70" style="left:calc(11rem + (100% - 11rem) * ${(pct(today) / 100).toFixed(4)})">
|
|
3380
|
-
|
|
3381
|
-
|
|
4112
|
+
<span class="absolute -top-0.5 left-1 text-[9px] font-semibold uppercase tracking-wide text-rose-500">today</span>
|
|
4113
|
+
</div>` : "";
|
|
3382
4114
|
return `<div class="mb-8 rounded-lg border border-zinc-200 bg-white p-4 dark:border-zinc-800 dark:bg-zinc-900">
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
4115
|
+
<div class="mb-3 flex items-baseline justify-between">
|
|
4116
|
+
<span class="text-[11px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">Timeline</span>
|
|
4117
|
+
<span class="text-[10.5px] text-zinc-400 dark:text-zinc-600">${new Date(min + 2 * DAY).toISOString().slice(0, 10)} to ${new Date(max - 2 * DAY).toISOString().slice(0, 10)}</span>
|
|
4118
|
+
</div>
|
|
4119
|
+
<div class="relative pt-4">
|
|
4120
|
+
<div class="absolute inset-y-0 left-44 right-0">${months.join("")}</div>
|
|
4121
|
+
${todayMark}
|
|
4122
|
+
<div class="relative flex flex-col gap-1">${bars}</div>
|
|
4123
|
+
</div>
|
|
4124
|
+
<div class="mt-3 flex gap-4 text-[10px] text-zinc-400 dark:text-zinc-500">
|
|
4125
|
+
<span class="inline-flex items-center gap-1.5"><span class="h-2 w-2 rounded-sm" style="background:${TIMELINE_COLOR.done}"></span>done</span>
|
|
4126
|
+
<span class="inline-flex items-center gap-1.5"><span class="h-2 w-2 rounded-sm" style="background:${TIMELINE_COLOR.active}"></span>active</span>
|
|
4127
|
+
<span class="inline-flex items-center gap-1.5"><span class="h-2 w-2 rounded-sm" style="background:${TIMELINE_COLOR.pending}"></span>pending</span>
|
|
4128
|
+
</div>
|
|
3397
4129
|
</div>`;
|
|
3398
4130
|
}
|
|
3399
|
-
function
|
|
3400
|
-
return
|
|
3401
|
-
|
|
3402
|
-
|
|
4131
|
+
function countDocs(d) {
|
|
4132
|
+
return d.files.length + d.dirs.reduce((n, sub) => n + countDocs(sub), 0);
|
|
4133
|
+
}
|
|
4134
|
+
function docsNavHtml(dirs, depth = 0) {
|
|
4135
|
+
return dirs.map((d) => `<details class="doc-folder" open data-folder="${esc(d.path)}" style="--doc-depth:${depth}">
|
|
4136
|
+
<summary>${esc(d.name)}<span class="doc-folder-count">${countDocs(d)}</span></summary>
|
|
4137
|
+
${docsNavHtml(d.dirs, depth + 1)}
|
|
4138
|
+
${d.files.map((f) => {
|
|
3403
4139
|
const name = basename(f);
|
|
3404
|
-
return `<a href="/docs?file=${encodeURIComponent(f)}" class="doc-link" data-file="${esc(f)}" title="${esc(f)}">${esc(name)}</a>`;
|
|
4140
|
+
return `<a href="/docs?file=${encodeURIComponent(f)}" class="doc-link" data-file="${esc(f)}" title="${esc(f)}" style="--doc-depth:${depth + 1}">${esc(name)}</a>`;
|
|
3405
4141
|
}).join("")}
|
|
3406
|
-
|
|
4142
|
+
</details>`).join("");
|
|
3407
4143
|
}
|
|
3408
4144
|
function docsPageHtml(relFile) {
|
|
3409
4145
|
const groups = listDocs();
|
|
@@ -3418,7 +4154,7 @@ function docsPageHtml(relFile) {
|
|
|
3418
4154
|
const parsed = parseFrontmatter(raw);
|
|
3419
4155
|
const body = parsed ? parsed.body : raw;
|
|
3420
4156
|
const fm = parsed?.fm ?? {};
|
|
3421
|
-
hasMermaid =
|
|
4157
|
+
hasMermaid = /^[ \t]*```mermaid/m.test(body);
|
|
3422
4158
|
const isTimeline = fm.render === "timeline" || basename(relFile).toUpperCase() === "TIMELINE.MD";
|
|
3423
4159
|
const timeline = isTimeline ? parseTimeline(body) : null;
|
|
3424
4160
|
const baseDir = dirname(relFile) === "." ? "" : dirname(relFile);
|
|
@@ -3426,9 +4162,14 @@ function docsPageHtml(relFile) {
|
|
|
3426
4162
|
const sources = [...backlinks.get(relFile) ?? [], ...fm.id ? backlinks.get(fm.id) ?? [] : []];
|
|
3427
4163
|
const seen = new Set;
|
|
3428
4164
|
const unique = sources.filter((s) => seen.has(s.file) ? false : (seen.add(s.file), true));
|
|
3429
|
-
main = `${timeline ? timelineHtml(timeline) : ""}
|
|
3430
|
-
|
|
3431
|
-
|
|
4165
|
+
main = `${timeline ? timelineHtml(timeline) : ""}
|
|
4166
|
+
<button type="button" id="doc-edit" class="doc-edit" title="Edit this document (e)" aria-label="Edit this document">
|
|
4167
|
+
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M11.5 2.5 13.5 4.5 5.5 12.5 2.5 13.5 3.5 10.5 Z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>
|
|
4168
|
+
<span>Edit</span>
|
|
4169
|
+
</button>
|
|
4170
|
+
<article class="doc-body">${mdToHtml(body, { docMode: true, baseDir })}</article>
|
|
4171
|
+
${backlinksHtml(unique, "Linked mentions", true)}
|
|
4172
|
+
<div class="mt-10 border-t border-zinc-100 pt-3 font-mono text-[10.5px] text-zinc-400 dark:border-zinc-800 dark:text-zinc-600">${esc(relFile)}</div>`;
|
|
3432
4173
|
}
|
|
3433
4174
|
return `<!doctype html>
|
|
3434
4175
|
<html lang="en"><head><meta charset="utf-8">
|
|
@@ -3444,22 +4185,22 @@ ${headerHtml("docs", "", true)}
|
|
|
3444
4185
|
|
|
3445
4186
|
<div class="flex h-[calc((100vh/var(--zoom))-3rem)]">
|
|
3446
4187
|
<aside id="doc-sidebar" class="hidden w-60 shrink-0 flex-col overflow-y-auto border-r border-zinc-200 bg-white px-2.5 py-2 dark:border-zinc-800 dark:bg-zinc-950 md:flex">
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
4188
|
+
<div class="mb-1 flex items-center gap-1 px-2 pb-1">
|
|
4189
|
+
<span class="text-[10.5px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">Files</span>
|
|
4190
|
+
<button id="fold-all" type="button" class="${ICON_BUTTON} ml-auto" aria-label="Collapse all folders" title="Collapse all folders">
|
|
4191
|
+
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6.5 8 2.5 12 6.5 M4 9.5 8 13.5 12 9.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
4192
|
+
</button>
|
|
4193
|
+
</div>
|
|
4194
|
+
<div id="doc-tree">${nav}</div>
|
|
3454
4195
|
</aside>
|
|
3455
4196
|
${RESIZER_HTML}
|
|
3456
4197
|
<main id="doc-main" class="flex-1 overflow-y-auto">
|
|
3457
|
-
|
|
3458
|
-
|
|
4198
|
+
<div id="search-results" class="mx-auto hidden max-w-3xl divide-y divide-zinc-200 px-8 py-5 dark:divide-zinc-800"></div>
|
|
4199
|
+
<div id="doc-content" class="mx-auto max-w-3xl px-8 py-8"${abs ? ` data-file="${esc(relFile)}" data-rev="${esc(fileRev(abs))}"` : ""}>${main}</div>
|
|
3459
4200
|
</main>
|
|
3460
4201
|
<aside id="doc-outline" class="hidden w-56 shrink-0 overflow-y-auto border-l border-zinc-200 bg-white px-3 py-3 dark:border-zinc-800 dark:bg-zinc-950 lg:block">
|
|
3461
|
-
|
|
3462
|
-
|
|
4202
|
+
<div class="mb-1.5 px-2 text-[10.5px] font-semibold uppercase tracking-wider text-zinc-400 dark:text-zinc-500">On this page</div>
|
|
4203
|
+
<nav id="outline-list"></nav>
|
|
3463
4204
|
</aside>
|
|
3464
4205
|
</div>
|
|
3465
4206
|
|
|
@@ -3478,57 +4219,57 @@ function newItemPanelHtml() {
|
|
|
3478
4219
|
const columns = boardColumns(allTasks());
|
|
3479
4220
|
const opt = (v, text) => `<option value="${esc(v)}">${esc(text)}</option>`;
|
|
3480
4221
|
return `<template id="detail-new"><div class="detail-root" data-id="new">
|
|
3481
|
-
|
|
3482
|
-
|
|
4222
|
+
<h2 class="text-[16px] font-semibold leading-snug text-zinc-900 dark:text-zinc-50">New</h2>
|
|
4223
|
+
<p class="${hint}">Writes a markdown file into the workspace. Ids and filenames are assigned for you.</p>
|
|
3483
4224
|
|
|
3484
|
-
|
|
3485
|
-
|
|
4225
|
+
<div class="mt-4 flex gap-1" data-kind-picker>
|
|
4226
|
+
${[
|
|
3486
4227
|
["task", "User story"],
|
|
3487
4228
|
["epic", "Epic"],
|
|
3488
4229
|
["doc", "Document"]
|
|
3489
4230
|
].map(([k, name]) => `<button type="button" class="pick" data-kind="${k}">${name}</button>`).join("")}
|
|
3490
|
-
|
|
4231
|
+
</div>
|
|
3491
4232
|
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
4233
|
+
<div class="mt-4"><label class="${label}" for="new-title">Title</label>
|
|
4234
|
+
<input id="new-title" name="title" class="${field}" maxlength="200" placeholder="Short imperative title">
|
|
4235
|
+
</div>
|
|
3495
4236
|
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
4237
|
+
<div class="mt-3" data-for="doc"><label class="${label}" for="new-path">File path</label>
|
|
4238
|
+
<input id="new-path" name="path" class="${field}" placeholder="notes/my-document.md">
|
|
4239
|
+
<p class="${hint}">Relative to the workspace root. Folders are created as needed.</p>
|
|
4240
|
+
</div>
|
|
3500
4241
|
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
</div>
|
|
3507
|
-
<div><label class="${label}" for="new-priority">Priority</label>
|
|
3508
|
-
<select id="new-priority" name="priority" class="${field}">
|
|
3509
|
-
${PRIORITIES.map((p) => opt(p, p)).join("")}
|
|
3510
|
-
</select>
|
|
3511
|
-
</div>
|
|
3512
|
-
<div><label class="${label}" for="new-status">Status</label>
|
|
3513
|
-
<select id="new-status" name="status" class="${field}">${columns.map((c) => opt(c, c.replace(/-/g, " "))).join("")}</select>
|
|
3514
|
-
</div>
|
|
4242
|
+
<div class="mt-3 grid grid-cols-2 gap-3" data-for="task">
|
|
4243
|
+
<div><label class="${label}" for="new-epic">Epic</label>
|
|
4244
|
+
<select id="new-epic" name="epic" class="${field}">
|
|
4245
|
+
${opt("", "(none)")}${epics.map((e) => opt(e.id, `${e.id} ${e.title}`)).join("")}
|
|
4246
|
+
</select>
|
|
3515
4247
|
</div>
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
4248
|
+
<div><label class="${label}" for="new-priority">Priority</label>
|
|
4249
|
+
<select id="new-priority" name="priority" class="${field}">
|
|
4250
|
+
${PRIORITIES.map((p) => opt(p, p)).join("")}
|
|
4251
|
+
</select>
|
|
3519
4252
|
</div>
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
<textarea id="new-subtasks" name="subtasks" rows="3" class="${field} resize-y" placeholder="One per line"></textarea>
|
|
4253
|
+
<div><label class="${label}" for="new-status">Status</label>
|
|
4254
|
+
<select id="new-status" name="status" class="${field}">${columns.map((c) => opt(c, c.replace(/-/g, " "))).join("")}</select>
|
|
3523
4255
|
</div>
|
|
4256
|
+
</div>
|
|
3524
4257
|
|
|
3525
|
-
|
|
4258
|
+
<div class="mt-3"><label class="${label}" for="new-body">Description</label>
|
|
4259
|
+
<textarea id="new-body" name="body" rows="5" class="${field} resize-y" placeholder="Markdown. Optional."></textarea>
|
|
4260
|
+
</div>
|
|
3526
4261
|
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
4262
|
+
<div class="mt-3" data-for="task"><label class="${label}" for="new-subtasks">Subtasks</label>
|
|
4263
|
+
<textarea id="new-subtasks" name="subtasks" rows="3" class="${field} resize-y" placeholder="One per line"></textarea>
|
|
4264
|
+
</div>
|
|
4265
|
+
|
|
4266
|
+
<p id="new-error" class="mt-4 hidden rounded-md border border-rose-300 bg-rose-50 px-2.5 py-1.5 text-[12px] text-rose-700 dark:border-rose-500/40 dark:bg-rose-500/10 dark:text-rose-300" role="alert"></p>
|
|
4267
|
+
|
|
4268
|
+
<div class="mt-4 flex items-center gap-2 border-t border-zinc-100 pt-4 dark:border-zinc-800">
|
|
4269
|
+
<button id="new-save" type="button"
|
|
4270
|
+
class="rounded-md bg-accent-600 px-3 py-1.5 text-[12.5px] font-medium text-white transition-colors hover:bg-accent-500">Create</button>
|
|
4271
|
+
<span id="new-target" class="font-mono text-[10.5px] text-zinc-400 dark:text-zinc-600"></span>
|
|
4272
|
+
</div>
|
|
3532
4273
|
</div></template>`;
|
|
3533
4274
|
}
|
|
3534
4275
|
var GRAPH_BUTTON = "rounded-md border border-zinc-200 bg-white px-2 py-1 text-[11px] text-zinc-500 transition-colors hover:text-zinc-900 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-100";
|
|
@@ -3550,8 +4291,8 @@ ${headerHtml("graph", `<span class="text-[11px] text-zinc-400 dark:text-zinc-600
|
|
|
3550
4291
|
<div id="graph-empty" class="pointer-events-none absolute inset-0 hidden items-center justify-center text-[13px] text-zinc-400 dark:text-zinc-600">No documents to graph yet.</div>
|
|
3551
4292
|
<div id="graph-hint" class="pointer-events-none absolute bottom-3 left-3 text-[10.5px] text-zinc-400 dark:text-zinc-600">Scroll to zoom · drag to pan · click a note to open it</div>
|
|
3552
4293
|
<div class="absolute bottom-3 right-3 flex items-center gap-1.5">
|
|
3553
|
-
|
|
3554
|
-
|
|
4294
|
+
<button id="graph-shape" type="button" class="${GRAPH_BUTTON}">Globe</button>
|
|
4295
|
+
<button id="graph-reset" type="button" title="Reset view" class="${GRAPH_BUTTON}">Reset</button>
|
|
3555
4296
|
</div>
|
|
3556
4297
|
</div>
|
|
3557
4298
|
|
|
@@ -3560,7 +4301,7 @@ ${newItemPanelHtml()}
|
|
|
3560
4301
|
${SHEET_HTML}
|
|
3561
4302
|
|
|
3562
4303
|
<!-- The graph ships with the page instead of as a second request. It is already built here for the
|
|
3563
|
-
|
|
4304
|
+
header count, and one round trip on a cold connection is the slowest part of this view. -->
|
|
3564
4305
|
<script type="application/json" id="graph-data">${JSON.stringify({ nodes, links }).replace(/</g, "\\u003c")}</script>
|
|
3565
4306
|
<script>${graph_default}</script>
|
|
3566
4307
|
</body></html>`;
|
|
@@ -3634,6 +4375,51 @@ function htmlPage(key, req, render) {
|
|
|
3634
4375
|
headers: { "content-type": "text/html; charset=utf-8", etag: hit.etag, "cache-control": "no-cache" }
|
|
3635
4376
|
});
|
|
3636
4377
|
}
|
|
4378
|
+
var ORDER_STEP = 100;
|
|
4379
|
+
var ORDER_MIN_GAP = 0.001;
|
|
4380
|
+
function placeInOrder(rest, patch, self) {
|
|
4381
|
+
let index;
|
|
4382
|
+
if (patch.move === "up" || patch.move === "down") {
|
|
4383
|
+
const here = rest.filter((t) => compareTasks(t, self) < 0).length;
|
|
4384
|
+
index = patch.move === "up" ? here - 1 : here + 1;
|
|
4385
|
+
if (index < 0 || index > rest.length)
|
|
4386
|
+
return { own: null, writes: new Map };
|
|
4387
|
+
} else {
|
|
4388
|
+
const anchor = patch.before ?? patch.after;
|
|
4389
|
+
const at = rest.findIndex((t) => t.id === anchor);
|
|
4390
|
+
if (at === -1)
|
|
4391
|
+
return null;
|
|
4392
|
+
index = patch.before !== undefined ? at : at + 1;
|
|
4393
|
+
}
|
|
4394
|
+
const writes = new Map;
|
|
4395
|
+
if (rest.some((t) => t.order === null)) {
|
|
4396
|
+
rest.forEach((t, i) => {
|
|
4397
|
+
const value = (i + 1) * ORDER_STEP;
|
|
4398
|
+
if (t.order !== value)
|
|
4399
|
+
writes.set(t.id, value);
|
|
4400
|
+
t.order = value;
|
|
4401
|
+
});
|
|
4402
|
+
}
|
|
4403
|
+
const ranks = rest.map((t) => t.order);
|
|
4404
|
+
const before = index > 0 ? ranks[index - 1] : null;
|
|
4405
|
+
const after = index < ranks.length ? ranks[index] : null;
|
|
4406
|
+
if (before !== null && after !== null) {
|
|
4407
|
+
if (after - before > ORDER_MIN_GAP)
|
|
4408
|
+
return { own: (before + after) / 2, writes };
|
|
4409
|
+
const respaced = [...rest];
|
|
4410
|
+
respaced.splice(index, 0, self);
|
|
4411
|
+
respaced.forEach((t, i) => {
|
|
4412
|
+
if (t.id !== self.id)
|
|
4413
|
+
writes.set(t.id, (i + 1) * ORDER_STEP);
|
|
4414
|
+
});
|
|
4415
|
+
return { own: (index + 1) * ORDER_STEP, writes };
|
|
4416
|
+
}
|
|
4417
|
+
if (after !== null)
|
|
4418
|
+
return { own: after - ORDER_STEP, writes };
|
|
4419
|
+
if (before !== null)
|
|
4420
|
+
return { own: before + ORDER_STEP, writes };
|
|
4421
|
+
return { own: ORDER_STEP, writes };
|
|
4422
|
+
}
|
|
3637
4423
|
async function handlePatch(id, req, ctx) {
|
|
3638
4424
|
const path = findItemPath(id);
|
|
3639
4425
|
if (!path)
|
|
@@ -3675,6 +4461,49 @@ async function handlePatch(id, req, ctx) {
|
|
|
3675
4461
|
raw = updated;
|
|
3676
4462
|
messages.push(`${id}: toggle subtask via board`);
|
|
3677
4463
|
}
|
|
4464
|
+
if (patch.body !== undefined) {
|
|
4465
|
+
const body = cleanDocument(patch.body);
|
|
4466
|
+
if (body === null)
|
|
4467
|
+
return json({ error: `body must be text under ${MAX_DOC_BYTES} characters` }, 413);
|
|
4468
|
+
const updated = replaceBody(raw, body);
|
|
4469
|
+
if (updated !== raw) {
|
|
4470
|
+
raw = updated;
|
|
4471
|
+
messages.push(`${id}: edit body via board`);
|
|
4472
|
+
}
|
|
4473
|
+
}
|
|
4474
|
+
if (patch.epic !== undefined) {
|
|
4475
|
+
if (patch.epic !== "" && !allEpics().some((e) => e.id === patch.epic)) {
|
|
4476
|
+
return json({ error: `unknown epic ${patch.epic}` }, 400);
|
|
4477
|
+
}
|
|
4478
|
+
const old = getFrontmatterValue(raw, "epic");
|
|
4479
|
+
if (old !== patch.epic) {
|
|
4480
|
+
raw = setFrontmatterValue(raw, "epic", patch.epic);
|
|
4481
|
+
messages.push(`${id}: epic ${old || "none"} -> ${patch.epic || "none"} via board`);
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
if (patch.move !== undefined || patch.before !== undefined || patch.after !== undefined) {
|
|
4485
|
+
const self = allTasks().find((t) => t.id === id);
|
|
4486
|
+
if (!self)
|
|
4487
|
+
return json({ error: `unknown task ${id}` }, 404);
|
|
4488
|
+
const status = patch.status ?? self.status;
|
|
4489
|
+
const epic = patch.epic ?? self.epic;
|
|
4490
|
+
const placed = placeInOrder(siblings(allTasks(), status, epic).filter((t) => t.id !== id), patch, self);
|
|
4491
|
+
if (placed === null)
|
|
4492
|
+
return json({ error: "move target not found in this group" }, 400);
|
|
4493
|
+
if (placed.own !== null) {
|
|
4494
|
+
for (const [target, value] of placed.writes) {
|
|
4495
|
+
if (target === id)
|
|
4496
|
+
continue;
|
|
4497
|
+
const p = findItemPath(target);
|
|
4498
|
+
if (p)
|
|
4499
|
+
writeFileSync2(p, setFrontmatterValue(readFileSync2(p, "utf8"), "order", String(value)));
|
|
4500
|
+
}
|
|
4501
|
+
if (getFrontmatterValue(raw, "order") !== String(placed.own)) {
|
|
4502
|
+
raw = setFrontmatterValue(raw, "order", String(placed.own));
|
|
4503
|
+
messages.push(`${id}: order ${placed.own} via board`);
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
3678
4507
|
const rel = relative(PLANNER_DIR, path);
|
|
3679
4508
|
if (messages.length === 0) {
|
|
3680
4509
|
ctx.detail = `${rel} unchanged`;
|
|
@@ -3709,6 +4538,20 @@ function nextId(prefix, width, ids) {
|
|
|
3709
4538
|
}
|
|
3710
4539
|
return prefix + String(max + 1).padStart(width, "0");
|
|
3711
4540
|
}
|
|
4541
|
+
var MAX_DOC_BYTES = 1e6;
|
|
4542
|
+
function cleanDocument(v) {
|
|
4543
|
+
if (typeof v !== "string" || v.length > MAX_DOC_BYTES)
|
|
4544
|
+
return null;
|
|
4545
|
+
return [...v.replace(/\r\n?/g, `
|
|
4546
|
+
`)].filter((c) => c >= " " || c === `
|
|
4547
|
+
` || c === "\t").join("");
|
|
4548
|
+
}
|
|
4549
|
+
function replaceBody(raw, body) {
|
|
4550
|
+
const m = raw.match(/^(---\n[\s\S]*?\n---\n?)([\s\S]*)$/);
|
|
4551
|
+
return (m ? m[1] : "") + (body.endsWith(`
|
|
4552
|
+
`) || body === "" ? body : body + `
|
|
4553
|
+
`);
|
|
4554
|
+
}
|
|
3712
4555
|
function cleanBody(v) {
|
|
3713
4556
|
if (typeof v !== "string")
|
|
3714
4557
|
return "";
|
|
@@ -3830,9 +4673,24 @@ async function handleDocPatch(rel, req, ctx) {
|
|
|
3830
4673
|
if (patch.rev && fileRev(path) !== String(patch.rev)) {
|
|
3831
4674
|
return json({ error: "stale: file changed since it was rendered" }, 409);
|
|
3832
4675
|
}
|
|
4676
|
+
const raw = readFileSync2(path, "utf8");
|
|
4677
|
+
if (patch.body !== undefined) {
|
|
4678
|
+
const body = cleanDocument(patch.body);
|
|
4679
|
+
if (body === null)
|
|
4680
|
+
return json({ error: `body must be text under ${MAX_DOC_BYTES} characters` }, 413);
|
|
4681
|
+
const updated2 = replaceBody(raw, body);
|
|
4682
|
+
if (updated2 === raw) {
|
|
4683
|
+
ctx.detail = `${rel} unchanged`;
|
|
4684
|
+
return json({ ok: true, rev: fileRev(path), unchanged: true });
|
|
4685
|
+
}
|
|
4686
|
+
writeFileSync2(path, updated2);
|
|
4687
|
+
gitCommitFile(path, `${rel}: edit via board`);
|
|
4688
|
+
scheduleBroadcast();
|
|
4689
|
+
ctx.detail = `${rel}: edit${gitEnabled() ? " [committed]" : ""}`;
|
|
4690
|
+
return json({ ok: true, rev: fileRev(path) });
|
|
4691
|
+
}
|
|
3833
4692
|
if (!patch.checkbox)
|
|
3834
4693
|
return json({ error: "nothing to change" }, 400);
|
|
3835
|
-
const raw = readFileSync2(path, "utf8");
|
|
3836
4694
|
const updated = toggleCheckboxLine(raw, Number(patch.checkbox.index), Boolean(patch.checkbox.checked));
|
|
3837
4695
|
if (updated === null)
|
|
3838
4696
|
return json({ error: `checkbox ${patch.checkbox.index} not found` }, 400);
|
|
@@ -3963,8 +4821,8 @@ async function handle(req, ctx) {
|
|
|
3963
4821
|
const q = (url.searchParams.get("q") ?? "").trim().toLowerCase();
|
|
3964
4822
|
const hits = [];
|
|
3965
4823
|
if (q) {
|
|
3966
|
-
for (const
|
|
3967
|
-
|
|
4824
|
+
for (const f of allDocFiles()) {
|
|
4825
|
+
{
|
|
3968
4826
|
const abs = safeDocPath(f);
|
|
3969
4827
|
if (!abs)
|
|
3970
4828
|
continue;
|
|
@@ -3992,6 +4850,15 @@ async function handle(req, ctx) {
|
|
|
3992
4850
|
if (url.pathname === "/api/docs" && req.method === "PATCH") {
|
|
3993
4851
|
return handleDocPatch(url.searchParams.get("file") ?? "", req, ctx);
|
|
3994
4852
|
}
|
|
4853
|
+
if (url.pathname === "/api/docs" && req.method === "GET") {
|
|
4854
|
+
const rel = url.searchParams.get("file") ?? "";
|
|
4855
|
+
const abs = safeDocPath(rel);
|
|
4856
|
+
if (!abs)
|
|
4857
|
+
return json({ error: `unknown document ${rel}` }, 404);
|
|
4858
|
+
const raw = readFileSync2(abs, "utf8");
|
|
4859
|
+
ctx.detail = rel;
|
|
4860
|
+
return json({ file: rel, body: parseFrontmatter(raw)?.body ?? raw, rev: fileRev(abs) });
|
|
4861
|
+
}
|
|
3995
4862
|
const createMatch = url.pathname.match(/^\/api\/(tasks|epics|docs)$/);
|
|
3996
4863
|
if (createMatch && req.method === "POST") {
|
|
3997
4864
|
let body;
|