elysia-rack 0.1.1 → 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.
@@ -0,0 +1,65 @@
1
+ // src/rack/registry.ts
2
+ var registrations = new Map;
3
+ function registerRack(reg) {
4
+ registrations.set(reg.path, reg);
5
+ }
6
+ function listRacks() {
7
+ return [...registrations.values()];
8
+ }
9
+ function getRack(id) {
10
+ for (const r of registrations.values())
11
+ if (r.metadata.id === id)
12
+ return r;
13
+ return;
14
+ }
15
+ function sortTreeNodes(list) {
16
+ list.sort((a, b) => {
17
+ const orderA = a.metadata.order ?? 0;
18
+ const orderB = b.metadata.order ?? 0;
19
+ if (orderA !== orderB)
20
+ return orderA - orderB;
21
+ const labelA = a.metadata.pluralLabel ?? a.metadata.label ?? a.metadata.id;
22
+ const labelB = b.metadata.pluralLabel ?? b.metadata.label ?? b.metadata.id;
23
+ return labelA.localeCompare(labelB);
24
+ });
25
+ for (const n of list)
26
+ if (n.children.length)
27
+ sortTreeNodes(n.children);
28
+ }
29
+ function buildRackTree(racks) {
30
+ const nodes = new Map;
31
+ for (const r of racks)
32
+ nodes.set(r.metadata.id, { ...r, children: [] });
33
+ const roots = [];
34
+ for (const node of nodes.values()) {
35
+ const parentId = node.metadata.parent;
36
+ if (parentId && nodes.has(parentId) && parentId !== node.metadata.id) {
37
+ nodes.get(parentId).children.push(node);
38
+ } else {
39
+ roots.push(node);
40
+ }
41
+ }
42
+ sortTreeNodes(roots);
43
+ return roots;
44
+ }
45
+ function getRackTree() {
46
+ return buildRackTree([...registrations.values()]);
47
+ }
48
+ function flatRackTree(tree = getRackTree()) {
49
+ const out = [];
50
+ const walk = (nodes) => {
51
+ for (const n of nodes) {
52
+ const { children, ...rest } = n;
53
+ out.push(rest);
54
+ if (children.length)
55
+ walk(children);
56
+ }
57
+ };
58
+ walk(tree);
59
+ return out;
60
+ }
61
+ function clearRacks() {
62
+ registrations.clear();
63
+ }
64
+
65
+ export { registerRack, listRacks, getRack, buildRackTree, getRackTree, flatRackTree, clearRacks };
@@ -0,0 +1,63 @@
1
+ // src/rack/registry.ts
2
+ var registrations = new Map;
3
+ function registerRack(reg) {
4
+ registrations.set(reg.path, reg);
5
+ }
6
+ function listRacks() {
7
+ return [...registrations.values()];
8
+ }
9
+ function getRack(id) {
10
+ for (const r of registrations.values())
11
+ if (r.metadata.id === id)
12
+ return r;
13
+ return;
14
+ }
15
+ function getRackTree() {
16
+ const nodes = new Map;
17
+ for (const r of registrations.values()) {
18
+ nodes.set(r.metadata.id, { ...r, children: [] });
19
+ }
20
+ const roots = [];
21
+ for (const node of nodes.values()) {
22
+ const parentId = node.metadata.parent;
23
+ if (parentId && nodes.has(parentId) && parentId !== node.metadata.id) {
24
+ nodes.get(parentId).children.push(node);
25
+ } else {
26
+ roots.push(node);
27
+ }
28
+ }
29
+ const sortNodes = (list) => {
30
+ list.sort((a, b) => {
31
+ const orderA = a.metadata.order ?? 0;
32
+ const orderB = b.metadata.order ?? 0;
33
+ if (orderA !== orderB)
34
+ return orderA - orderB;
35
+ const labelA = a.metadata.pluralLabel ?? a.metadata.label ?? a.metadata.id;
36
+ const labelB = b.metadata.pluralLabel ?? b.metadata.label ?? b.metadata.id;
37
+ return labelA.localeCompare(labelB);
38
+ });
39
+ for (const n of list)
40
+ if (n.children.length)
41
+ sortNodes(n.children);
42
+ };
43
+ sortNodes(roots);
44
+ return roots;
45
+ }
46
+ function flatRackTree(tree = getRackTree()) {
47
+ const out = [];
48
+ const walk = (nodes) => {
49
+ for (const n of nodes) {
50
+ const { children, ...rest } = n;
51
+ out.push(rest);
52
+ if (children.length)
53
+ walk(children);
54
+ }
55
+ };
56
+ walk(tree);
57
+ return out;
58
+ }
59
+ function clearRacks() {
60
+ registrations.clear();
61
+ }
62
+
63
+ export { registerRack, listRacks, getRack, getRackTree, flatRackTree, clearRacks };
package/dist/panel.css CHANGED
@@ -1 +1 @@
1
- @import "https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,500;0,700;0,900;1,500&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;1,8..60,400&display=swap";@layer components;@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-5xl:3rem;--text-5xl--line-height:1;--font-weight-normal:400;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-black:900;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--radius-xs:var(--radius-xs);--radius-sm:var(--radius-sm);--radius-md:var(--radius-md);--radius-lg:var(--radius-lg);--radius-xl:var(--radius-xl);--radius-2xl:var(--radius-2xl);--radius-3xl:var(--radius-3xl);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius-full:var(--radius-full);--font-koran:"Playfair Display", Georgia, "Times New Roman", serif;--font-koran-body:"Source Serif 4", Georgia, "Times New Roman", serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}body{background-color:var(--background);color:var(--foreground)}::selection{background-color:var(--selection);color:var(--selection-foreground)}:focus-visible{outline:2px solid var(--focus);outline-offset:2px}}@layer utilities{.visible{visibility:visible}.mx-auto{margin-inline:auto}.mt-1{margin-top:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.contents{display:contents}.flex{display:flex}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.h-4{height:calc(var(--spacing) * 4)}.h-full{height:100%}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.w-4{width:calc(var(--spacing) * 4)}.w-64{width:calc(var(--spacing) * 64)}.w-full{width:100%}.max-w-5xl{max-width:var(--container-5xl)}.min-w-0{min-width:0}.min-w-80{min-width:calc(var(--spacing) * 80)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded-full{border-radius:var(--radius-full)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-border{border-color:var(--border)}.border-input{border-color:var(--input)}.border-sidebar-border{border-color:var(--sidebar-border)}.border-table-border{border-color:var(--table-border)}.bg-background{background-color:var(--background)}.bg-badge-background{background-color:var(--badge-background)}.bg-card{background-color:var(--card)}.bg-destructive{background-color:var(--destructive)}.bg-header{background-color:var(--header)}.bg-input-background{background-color:var(--input-background)}.bg-primary{background-color:var(--primary)}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar)}.bg-sidebar-primary{background-color:var(--sidebar-primary)}.bg-success-muted{background-color:var(--success-muted)}.bg-table{background-color:var(--table-background)}.bg-table-header{background-color:var(--table-header)}.bg-table-row-hover{background-color:var(--table-row-hover)}.bg-warning-muted{background-color:var(--warning-muted)}.p-0{padding:0}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pb-1{padding-bottom:var(--spacing)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-10{padding-bottom:calc(var(--spacing) * 10)}.pl-2{padding-left:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.font-koran{font-family:var(--font-koran)}.font-koran-body{font-family:var(--font-koran-body)}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.whitespace-nowrap{white-space:nowrap}.text-badge-foreground{color:var(--badge-foreground)}.text-card-foreground{color:var(--card-foreground)}.text-destructive{color:var(--destructive)}.text-destructive-foreground{color:var(--destructive-foreground)}.text-foreground{color:var(--foreground)}.text-form-error{color:var(--form-error)}.text-form-label{color:var(--form-label)}.text-header-foreground{color:var(--header-foreground)}.text-link{color:var(--link)}.text-primary-foreground{color:var(--primary-foreground)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground{color:var(--sidebar-foreground)}.text-sidebar-primary-foreground{color:var(--sidebar-primary-foreground)}.text-success-muted-foreground{color:var(--success-muted-foreground)}.text-text-disabled{color:var(--text-disabled)}.text-text-muted{color:var(--text-muted)}.text-warning-muted-foreground{color:var(--warning-muted-foreground)}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.underline{text-decoration-line:underline}.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,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.group-open\:rotate-180:is(:where(.group):is([open],:popover-open,:open) *){rotate:180deg}.placeholder\:text-form-placeholder::placeholder{color:var(--form-placeholder)}@media (hover:hover){.hover\:bg-navigation-hover:hover{background-color:var(--navigation-hover)}.hover\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\:bg-table-row-hover:hover{background-color:var(--table-row-hover)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-link-hover:hover{color:var(--link-hover)}.hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}}@media (width>=40rem){.sm\:inline{display:inline}.sm\:p-6{padding:calc(var(--spacing) * 6)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}}:root{--background:oklch(100% 0 0);--foreground:oklch(14.5% 0 0);--surface:oklch(100% 0 0);--surface-foreground:oklch(14.5% 0 0);--surface-muted:oklch(98.5% 0 0);--surface-muted-foreground:oklch(55.6% 0 0);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--card-muted:oklch(98.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--overlay:oklch(14.5% 0 0/.5);--overlay-foreground:oklch(100% 0 0);--primary:oklch(20.5% 0 0);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(97% 0 0);--secondary-foreground:oklch(20.5% 0 0);--muted:oklch(97% 0 0);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(97% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--destructive-foreground:oklch(98.5% 0 0);--success:oklch(59.6% .145 163.225);--success-foreground:oklch(98.5% 0 0);--success-muted:oklch(96.2% .044 156.743);--success-muted-foreground:oklch(44.8% .119 151.328);--warning:oklch(68.1% .162 75.834);--warning-foreground:oklch(20.5% 0 0);--warning-muted:oklch(98.7% .026 102.212);--warning-muted-foreground:oklch(47.6% .114 61.907);--info:oklch(62.3% .214 259.815);--info-foreground:oklch(98.5% 0 0);--info-muted:oklch(97% .014 254.604);--info-muted-foreground:oklch(48.8% .217 264.376);--border:oklch(92.2% 0 0);--border-muted:oklch(96% 0 0);--input:oklch(92.2% 0 0);--input-background:oklch(100% 0 0);--ring:oklch(70.8% 0 0);--focus:oklch(62.3% .214 259.815);--text:oklch(14.5% 0 0);--text-muted:oklch(55.6% 0 0);--text-subtle:oklch(70.8% 0 0);--text-disabled:oklch(70.8% 0 0);--text-inverse:oklch(98.5% 0 0);--selection:oklch(20.5% 0 0);--selection-foreground:oklch(98.5% 0 0);--link:oklch(48.8% .243 264.376);--link-hover:oklch(42.4% .199 265.638);--link-visited:oklch(48.8% .243 264.376);--table-background:oklch(100% 0 0);--table-header:oklch(98.5% 0 0);--table-row:oklch(100% 0 0);--table-row-hover:oklch(98.5% 0 0);--table-row-selected:oklch(97% 0 0);--table-border:oklch(92.2% 0 0);--form-label:oklch(20.5% 0 0);--form-description:oklch(55.6% 0 0);--form-placeholder:oklch(70.8% 0 0);--form-error:oklch(57.7% .245 27.325);--form-success:oklch(59.6% .145 163.225);--navigation-background:oklch(100% 0 0);--navigation-foreground:oklch(14.5% 0 0);--navigation-hover:oklch(97% 0 0);--navigation-active:oklch(97% 0 0);--navigation-active-foreground:oklch(20.5% 0 0);--navigation-border:oklch(92.2% 0 0);--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(20.5% 0 0);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-muted:oklch(55.6% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0);--header:oklch(100% 0 0);--header-foreground:oklch(14.5% 0 0);--header-border:oklch(92.2% 0 0);--breadcrumb-foreground:oklch(55.6% 0 0);--breadcrumb-current:oklch(14.5% 0 0);--breadcrumb-separator:oklch(70.8% 0 0);--badge-background:oklch(97% 0 0);--badge-foreground:oklch(20.5% 0 0);--code-background:oklch(97% 0 0);--code-foreground:oklch(20.5% 0 0);--chart-1:oklch(64.6% .222 41.116);--chart-2:oklch(60% .118 184.704);--chart-3:oklch(39.8% .07 227.392);--chart-4:oklch(82.8% .189 84.429);--chart-5:oklch(76.9% .188 70.08);--radius:.625rem;--radius-xs:calc(var(--radius) - 6px);--radius-sm:calc(var(--radius) - 4px);--radius-md:calc(var(--radius) - 2px);--radius-lg:var(--radius);--radius-xl:calc(var(--radius) + 4px);--radius-2xl:calc(var(--radius) + 8px);--radius-3xl:calc(var(--radius) + 12px);--radius-full:9999px}.dark{--background:oklch(14.5% 0 0);--foreground:oklch(98.5% 0 0);--surface:oklch(20.5% 0 0);--surface-foreground:oklch(98.5% 0 0);--surface-muted:oklch(18% 0 0);--surface-muted-foreground:oklch(70.8% 0 0);--card:oklch(20.5% 0 0);--card-foreground:oklch(98.5% 0 0);--card-muted:oklch(26.9% 0 0);--popover:oklch(20.5% 0 0);--popover-foreground:oklch(98.5% 0 0);--overlay:oklch(0% 0 0/.65);--overlay-foreground:oklch(98.5% 0 0);--primary:oklch(92.2% 0 0);--primary-foreground:oklch(20.5% 0 0);--secondary:oklch(26.9% 0 0);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(26.9% 0 0);--muted-foreground:oklch(70.8% 0 0);--accent:oklch(26.9% 0 0);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--destructive-foreground:oklch(98.5% 0 0);--success:oklch(69.6% .17 162.48);--success-foreground:oklch(14.5% 0 0);--success-muted:oklch(26.9% .07 156.743);--success-muted-foreground:oklch(76.5% .13 152.461);--warning:oklch(76.9% .188 70.08);--warning-foreground:oklch(20.5% 0 0);--warning-muted:oklch(26.9% .06 85);--warning-muted-foreground:oklch(82.8% .15 84);--info:oklch(70.7% .165 254.624);--info-foreground:oklch(14.5% 0 0);--info-muted:oklch(26.9% .06 255);--info-muted-foreground:oklch(70.7% .165 254.624);--border:oklch(100% 0 0/.1);--border-muted:oklch(100% 0 0/.06);--input:oklch(100% 0 0/.15);--input-background:oklch(20.5% 0 0);--ring:oklch(55.6% 0 0);--focus:oklch(70.7% .165 254.624);--text:oklch(98.5% 0 0);--text-muted:oklch(70.8% 0 0);--text-subtle:oklch(55.6% 0 0);--text-disabled:oklch(55.6% 0 0);--text-inverse:oklch(14.5% 0 0);--selection:oklch(92.2% 0 0);--selection-foreground:oklch(20.5% 0 0);--link:oklch(70.7% .165 254.624);--link-hover:oklch(80.9% .105 251.813);--link-visited:oklch(70.7% .165 254.624);--table-background:oklch(20.5% 0 0);--table-header:oklch(18% 0 0);--table-row:oklch(20.5% 0 0);--table-row-hover:oklch(26.9% 0 0);--table-row-selected:oklch(26.9% 0 0);--table-border:oklch(100% 0 0/.1);--form-label:oklch(98.5% 0 0);--form-description:oklch(70.8% 0 0);--form-placeholder:oklch(55.6% 0 0);--form-error:oklch(70.4% .191 22.216);--form-success:oklch(69.6% .17 162.48);--navigation-background:oklch(20.5% 0 0);--navigation-foreground:oklch(98.5% 0 0);--navigation-hover:oklch(26.9% 0 0);--navigation-active:oklch(26.9% 0 0);--navigation-active-foreground:oklch(98.5% 0 0);--navigation-border:oklch(100% 0 0/.1);--sidebar:oklch(20.5% 0 0);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(48.8% .243 264.376);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(26.9% 0 0);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-muted:oklch(70.8% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.6% 0 0);--header:oklch(20.5% 0 0);--header-foreground:oklch(98.5% 0 0);--header-border:oklch(100% 0 0/.1);--breadcrumb-foreground:oklch(70.8% 0 0);--breadcrumb-current:oklch(98.5% 0 0);--breadcrumb-separator:oklch(55.6% 0 0);--badge-background:oklch(26.9% 0 0);--badge-foreground:oklch(98.5% 0 0);--code-background:oklch(26.9% 0 0);--code-foreground:oklch(98.5% 0 0);--chart-1:oklch(48.8% .243 264.376);--chart-2:oklch(69.6% .17 162.48);--chart-3:oklch(76.9% .188 70.08);--chart-4:oklch(62.7% .265 303.9);--chart-5:oklch(64.5% .246 16.439)}:root{--background:#f5f0e2;--foreground:#1c1610;--surface:#f5f0e2;--surface-foreground:#1c1610;--surface-muted:#ece4d0;--surface-muted-foreground:#6f6250;--card:#faf6ea;--card-foreground:#1c1610;--card-muted:#ece4d0;--popover:#faf6ea;--popover-foreground:#1c1610;--primary:#1c1610;--primary-foreground:#f5f0e2;--secondary:#e9e0c8;--secondary-foreground:#1c1610;--muted:#e9e0c8;--muted-foreground:#6f6250;--accent:#e9e0c8;--accent-foreground:#1c1610;--border:#d8cdae;--border-muted:#e4dabd;--input:#c9bc9c;--input-background:#faf6ea;--ring:#6f6250;--text:#1c1610;--text-muted:#6f6250;--text-subtle:#a2937a;--text-disabled:#a2937a;--text-inverse:#f5f0e2;--selection:#1c1610;--selection-foreground:#f5f0e2;--link:#9b1c1c;--link-hover:#7c1616;--link-visited:#9b1c1c;--table-background:#faf6ea;--table-header:#ece4d0;--table-row:#faf6ea;--table-row-hover:#f1e9d2;--table-row-selected:#e9e0c8;--table-border:#d8cdae;--form-label:#1c1610;--form-description:#6f6250;--form-placeholder:#a2937a;--navigation-background:#f5f0e2;--navigation-foreground:#1c1610;--navigation-hover:#e9e0c8;--navigation-active:#e9e0c8;--navigation-active-foreground:#1c1610;--navigation-border:#d8cdae;--sidebar:#ece4d0;--sidebar-foreground:#1c1610;--sidebar-primary:#1c1610;--sidebar-primary-foreground:#f5f0e2;--sidebar-accent:#e9e0c8;--sidebar-accent-foreground:#1c1610;--sidebar-muted:#6f6250;--sidebar-border:#d8cdae;--sidebar-ring:#6f6250;--header:#f5f0e2;--header-foreground:#1c1610;--header-border:#1c1610;--breadcrumb-foreground:#6f6250;--breadcrumb-current:#1c1610;--breadcrumb-separator:#a2937a;--badge-background:#e9e0c8;--badge-foreground:#1c1610;--code-background:#ece4d0;--code-foreground:#1c1610}.dark{--background:#181310;--foreground:#ece1c8;--surface:#211a13;--surface-foreground:#ece1c8;--surface-muted:#241d15;--surface-muted-foreground:#a49378;--card:#211a13;--card-foreground:#ece1c8;--card-muted:#2a2118;--popover:#211a13;--popover-foreground:#ece1c8;--primary:#ece1c8;--primary-foreground:#181310;--secondary:#2a2118;--secondary-foreground:#ece1c8;--muted:#2a2118;--muted-foreground:#a49378;--accent:#2a2118;--accent-foreground:#ece1c8;--border:#3a2f22;--border-muted:#2a2118;--input:#4a3d2c;--input-background:#211a13;--ring:#a49378;--text:#ece1c8;--text-muted:#a49378;--text-subtle:#6f6250;--text-disabled:#6f6250;--text-inverse:#181310;--selection:#ece1c8;--selection-foreground:#181310;--link:#e06552;--link-hover:#ef8267;--link-visited:#e06552;--table-background:#211a13;--table-header:#181310;--table-row:#211a13;--table-row-hover:#2a2118;--table-row-selected:#2a2118;--table-border:#3a2f22;--form-label:#ece1c8;--form-description:#a49378;--form-placeholder:#6f6250;--navigation-background:#181310;--navigation-foreground:#ece1c8;--navigation-hover:#2a2118;--navigation-active:#2a2118;--navigation-active-foreground:#ece1c8;--navigation-border:#3a2f22;--sidebar:#211a13;--sidebar-foreground:#ece1c8;--sidebar-primary:#ece1c8;--sidebar-primary-foreground:#181310;--sidebar-accent:#2a2118;--sidebar-accent-foreground:#ece1c8;--sidebar-muted:#a49378;--sidebar-border:#3a2f22;--sidebar-ring:#a49378;--header:#181310;--header-foreground:#ece1c8;--header-border:#ece1c8;--breadcrumb-foreground:#a49378;--breadcrumb-current:#ece1c8;--breadcrumb-separator:#6f6250;--badge-background:#2a2118;--badge-foreground:#ece1c8;--code-background:#2a2118;--code-foreground:#ece1c8}dialog{max-width:min(32rem,100vw - 2rem);max-height:calc(100vh - 2rem);margin:auto}dialog::backdrop{background-color:var(--overlay)}.koran-rule-double{border-top:1px solid var(--header-border);position:relative}.koran-rule-double:after{content:"";border-top:3px solid var(--header-border);margin-top:2px;display:block}.koran-paper{background-color:var(--background);background-image:radial-gradient(at top,#ffffff59,#0000 60%)}.dark .koran-paper{background-image:radial-gradient(at top,#ffffff0d,#0000 60%)}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
1
+ @import "https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,500;0,700;0,900;1,500&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;1,8..60,400&display=swap";@layer components;@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-5xl:3rem;--text-5xl--line-height:1;--font-weight-normal:400;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-black:900;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--radius-xs:var(--radius-xs);--radius-sm:var(--radius-sm);--radius-md:var(--radius-md);--radius-lg:var(--radius-lg);--radius-xl:var(--radius-xl);--radius-2xl:var(--radius-2xl);--radius-3xl:var(--radius-3xl);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-primary:var(--primary);--radius-full:var(--radius-full);--font-koran:"Playfair Display", Georgia, "Times New Roman", serif;--font-koran-body:"Source Serif 4", Georgia, "Times New Roman", serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}body{background-color:var(--background);color:var(--foreground)}::selection{background-color:var(--selection);color:var(--selection-foreground)}:focus-visible{outline:2px solid var(--focus);outline-offset:2px}}@layer utilities{.visible{visibility:visible}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.contents{display:contents}.flex{display:flex}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.h-4{height:calc(var(--spacing) * 4)}.h-full{height:100%}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.w-4{width:calc(var(--spacing) * 4)}.w-64{width:calc(var(--spacing) * 64)}.w-full{width:100%}.max-w-5xl{max-width:var(--container-5xl)}.min-w-0{min-width:0}.min-w-80{min-width:calc(var(--spacing) * 80)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded-full{border-radius:var(--radius-full)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-border{border-color:var(--border)}.border-input{border-color:var(--input)}.border-sidebar-border{border-color:var(--sidebar-border)}.border-table-border{border-color:var(--table-border)}.bg-background{background-color:var(--background)}.bg-badge-background{background-color:var(--badge-background)}.bg-card{background-color:var(--card)}.bg-destructive{background-color:var(--destructive)}.bg-header{background-color:var(--header)}.bg-input-background{background-color:var(--input-background)}.bg-primary{background-color:var(--primary)}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar)}.bg-sidebar-primary{background-color:var(--sidebar-primary)}.bg-success-muted{background-color:var(--success-muted)}.bg-table{background-color:var(--table-background)}.bg-table-header{background-color:var(--table-header)}.bg-table-row-hover{background-color:var(--table-row-hover)}.bg-warning-muted{background-color:var(--warning-muted)}.p-0{padding:0}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pb-1{padding-bottom:var(--spacing)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-10{padding-bottom:calc(var(--spacing) * 10)}.pl-2{padding-left:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.font-koran{font-family:var(--font-koran)}.font-koran-body{font-family:var(--font-koran-body)}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.whitespace-nowrap{white-space:nowrap}.text-badge-foreground{color:var(--badge-foreground)}.text-card-foreground{color:var(--card-foreground)}.text-destructive{color:var(--destructive)}.text-destructive-foreground{color:var(--destructive-foreground)}.text-foreground{color:var(--foreground)}.text-form-error{color:var(--form-error)}.text-form-label{color:var(--form-label)}.text-header-foreground{color:var(--header-foreground)}.text-link{color:var(--link)}.text-primary-foreground{color:var(--primary-foreground)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground{color:var(--sidebar-foreground)}.text-sidebar-primary-foreground{color:var(--sidebar-primary-foreground)}.text-success-muted-foreground{color:var(--success-muted-foreground)}.text-text-disabled{color:var(--text-disabled)}.text-text-muted{color:var(--text-muted)}.text-warning-muted-foreground{color:var(--warning-muted-foreground)}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.underline{text-decoration-line:underline}.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,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.group-open\:rotate-180:is(:where(.group):is([open],:popover-open,:open) *){rotate:180deg}.placeholder\:text-form-placeholder::placeholder{color:var(--form-placeholder)}@media (hover:hover){.hover\:bg-navigation-hover:hover{background-color:var(--navigation-hover)}.hover\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\:bg-table-row-hover:hover{background-color:var(--table-row-hover)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-link-hover:hover{color:var(--link-hover)}.hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}}@media (width>=40rem){.sm\:inline{display:inline}.sm\:p-6{padding:calc(var(--spacing) * 6)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}}:root{--background:oklch(100% 0 0);--foreground:oklch(14.5% 0 0);--surface:oklch(100% 0 0);--surface-foreground:oklch(14.5% 0 0);--surface-muted:oklch(98.5% 0 0);--surface-muted-foreground:oklch(55.6% 0 0);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--card-muted:oklch(98.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--overlay:oklch(14.5% 0 0/.5);--overlay-foreground:oklch(100% 0 0);--primary:oklch(20.5% 0 0);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(97% 0 0);--secondary-foreground:oklch(20.5% 0 0);--muted:oklch(97% 0 0);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(97% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--destructive-foreground:oklch(98.5% 0 0);--success:oklch(59.6% .145 163.225);--success-foreground:oklch(98.5% 0 0);--success-muted:oklch(96.2% .044 156.743);--success-muted-foreground:oklch(44.8% .119 151.328);--warning:oklch(68.1% .162 75.834);--warning-foreground:oklch(20.5% 0 0);--warning-muted:oklch(98.7% .026 102.212);--warning-muted-foreground:oklch(47.6% .114 61.907);--info:oklch(62.3% .214 259.815);--info-foreground:oklch(98.5% 0 0);--info-muted:oklch(97% .014 254.604);--info-muted-foreground:oklch(48.8% .217 264.376);--border:oklch(92.2% 0 0);--border-muted:oklch(96% 0 0);--input:oklch(92.2% 0 0);--input-background:oklch(100% 0 0);--ring:oklch(70.8% 0 0);--focus:oklch(62.3% .214 259.815);--text:oklch(14.5% 0 0);--text-muted:oklch(55.6% 0 0);--text-subtle:oklch(70.8% 0 0);--text-disabled:oklch(70.8% 0 0);--text-inverse:oklch(98.5% 0 0);--selection:oklch(20.5% 0 0);--selection-foreground:oklch(98.5% 0 0);--link:oklch(48.8% .243 264.376);--link-hover:oklch(42.4% .199 265.638);--link-visited:oklch(48.8% .243 264.376);--table-background:oklch(100% 0 0);--table-header:oklch(98.5% 0 0);--table-row:oklch(100% 0 0);--table-row-hover:oklch(98.5% 0 0);--table-row-selected:oklch(97% 0 0);--table-border:oklch(92.2% 0 0);--form-label:oklch(20.5% 0 0);--form-description:oklch(55.6% 0 0);--form-placeholder:oklch(70.8% 0 0);--form-error:oklch(57.7% .245 27.325);--form-success:oklch(59.6% .145 163.225);--navigation-background:oklch(100% 0 0);--navigation-foreground:oklch(14.5% 0 0);--navigation-hover:oklch(97% 0 0);--navigation-active:oklch(97% 0 0);--navigation-active-foreground:oklch(20.5% 0 0);--navigation-border:oklch(92.2% 0 0);--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(20.5% 0 0);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-muted:oklch(55.6% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0);--header:oklch(100% 0 0);--header-foreground:oklch(14.5% 0 0);--header-border:oklch(92.2% 0 0);--breadcrumb-foreground:oklch(55.6% 0 0);--breadcrumb-current:oklch(14.5% 0 0);--breadcrumb-separator:oklch(70.8% 0 0);--badge-background:oklch(97% 0 0);--badge-foreground:oklch(20.5% 0 0);--code-background:oklch(97% 0 0);--code-foreground:oklch(20.5% 0 0);--chart-1:oklch(64.6% .222 41.116);--chart-2:oklch(60% .118 184.704);--chart-3:oklch(39.8% .07 227.392);--chart-4:oklch(82.8% .189 84.429);--chart-5:oklch(76.9% .188 70.08);--radius:.625rem;--radius-xs:calc(var(--radius) - 6px);--radius-sm:calc(var(--radius) - 4px);--radius-md:calc(var(--radius) - 2px);--radius-lg:var(--radius);--radius-xl:calc(var(--radius) + 4px);--radius-2xl:calc(var(--radius) + 8px);--radius-3xl:calc(var(--radius) + 12px);--radius-full:9999px}.dark{--background:oklch(14.5% 0 0);--foreground:oklch(98.5% 0 0);--surface:oklch(20.5% 0 0);--surface-foreground:oklch(98.5% 0 0);--surface-muted:oklch(18% 0 0);--surface-muted-foreground:oklch(70.8% 0 0);--card:oklch(20.5% 0 0);--card-foreground:oklch(98.5% 0 0);--card-muted:oklch(26.9% 0 0);--popover:oklch(20.5% 0 0);--popover-foreground:oklch(98.5% 0 0);--overlay:oklch(0% 0 0/.65);--overlay-foreground:oklch(98.5% 0 0);--primary:oklch(92.2% 0 0);--primary-foreground:oklch(20.5% 0 0);--secondary:oklch(26.9% 0 0);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(26.9% 0 0);--muted-foreground:oklch(70.8% 0 0);--accent:oklch(26.9% 0 0);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--destructive-foreground:oklch(98.5% 0 0);--success:oklch(69.6% .17 162.48);--success-foreground:oklch(14.5% 0 0);--success-muted:oklch(26.9% .07 156.743);--success-muted-foreground:oklch(76.5% .13 152.461);--warning:oklch(76.9% .188 70.08);--warning-foreground:oklch(20.5% 0 0);--warning-muted:oklch(26.9% .06 85);--warning-muted-foreground:oklch(82.8% .15 84);--info:oklch(70.7% .165 254.624);--info-foreground:oklch(14.5% 0 0);--info-muted:oklch(26.9% .06 255);--info-muted-foreground:oklch(70.7% .165 254.624);--border:oklch(100% 0 0/.1);--border-muted:oklch(100% 0 0/.06);--input:oklch(100% 0 0/.15);--input-background:oklch(20.5% 0 0);--ring:oklch(55.6% 0 0);--focus:oklch(70.7% .165 254.624);--text:oklch(98.5% 0 0);--text-muted:oklch(70.8% 0 0);--text-subtle:oklch(55.6% 0 0);--text-disabled:oklch(55.6% 0 0);--text-inverse:oklch(14.5% 0 0);--selection:oklch(92.2% 0 0);--selection-foreground:oklch(20.5% 0 0);--link:oklch(70.7% .165 254.624);--link-hover:oklch(80.9% .105 251.813);--link-visited:oklch(70.7% .165 254.624);--table-background:oklch(20.5% 0 0);--table-header:oklch(18% 0 0);--table-row:oklch(20.5% 0 0);--table-row-hover:oklch(26.9% 0 0);--table-row-selected:oklch(26.9% 0 0);--table-border:oklch(100% 0 0/.1);--form-label:oklch(98.5% 0 0);--form-description:oklch(70.8% 0 0);--form-placeholder:oklch(55.6% 0 0);--form-error:oklch(70.4% .191 22.216);--form-success:oklch(69.6% .17 162.48);--navigation-background:oklch(20.5% 0 0);--navigation-foreground:oklch(98.5% 0 0);--navigation-hover:oklch(26.9% 0 0);--navigation-active:oklch(26.9% 0 0);--navigation-active-foreground:oklch(98.5% 0 0);--navigation-border:oklch(100% 0 0/.1);--sidebar:oklch(20.5% 0 0);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(48.8% .243 264.376);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(26.9% 0 0);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-muted:oklch(70.8% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.6% 0 0);--header:oklch(20.5% 0 0);--header-foreground:oklch(98.5% 0 0);--header-border:oklch(100% 0 0/.1);--breadcrumb-foreground:oklch(70.8% 0 0);--breadcrumb-current:oklch(98.5% 0 0);--breadcrumb-separator:oklch(55.6% 0 0);--badge-background:oklch(26.9% 0 0);--badge-foreground:oklch(98.5% 0 0);--code-background:oklch(26.9% 0 0);--code-foreground:oklch(98.5% 0 0);--chart-1:oklch(48.8% .243 264.376);--chart-2:oklch(69.6% .17 162.48);--chart-3:oklch(76.9% .188 70.08);--chart-4:oklch(62.7% .265 303.9);--chart-5:oklch(64.5% .246 16.439)}:root{--background:#f5f0e2;--foreground:#1c1610;--surface:#f5f0e2;--surface-foreground:#1c1610;--surface-muted:#ece4d0;--surface-muted-foreground:#6f6250;--card:#faf6ea;--card-foreground:#1c1610;--card-muted:#ece4d0;--popover:#faf6ea;--popover-foreground:#1c1610;--primary:#1c1610;--primary-foreground:#f5f0e2;--secondary:#e9e0c8;--secondary-foreground:#1c1610;--muted:#e9e0c8;--muted-foreground:#6f6250;--accent:#e9e0c8;--accent-foreground:#1c1610;--border:#d8cdae;--border-muted:#e4dabd;--input:#c9bc9c;--input-background:#faf6ea;--ring:#6f6250;--text:#1c1610;--text-muted:#6f6250;--text-subtle:#a2937a;--text-disabled:#a2937a;--text-inverse:#f5f0e2;--selection:#1c1610;--selection-foreground:#f5f0e2;--link:#9b1c1c;--link-hover:#7c1616;--link-visited:#9b1c1c;--table-background:#faf6ea;--table-header:#ece4d0;--table-row:#faf6ea;--table-row-hover:#f1e9d2;--table-row-selected:#e9e0c8;--table-border:#d8cdae;--form-label:#1c1610;--form-description:#6f6250;--form-placeholder:#a2937a;--navigation-background:#f5f0e2;--navigation-foreground:#1c1610;--navigation-hover:#e9e0c8;--navigation-active:#e9e0c8;--navigation-active-foreground:#1c1610;--navigation-border:#d8cdae;--sidebar:#ece4d0;--sidebar-foreground:#1c1610;--sidebar-primary:#1c1610;--sidebar-primary-foreground:#f5f0e2;--sidebar-accent:#e9e0c8;--sidebar-accent-foreground:#1c1610;--sidebar-muted:#6f6250;--sidebar-border:#d8cdae;--sidebar-ring:#6f6250;--header:#f5f0e2;--header-foreground:#1c1610;--header-border:#1c1610;--breadcrumb-foreground:#6f6250;--breadcrumb-current:#1c1610;--breadcrumb-separator:#a2937a;--badge-background:#e9e0c8;--badge-foreground:#1c1610;--code-background:#ece4d0;--code-foreground:#1c1610}.dark{--background:#181310;--foreground:#ece1c8;--surface:#211a13;--surface-foreground:#ece1c8;--surface-muted:#241d15;--surface-muted-foreground:#a49378;--card:#211a13;--card-foreground:#ece1c8;--card-muted:#2a2118;--popover:#211a13;--popover-foreground:#ece1c8;--primary:#ece1c8;--primary-foreground:#181310;--secondary:#2a2118;--secondary-foreground:#ece1c8;--muted:#2a2118;--muted-foreground:#a49378;--accent:#2a2118;--accent-foreground:#ece1c8;--border:#3a2f22;--border-muted:#2a2118;--input:#4a3d2c;--input-background:#211a13;--ring:#a49378;--text:#ece1c8;--text-muted:#a49378;--text-subtle:#6f6250;--text-disabled:#6f6250;--text-inverse:#181310;--selection:#ece1c8;--selection-foreground:#181310;--link:#e06552;--link-hover:#ef8267;--link-visited:#e06552;--table-background:#211a13;--table-header:#181310;--table-row:#211a13;--table-row-hover:#2a2118;--table-row-selected:#2a2118;--table-border:#3a2f22;--form-label:#ece1c8;--form-description:#a49378;--form-placeholder:#6f6250;--navigation-background:#181310;--navigation-foreground:#ece1c8;--navigation-hover:#2a2118;--navigation-active:#2a2118;--navigation-active-foreground:#ece1c8;--navigation-border:#3a2f22;--sidebar:#211a13;--sidebar-foreground:#ece1c8;--sidebar-primary:#ece1c8;--sidebar-primary-foreground:#181310;--sidebar-accent:#2a2118;--sidebar-accent-foreground:#ece1c8;--sidebar-muted:#a49378;--sidebar-border:#3a2f22;--sidebar-ring:#a49378;--header:#181310;--header-foreground:#ece1c8;--header-border:#ece1c8;--breadcrumb-foreground:#a49378;--breadcrumb-current:#ece1c8;--breadcrumb-separator:#6f6250;--badge-background:#2a2118;--badge-foreground:#ece1c8;--code-background:#2a2118;--code-foreground:#ece1c8}dialog{max-width:min(32rem,100vw - 2rem);max-height:calc(100vh - 2rem);margin:auto}dialog::backdrop{background-color:var(--overlay)}.koran-rule-double{border-top:1px solid var(--header-border);position:relative}.koran-rule-double:after{content:"";border-top:3px solid var(--header-border);margin-top:2px;display:block}.koran-paper{background-color:var(--background);background-image:radial-gradient(at top,#ffffff59,#0000 60%)}.dark .koran-paper{background-image:radial-gradient(at top,#ffffff0d,#0000 60%)}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
@@ -0,0 +1,71 @@
1
+ import Elysia from "elysia";
2
+ export interface DashboardOptions {
3
+ /** Mount path for the dashboard page (default "/") */
4
+ path?: string;
5
+ /** Dashboard title (default "Panel") */
6
+ title?: string;
7
+ /** React page registry key (default "/dashboard") */
8
+ pagePath?: string;
9
+ }
10
+ /**
11
+ * Dashboard plugin — mounts `GET {path}` that renders
12
+ * `page("/dashboard")` via `reactPlugin`.
13
+ *
14
+ * Tree metadata is stored in memory by `rack()` (registry)
15
+ * and loaded when Dashboard renders via `getRackTree()`.
16
+ *
17
+ * Recommended setup:
18
+ * ```ts
19
+ * const app = new Elysia()
20
+ * .use(reactPlugin({ pages }))
21
+ * .use(dashboard({ title: "My Panel" }))
22
+ * .use(rack("/catalog/products", { model, metadata: { id: "products", group: "Catalog" } }))
23
+ * ```
24
+ */
25
+ export declare function dashboard(options?: DashboardOptions): Elysia<"", {
26
+ decorator: {};
27
+ store: {};
28
+ derive: {};
29
+ resolve: {};
30
+ }, {
31
+ typebox: {};
32
+ error: {};
33
+ }, {
34
+ schema: {};
35
+ standaloneSchema: {};
36
+ macro: {};
37
+ macroFn: {};
38
+ parser: {};
39
+ response: {};
40
+ }, {
41
+ [x: string]: {
42
+ get: {
43
+ body: unknown;
44
+ params: {};
45
+ query: unknown;
46
+ headers: unknown;
47
+ response: {
48
+ 200: {
49
+ [x: symbol]: true;
50
+ path: string;
51
+ props: {
52
+ resource: string | undefined;
53
+ name?: string | undefined;
54
+ };
55
+ };
56
+ };
57
+ };
58
+ };
59
+ }, {
60
+ derive: {};
61
+ resolve: {};
62
+ schema: {};
63
+ standaloneSchema: {};
64
+ response: {};
65
+ }, {
66
+ derive: {};
67
+ resolve: {};
68
+ schema: {};
69
+ standaloneSchema: {};
70
+ response: {};
71
+ }>;
@@ -2,7 +2,8 @@ import Elysia from "elysia";
2
2
  import type { Rack } from "./types";
3
3
  export type { Rack } from "./types";
4
4
  export type * from "./models";
5
- export { clearRacks, listRacks, registerRack, type RackRegistration, } from "./registry";
5
+ export { buildRackTree, clearRacks, flatRackTree, getRack, getRackTree, listRacks, registerRack, type RackRegistration, type RackTreeNode, } from "./registry";
6
+ export { dashboard, type DashboardOptions } from "./dashboard";
6
7
  export { resolveAdapter } from "./adapter";
7
8
  export { getAdapter, drizzleAdapter, prismaAdapter, type AdapterOptions, type CrudAdapter, type FieldDescriptor, type FieldKind, type ListResult, } from "./adapters/index";
8
9
  export { authorize, type AuthCheck } from "./authorization";
@@ -1,12 +1,16 @@
1
1
  import {
2
+ buildRackTree,
2
3
  clearRacks,
4
+ flatRackTree,
5
+ getRack,
6
+ getRackTree,
3
7
  listRacks,
4
8
  registerRack
5
- } from "../index-zc7rgvmm.js";
9
+ } from "../index-4wxvmd8a.js";
6
10
  import"../index-37x76zdn.js";
7
11
 
8
12
  // src/rack/index.ts
9
- import Elysia, { status as status2 } from "elysia";
13
+ import Elysia2, { status as status2 } from "elysia";
10
14
 
11
15
  // src/rack/adapter.ts
12
16
  function resolveAdapter(model) {
@@ -1122,6 +1126,21 @@ function parseListQuery(raw, queryOptions) {
1122
1126
  }
1123
1127
  return { search, filters, sort, page, limit };
1124
1128
  }
1129
+ // src/rack/dashboard.ts
1130
+ import Elysia from "elysia";
1131
+ function dashboard(options) {
1132
+ const path = options?.path ?? "/";
1133
+ const pagePath = options?.pagePath ?? "/dashboard";
1134
+ const title = options?.title;
1135
+ return new Elysia({ name: "rack:dashboard" }).get(path, ({ query }) => ({
1136
+ [Symbol.for(PANEL_PAGE_KEY)]: true,
1137
+ path: pagePath,
1138
+ props: {
1139
+ ...title !== undefined ? { name: title } : {},
1140
+ resource: typeof query.resource === "string" ? query.resource : undefined
1141
+ }
1142
+ }));
1143
+ }
1125
1144
 
1126
1145
  // src/rack/index.ts
1127
1146
  var DEFAULT_OPERATIONS = {
@@ -1150,7 +1169,7 @@ function rack(path, options) {
1150
1169
  returning: options.settings?.returning,
1151
1170
  softDelete: options.settings?.softDelete
1152
1171
  };
1153
- const app = new Elysia({ name: `rack:${path}`, prefix: path });
1172
+ const app = new Elysia2({ name: `rack:${path}`, prefix: path });
1154
1173
  const notFound = (operation, id) => status2(404, { error: "Not Found", operation, id });
1155
1174
  const envelope = (operation) => ({
1156
1175
  resource,
@@ -1352,11 +1371,16 @@ export {
1352
1371
  parseListQuery,
1353
1372
  panelPage,
1354
1373
  listRacks,
1374
+ getRackTree,
1375
+ getRack,
1355
1376
  getAdapter,
1377
+ flatRackTree,
1356
1378
  drizzleAdapter,
1357
1379
  detailFor,
1380
+ dashboard,
1358
1381
  createMemoryIdempotencyStore,
1359
1382
  clearRacks,
1383
+ buildRackTree,
1360
1384
  authorize,
1361
1385
  PANEL_PAGE_KEY
1362
1386
  };
@@ -9,6 +9,10 @@ export interface RackRegistration {
9
9
  /** Resolved operations (all default `true`). */
10
10
  operations: Required<Rack.CrudOperationsOptions>;
11
11
  }
12
+ /** Tree node for sidebar/dashboard. Children are resources where `metadata.parent === node.metadata.id`. */
13
+ export interface RackTreeNode extends RackRegistration {
14
+ children: RackTreeNode[];
15
+ }
12
16
  /**
13
17
  * Register a rack resource. Called automatically by `rack()`.
14
18
  * Re-registering the same path replaces the previous entry.
@@ -16,5 +20,17 @@ export interface RackRegistration {
16
20
  export declare function registerRack(reg: RackRegistration): void;
17
21
  /** List all registered rack resources in registration order. */
18
22
  export declare function listRacks(): RackRegistration[];
23
+ /** Get a single registration by `metadata.id`. */
24
+ export declare function getRack(id: string): RackRegistration | undefined;
25
+ /** Build a tree from an explicit list (pure, reusable for props.racks). */
26
+ export declare function buildRackTree(racks: RackRegistration[]): RackTreeNode[];
27
+ /**
28
+ * Build a hierarchical tree from `parent` metadata.
29
+ * - Nodes without `parent` or with missing parent become roots.
30
+ * - Children are sorted by `order` then label.
31
+ */
32
+ export declare function getRackTree(): RackTreeNode[];
33
+ /** Flatten tree into a depth-first ordered list (useful for legacy dashboard). */
34
+ export declare function flatRackTree(tree?: RackTreeNode[]): RackRegistration[];
19
35
  /** Clear the registry (mainly for tests). */
20
36
  export declare function clearRacks(): void;
@@ -33,7 +33,7 @@ export declare function reactPlugin(options: ReactRack.ReactPluginOptions): Elys
33
33
  headers: unknown;
34
34
  response: {
35
35
  200: Response;
36
- 404: "Panel asset not built. Run `bun run build:assets`." | "Panel asset not found";
36
+ 404: "Panel asset not found";
37
37
  422: {
38
38
  type: "validation";
39
39
  on: string;
@@ -4,18 +4,66 @@ import {
4
4
 
5
5
  // src/react/index.ts
6
6
  import Elysia from "elysia";
7
+ import { existsSync as existsSync2 } from "node:fs";
8
+ import { readFile as readFile2 } from "node:fs/promises";
9
+ import { join as join2 } from "node:path";
10
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
11
+
12
+ // src/react/render.tsx
7
13
  import { existsSync } from "node:fs";
8
14
  import { readFile } from "node:fs/promises";
9
15
  import { join } from "node:path";
10
16
  import { fileURLToPath } from "node:url";
11
-
12
- // src/react/render.tsx
13
17
  import { renderToReadableStream } from "react-dom/server";
14
18
  import { jsxDEV } from "react/jsx-dev-runtime";
15
19
  function pageTitle(path, props) {
16
20
  const metadata = props?.metadata;
17
21
  return metadata?.pluralLabel ?? metadata?.label ?? path;
18
22
  }
23
+ function escapeHtml(value) {
24
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
25
+ }
26
+ var FALLBACK_TEMPLATE = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>{{title}}</title><link rel="stylesheet" href="/__rack/panel.css"><script type="module" src="/__rack/panel-theme.js"></script><!--app-head--></head><body><!--app--></body></html>`;
27
+ var cachedTemplate = null;
28
+ async function loadTemplate() {
29
+ if (cachedTemplate !== null)
30
+ return cachedTemplate;
31
+ const candidates = [
32
+ join(fileURLToPath(new URL(".", import.meta.url)), "app.html"),
33
+ join(fileURLToPath(new URL(".", import.meta.url)), "..", "app.html"),
34
+ join(process.cwd(), "dist", "app.html"),
35
+ join(process.cwd(), "src", "react", "app.html")
36
+ ];
37
+ for (const dir of [join(process.cwd(), "dist"), join(fileURLToPath(new URL(".", import.meta.url)), "..")]) {
38
+ const p = join(dir, "app.html");
39
+ if (!candidates.includes(p))
40
+ candidates.push(p);
41
+ }
42
+ for (const p of candidates) {
43
+ try {
44
+ if (!existsSync(p))
45
+ continue;
46
+ cachedTemplate = await readFile(p, "utf-8");
47
+ return cachedTemplate;
48
+ } catch {}
49
+ }
50
+ cachedTemplate = FALLBACK_TEMPLATE;
51
+ return cachedTemplate;
52
+ }
53
+ function buildShell(template, title) {
54
+ const safeTitle = escapeHtml(title);
55
+ const html = template.replaceAll("{{title}}", safeTitle);
56
+ for (const marker of ["<!--app-->", "{{body}}", "<!--app-html-->"]) {
57
+ const idx = html.indexOf(marker);
58
+ if (idx !== -1) {
59
+ return { head: html.slice(0, idx), tail: html.slice(idx + marker.length) };
60
+ }
61
+ }
62
+ const bodyClose = html.lastIndexOf("</body>");
63
+ if (bodyClose !== -1)
64
+ return { head: html.slice(0, bodyClose), tail: html.slice(bodyClose) };
65
+ return { head: html, tail: "" };
66
+ }
19
67
  function createRenderer(pages) {
20
68
  return async function render(path, props) {
21
69
  const loader = pages[path];
@@ -29,7 +77,8 @@ function createRenderer(pages) {
29
77
  const stream = await renderToReadableStream(/* @__PURE__ */ jsxDEV(Component, {
30
78
  ...props
31
79
  }, undefined, false, undefined, this));
32
- const head = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${pageTitle(path, props)}</title><link rel="stylesheet" href="/__rack/panel.css"><script type="module" src="/__rack/panel-theme.js"></script></head><body>`;
80
+ const template = await loadTemplate();
81
+ const { head, tail } = buildShell(template, pageTitle(path, props));
33
82
  const encoder = new TextEncoder;
34
83
  const combined = new ReadableStream({
35
84
  async start(controller) {
@@ -45,7 +94,7 @@ function createRenderer(pages) {
45
94
  } finally {
46
95
  reader.releaseLock();
47
96
  }
48
- controller.enqueue(encoder.encode("</body></html>"));
97
+ controller.enqueue(encoder.encode(tail));
49
98
  controller.close();
50
99
  }
51
100
  });
@@ -70,18 +119,31 @@ var PANEL_ASSETS = {
70
119
  "panel-app.js": "text/javascript; charset=utf-8"
71
120
  };
72
121
  function resolveDistDir() {
73
- const candidates = [
74
- join(process.cwd(), "dist"),
75
- join(fileURLToPath(new URL(".", import.meta.url)), "..")
76
- ];
122
+ const candidates = [join2(process.cwd(), "dist"), join2(fileURLToPath2(new URL(".", import.meta.url)), "..")];
77
123
  for (const dir of candidates) {
78
124
  try {
79
- if (existsSync(join(dir, "panel-app.js")))
125
+ if (existsSync2(join2(dir, "panel-app.js")))
80
126
  return dir;
81
127
  } catch {}
82
128
  }
83
129
  return null;
84
130
  }
131
+ function pickPanelCssOption(options) {
132
+ return options.css ?? options.panelCss;
133
+ }
134
+ function resolveCssFilePath(raw) {
135
+ const candidates = [raw, join2(process.cwd(), raw)];
136
+ const dist = resolveDistDir();
137
+ if (dist)
138
+ candidates.push(join2(dist, raw));
139
+ for (const p of candidates) {
140
+ try {
141
+ if (existsSync2(p))
142
+ return p;
143
+ } catch {}
144
+ }
145
+ return null;
146
+ }
85
147
  function page(path, props) {
86
148
  return {
87
149
  [ReactRack.PAGE]: true,
@@ -90,18 +152,76 @@ function page(path, props) {
90
152
  };
91
153
  }
92
154
  var pages = {
93
- "/dashboard": () => import("../Dashboard-xpstr7fr.js"),
155
+ "/dashboard": () => import("../Dashboard-2j561p7c.js"),
94
156
  "/panel": () => import("../Panel-fbp3wrsw.js")
95
157
  };
96
158
  function reactPlugin(options) {
97
159
  const render = createRenderer(options.pages);
160
+ const panelCssOverride = pickPanelCssOption(options);
161
+ let cachedCustomCss = null;
162
+ let cachedCustomCssLoaded = false;
163
+ async function loadCustomPanelCss() {
164
+ if (cachedCustomCssLoaded)
165
+ return cachedCustomCss;
166
+ if (!panelCssOverride) {
167
+ cachedCustomCssLoaded = true;
168
+ return cachedCustomCss = null;
169
+ }
170
+ if (typeof panelCssOverride === "object" && panelCssOverride.content !== undefined) {
171
+ cachedCustomCssLoaded = true;
172
+ return cachedCustomCss = panelCssOverride.content;
173
+ }
174
+ if (typeof panelCssOverride === "object" && panelCssOverride.path) {
175
+ const p = resolveCssFilePath(panelCssOverride.path);
176
+ if (p) {
177
+ try {
178
+ cachedCustomCssLoaded = true;
179
+ return cachedCustomCss = await readFile2(p);
180
+ } catch {}
181
+ }
182
+ cachedCustomCssLoaded = true;
183
+ return cachedCustomCss = null;
184
+ }
185
+ if (typeof panelCssOverride === "string") {
186
+ const filePath = resolveCssFilePath(panelCssOverride);
187
+ if (filePath) {
188
+ try {
189
+ cachedCustomCssLoaded = true;
190
+ return cachedCustomCss = await readFile2(filePath);
191
+ } catch {}
192
+ }
193
+ const looksLikeCss = panelCssOverride.includes("{") || panelCssOverride.includes(`
194
+ `);
195
+ if (looksLikeCss) {
196
+ cachedCustomCssLoaded = true;
197
+ return cachedCustomCss = panelCssOverride;
198
+ }
199
+ cachedCustomCssLoaded = true;
200
+ return cachedCustomCss = null;
201
+ }
202
+ cachedCustomCssLoaded = true;
203
+ return cachedCustomCss = null;
204
+ }
98
205
  return new Elysia({ name: "@elysia-rack/react" }).decorate("react", { render }).get("/__rack/:name", async ({ params, status }) => {
99
206
  const type = PANEL_ASSETS[params.name];
207
+ if (!type)
208
+ return status(404, "Panel asset not found");
209
+ if (params.name === "panel.css") {
210
+ const custom = await loadCustomPanelCss();
211
+ if (custom !== null) {
212
+ return new Response(custom, {
213
+ headers: {
214
+ "Content-Type": type,
215
+ "Cache-Control": "public, max-age=3600"
216
+ }
217
+ });
218
+ }
219
+ }
100
220
  const dir = resolveDistDir();
101
- if (!type || !dir)
102
- return status(404, "Panel asset not built. Run `bun run build:assets`.");
221
+ if (!dir)
222
+ return status(404, "Panel asset not found");
103
223
  try {
104
- const data = await readFile(join(dir, params.name));
224
+ const data = await readFile2(join2(dir, params.name));
105
225
  return new Response(data, {
106
226
  headers: {
107
227
  "Content-Type": type,
@@ -4,6 +4,7 @@ export interface SidebarItem {
4
4
  icon?: string;
5
5
  href: string;
6
6
  active?: boolean;
7
+ children?: SidebarItem[];
7
8
  }
8
9
  export interface SidebarGroup {
9
10
  name: string;