|
|
|
@ -1,52 +0,0 @@ |
|
|
|
{ |
|
|
|
|
|
|
|
"files": [ |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/theme/build-tokens.ts", |
|
|
|
|
|
|
|
"content": "#!/usr/bin/env -S deno run --allow-read --allow-write\nimport { join } from \"https://deno.land/std@0.224.0/path/mod.ts\";\nimport { parse as parseYaml } from \"https://deno.land/std@0.224.0/yaml/mod.ts\";\nconst themesDir = \"src/lib/theme/themes\";\nconst outCss = \"src/lib/theme/generated/themes.css\";\nfunction toRgb(hex: string) {\n const h = hex.replace(\"#\", \"\").trim();\n const n = (s: string) => parseInt(s, 16);\n if (h.length === 3) return `${n(h[0]+h[0])} ${n(h[1]+h[1])} ${n(h[2]+h[2])}`;\n return `${n(h.slice(0,2))} ${n(h.slice(2,4))} ${n(h.slice(4,6))}`;\n}\nconst entries: string[] = [];\nfor await (const ent of Deno.readDir(themesDir)) if (ent.isFile && ent.name.endsWith(\".yaml\")) entries.push(ent.name);\nentries.sort();\nlet css = \"\";\nfor (const file of entries) {\n const t = parseYaml(await Deno.readTextFile(join(themesDir, file))) as any;\n const sel = t.name === \"light\" ? ':root,[data-theme=\"light\"]' : `[data-theme=\"${t.name}\"]`;\n css += `${sel}{\\n`;\n for (const [k, v] of Object.entries(t.colors ?? {})) css += `--color-${k}: ${toRgb(String(v))};\\n`;\n for (const [k, v] of Object.entries(t.radii ?? {})) css += `--radius-${k}: ${v};\\n`;\n for (const [k, v] of Object.entries(t.spacing ?? {})) css += `--space-${k}: ${v};\\n`;\n const ty = t.typography ?? {};\n if (ty[\"font-reading\"]) css += `--font-reading: ${ty[\"font-reading\"]};\\n`;\n if (ty[\"font-ui\"]) css += `--font-ui: ${ty[\"font-ui\"]};\\n`;\n if (ty[\"leading-reading\"]) css += `--leading-reading: ${ty[\"leading-reading\"]};\\n`;\n if (ty[\"measure-ch\"]) css += `--measure-ch: ${ty[\"measure-ch\"]};\\n`;\n css += `}\\n\\n`;\n}\nawait Deno.mkdir(join(\"src/lib/theme/generated\"), { recursive: true });\nawait Deno.writeTextFile(outCss, css);\nconsole.log(\"Wrote\", outCss);\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "deno.json", |
|
|
|
|
|
|
|
"content": "{\n \"tasks\": {\n \"tokens\": \"deno run --allow-read --allow-write src/lib/theme/build-tokens.ts\",\n \"scaffold\": \"deno run --allow-read --allow-write scripts/scaffold.ts scripts/a-ui.v5.manifest.json\"\n }\n}\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/a/primitives/AButton.svelte", |
|
|
|
|
|
|
|
"content": "<script lang=\"ts\">\n import { cva, twMerge } from '$lib/styles/cva';\n let {\n variant = 'solid',\n size = 'md',\n as = 'button',\n disabled = false,\n class: className = '',\n // common attrs (no $$restProps in runes)\n href = undefined as string | undefined,\n target = undefined as string | undefined,\n rel = undefined as string | undefined,\n type = 'button',\n onclick = undefined as undefined | ((e:MouseEvent)=>void)\n } = $props();\n\n const styles = cva(\n 'inline-flex items-center justify-center font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-primary/40 transition',\n { variants: { variant:\n { solid: 'bg-primary text-[rgb(var(--color-primary-contrast,255 255 255))] hover:bg-primary/90',\n outline: 'border border-primary text-primary hover:bg-primary/10',\n ghost: 'text-primary hover:bg-primary/10' },\n size: { sm: 'h-8 px-3 text-sm', md: 'h-10 px-4', lg: 'h-12 px-5 text-lg' } },\n defaultVariants: { variant: 'solid', size: 'md' } }\n );\n</script>\n\n<svelte:element\n this={as}\n class={twMerge(styles({ variant, size }), className)}\n disabled={as === 'button' ? disabled : undefined}\n href={as === 'a' ? href : undefined}\n target={as === 'a' ? target : undefined}\n rel={as === 'a' ? rel : undefined}\n type={as === 'button' ? type : undefined}\n onclick={onclick}\n>\n <slot />\n</svelte:element>\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/a/primitives/AInput.svelte", |
|
|
|
|
|
|
|
"content": "<script lang=\"ts\"> let { value = '', class: className = '', placeholder = '' } = $props(); </script>\n<input\n class={`w-full h-10 px-3 rounded-md border border-muted/30 bg-surface text-text placeholder:opacity-60 focus:outline-none focus:ring-2 focus:ring-primary/40 ${className}`}\n bind:value\n placeholder={placeholder}\n/>\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/a/primitives/ACard.svelte", |
|
|
|
|
|
|
|
"content": "<script lang=\"ts\"> let { class: className = '' } = $props(); </script>\n<div class={`rounded-lg border border-muted/20 bg-surface shadow-sm ${className}`}>\n <slot name=\"header\" />\n <div class=\"p-4\"><slot /></div>\n <slot name=\"footer\" />\n</div>\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/a/primitives/ASwitch.svelte", |
|
|
|
|
|
|
|
"content": "<script lang=\"ts\">\n let { checked = false, class: className = '', onchange = undefined as undefined | ((v:boolean)=>void) } = $props();\n function toggle(){ checked = !checked; onchange?.(checked); }\n</script>\n<button type=\"button\" role=\"switch\" aria-checked={checked}\n onclick={toggle}\n class={`inline-flex items-center h-6 w-11 rounded-full border border-muted/30 transition px-0.5 ${checked ? 'bg-primary' : 'bg-surface'} ${className}`}\n>\n <span class={`inline-block h-5 w-5 rounded-full bg-white shadow transform transition ${checked ? 'translate-x-5' : 'translate-x-0'}`} />\n</button>\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/a/primitives/ADetails.svelte", |
|
|
|
|
|
|
|
"content": "<script lang=\"ts\">\n import { showTech } from '$lib/tech/tech-store';\n let { summary = '', tech = false, defaultOpen = false, forceHide = false, class: className = '' } = $props();\n let open = defaultOpen;\n $: if (tech && !$showTech) open = false;\n function onToggle(e: Event){ const el = e.currentTarget as HTMLDetailsElement; open = el.open; }\n</script>\n<details open={open} ontoggle={onToggle} class={`group rounded-lg border border-muted/20 bg-surface ${className}`} data-kind={tech ? 'tech':'general'}>\n <summary class=\"flex items-center gap-2 cursor-pointer list-none px-3 py-2 rounded-lg select-none hover:bg-primary/10\">\n <svg class={`h-4 w-4 transition-transform ${open ? 'rotate-90':''}`} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 18l6-6-6-6\"/></svg>\n <span class=\"font-medium\">{summary}</span>\n {#if tech}<span class=\"ml-2 text-[10px] uppercase tracking-wide rounded px-1.5 py-0.5 border border-primary/30 text-primary bg-primary/5\">Technical</span>{/if}\n <span class=\"ml-auto text-xs opacity-60 group-open:opacity-50\">{open ? 'Hide':'Show'}</span>\n </summary>\n {#if !(tech && !$showTech && forceHide)}<div class=\"px-3 pb-3 pt-1 text-[0.95rem] leading-6\"><slot /></div>{/if}\n</details>\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/a/reader/AReaderTOC.svelte", |
|
|
|
|
|
|
|
"content": "<script lang=\"ts\">\n import { onMount } from 'svelte';\n import TocNode from './TocNode.svelte';\n import type { TocItem } from './toc-utils';\n let { items = [] as TocItem[], activeId = null as string | null, collapsible = true, expandDepth = 1, class: className = '', onnavigate = undefined as undefined | ((href:string)=>void) } = $props();\n let expanded = new Set<string>();\n let parentOf = new Map<string, string | null>();\n function mapParents(list: TocItem[], parent: string | null = null){ for (const it of list){ parentOf.set(it.id, parent); if (it.children?.length) mapParents(it.children, it.id); } }\n $: mapParents(items);\n function initExpansion(list: TocItem[], depth = 0){ for (const it of list){ if (depth < expandDepth) expanded.add(it.id); if (it.children?.length) initExpansion(it.children, depth + 1); } expanded = new Set(expanded); }\n function expandAncestors(id: string | null){ if (!id) return; let cur: string | null | undefined = id; while (cur){ expanded.add(cur); cur = parentOf.get(cur) ?? null; } expanded = new Set(expanded); }\n onMount(()=>{ initExpansion(items,0); expandAncestors(activeId); if (activeId){ const el = document.querySelector(`[data-toc-id=\"${activeId}\"]`); if (el instanceof HTMLElement) el.scrollIntoView({ block:'nearest' }); } });\n $: if (activeId) expandAncestors(activeId);\n function toggle(id: string){ if (!collapsible) return; if (expanded.has(id)) expanded.delete(id); else expanded.add(id); expanded = new Set(expanded); }\n const pad = (depth:number)=>`padding-left: calc(var(--space-4) * ${Math.max(depth,0)})`;\n const onNavigate = (href:string)=> onnavigate?.(href);\n</script>\n<nav aria-label=\"Table of contents\" class={`text-sm ${className}`}>\n <ul class=\"space-y-1 max-h-[calc(100vh-6rem)] overflow-auto pr-1\">\n {#each items as item (item.id)}\n <TocNode {item} depth={0} {activeId} {collapsible} {expanded} {toggle} {pad} {onNavigate} />\n {/each}\n </ul>\n</nav>\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/a/reader/TocNode.svelte", |
|
|
|
|
|
|
|
"content": "<script lang=\"ts\">\n import type { TocItem } from './toc-utils';\n import TocNode from './TocNode.svelte';\n let { item, depth = 0, activeId = null as string | null, collapsible = true, expanded, toggle, pad, onNavigate }: { item: TocItem; depth?: number; activeId?: string|null; collapsible?: boolean; expanded: Set<string>; toggle: (id:string)=>void; pad: (depth:number)=>string; onNavigate: (href:string)=>void; } = $props();\n const hasChildren = !!(item.children && item.children.length > 0);\n $: isOpen = expanded.has(item.id);\n $: isActive = activeId === item.id;\n</script>\n<li>\n <div class=\"flex items-center gap-1 rounded-md hover:bg-primary/10\" style={pad(depth)}>\n {#if collapsible && hasChildren}\n <button class=\"shrink-0 h-6 w-6 grid place-items-center rounded-md hover:bg-primary/10\" aria-label={isOpen?`Collapse ${item.title}`:`Expand ${item.title}`} aria-expanded={isOpen} onclick={() => toggle(item.id)}>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class={isOpen ? 'rotate-90 transition-transform' : 'transition-transform'}><path d=\"M9 18l6-6-6-6\" /></svg>\n </button>\n {:else}\n <span class=\"shrink-0 h-6 w-6\" />\n {/if}\n <a href={item.href ?? `#${item.id}`} data-toc-id={item.id}\n class=\"flex-1 min-w-0 rounded-md px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-primary/40\"\n class:hover=\"bg-primary/10\" class:text-primary={isActive} class:bg-primary/10={isActive}\n onclick={() => onNavigate(item.href ?? `#${item.id}`)}>\n <span class=\"truncate\">{item.title}</span>\n </a>\n </div>\n {#if hasChildren}\n <ul id={`sub-${item.id}`} aria-hidden={!isOpen} class={isOpen ? 'mt-1 space-y-1' : 'hidden'}>\n {#each item.children as child (child.id)}\n <TocNode item={child} depth={depth + 1} {activeId} {collapsible} {expanded} {toggle} {pad} {onNavigate} />\n {/each}\n </ul>\n {/if}\n</li>\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/a/nav/ANavbar.svelte", |
|
|
|
|
|
|
|
"content": "<script lang=\"ts\">\n import type { NavItem, UserInfo } from './nav-types';\n import UserDropdown from './UserDropdown.svelte';\n import MobileNav from './MobileNav.svelte';\n import { onMount } from 'svelte';\n let { items = [] as NavItem[], currentPath = '', user = null as UserInfo | null, userMenu = [] as NavItem[], logo = null, brand = 'Alexandria', onselect = undefined as undefined | ((i:NavItem)=>void) } = $props();\n let mobileOpen = false; let openId: string | null = null;\n function onKey(e: KeyboardEvent){ if (e.key === 'Escape'){ openId = null; mobileOpen = false; } }\n onMount(()=>{ window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); });\n const topItems = items.map((it,i)=>({ ...it, id: it.id || `${(it.title||'item').toLowerCase().replace(/[^\\w]+/g,'-')}-${i}` }));\n function isActive(href?: string){ if (!href) return false; try { return new URL(href, 'http://x').pathname === currentPath; } catch { return href === currentPath; } }\n</script>\n<header class=\"sticky top-0 z-40 border-b border-muted/20 bg-surface/95 backdrop-blur supports-[backdrop-filter]:bg-surface/80\">\n <div class=\"mx-auto max-w-7xl px-3 sm:px-4 lg:px-6\">\n <div class=\"h-14 flex items-center gap-3\">\n <button class=\"lg:hidden inline-flex h-9 w-9 items-center justify-center rounded-md border border-muted/30\" aria-label=\"Open menu\" onclick={() => (mobileOpen = true)}>\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\"/></svg>\n </button>\n <a href=\"/\" class=\"flex items-center gap-2\">{#if logo}<svelte:component this={logo} />{/if}<span class=\"font-semibold\">{brand}</span></a>\n <nav class=\"ml-4 hidden lg:flex items-stretch gap-1\">\n {#each topItems as item}\n <div class=\"relative\" onmouseleave={() => (openId = null)}>\n <button class=\"px-2.5 h-10 rounded-md inline-flex items-center gap-1.5 hover:bg-primary/10 focus:outline-none focus:ring-2 focus:ring-primary/40\"\n aria-haspopup={item.children?.length ? 'menu' : undefined}\n aria-expanded={openId === item.id}\n onmouseenter={() => (openId = item.children?.length ? item.id! : null)}\n onfocus={() => (openId = item.children?.length ? item.id! : null)}\n onclick={() => (openId = openId === item.id ? null : (item.children?.length ? item.id! : null))}\n >\n {#if item.icon}<svelte:component this={item.icon} class=\"h-4 w-4\" />{/if}\n <a href={item.href || '#'} class=\"px-0.5 rounded-md\" class:text-primary={isActive(item.href)}>{item.title}</a>\n {#if item.children?.length}<svg class=\"h-4 w-4 opacity-70\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>{/if}\n {#if item.badge}<span class=\"text-[10px] ml-1 rounded px-1.5 py-0.5 border border-primary/30 text-primary bg-primary/5\">{item.badge}</span>{/if}\n </button>\n {#if item.children?.length && openId === item.id}\n <div class=\"absolute left-0 mt-1 w-[min(90vw,48rem)] rounded-lg border border-muted/20 bg-surface shadow-lg p-3\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n {#each item.children as section}\n <div>\n <a href={section.href || '#'} class=\"font-medium hover:underline flex items-center gap-2\">{#if section.icon}<svelte:component this={section.icon} class=\"h-4 w-4\" />{/if}{section.title}</a>\n {#if section.children?.length}\n <ul class=\"mt-2 space-y-1\">\n {#each section.children as leaf}\n <li><a href={leaf.href || '#'} target={leaf.external ? '_blank' : undefined} rel={leaf.external ? 'noreferrer' : undefined} class=\"block rounded-md px-2 py-1 hover:bg-primary/10\">{leaf.title}</a></li>\n {/each}\n </ul>\n {/if}\n </div>\n {/each}\n </div>\n </div>\n {/if}\n </div>\n {/each}\n </nav>\n <div class=\"ml-auto flex items-center gap-2\">\n <slot name=\"search\" />\n <UserDropdown {user} items={userMenu} onselect={onselect} />\n </div>\n </div>\n </div>\n <MobileNav bind:open={mobileOpen} {items} {user} userItems={userMenu} />\n</header>\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/a/nav/UserDropdown.svelte", |
|
|
|
|
|
|
|
"content": "<script lang=\"ts\">\n import type { NavItem, UserInfo } from './nav-types';\n import { onMount } from 'svelte';\n let { user = null as UserInfo | null, items = [] as NavItem[], onselect = undefined as undefined | ((i:NavItem)=>void) } = $props();\n let open = false; let btn: HTMLButtonElement;\n function onDoc(e: MouseEvent){ if (!open) return; if (!(e.target instanceof Node)) return; if (!btn?.parentElement?.contains(e.target)) open = false; }\n onMount(()=>{ document.addEventListener('mousedown', onDoc); return () => document.removeEventListener('mousedown', onDoc); });\n</script>\n<div class=\"relative\">\n <button bind:this={btn} class=\"inline-flex items-center gap-2 h-9 px-2 rounded-md border border-muted/30 hover:bg-primary/10\" aria-haspopup=\"menu\" aria-expanded={open} onclick={() => (open = !open)}>\n <img src={user?.avatarUrl || 'https://via.placeholder.com/24'} alt=\"\" class=\"h-6 w-6 rounded-full object-cover\" />\n <span class=\"hidden sm:block text-sm\">{user?.name || 'Account'}</span>\n <svg class=\"h-4 w-4 opacity-70\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n {#if open}\n <div class=\"absolute right-0 mt-1 min-w-[14rem] rounded-lg border border-muted/20 bg-surface shadow-lg py-1 z-50\">\n {#if user}\n <div class=\"px-3 py-2 text-sm\">\n <div class=\"font-medium\">{user.name}</div>\n {#if user.email}<div class=\"opacity-70\">{user.email}</div>{/if}\n </div>\n <div class=\"my-1 h-px bg-muted/20\" />\n {/if}\n <ul>\n {#each items as it}\n {#if it.divider}\n <li class=\"my-1 h-px bg-muted/20\"></li>\n {:else}\n <li>\n <a href={it.href || '#'} target={it.external ? '_blank':undefined} rel={it.external ? 'noreferrer':undefined}\n class=\"flex items-center gap-2 px-3 py-2 text-sm hover:bg-primary/10\"\n onclick={(e)=>{ if (!it.href || it.href==='#'){ e.preventDefault(); open=false; onselect?.(it); } else { open=false; } }}>\n {it.title}\n {#if it.badge}<span class=\"ml-auto text-[10px] rounded px-1.5 py-0.5 border border-primary/30 text-primary bg-primary/5\">{it.badge}</span>{/if}\n </a>\n </li>\n {/if}\n {/each}\n </ul>\n </div>\n {/if}\n</div>\n" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"path": "src/lib/a/nav/MobileNav.svelte", |
|
|
|
|
|
|
|
"content": "<script lang=\"ts\">\n import type { NavItem, UserInfo } from './nav-types';\n let { open = false, items = [] as NavItem[], user = null as UserInfo | null, userItems = [] as NavItem[] } = $props();\n function close(){ open = false; }\n</script>\n{#if open}\n <div class=\"fixed inset-0 z-50\">\n <div class=\"absolute inset-0 bg-black/40\" onclick={close}></div>\n <aside class=\"absolute left-0 top-0 h-full w-[88vw] max-w-sm bg-surface border-r border-muted/20 shadow-xl p-3 overflow-y-auto\">\n <div class=\"flex items-center gap-3\">\n <img src={user?.avatarUrl || 'https://via.placeholder.com/32'} alt=\"\" class=\"h-8 w-8 rounded-full\" />\n <div>\n <div class=\"text-sm font-medium\">{user?.name || 'Welcome'}</div>\n {#if user?.email}<div class=\"text-xs opacity-70\">{user.email}</div>{/if}\n </div>\n <button class=\"ml-auto h-8 w-8 grid place-items-center rounded-md border border-muted/30\" onclick={close} aria-label=\"Close\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>\n </button>\n </div>\n <nav class=\"mt-4\">\n <ul class=\"space-y-1\">\n {#each items as item}\n <li>\n {#if item.children?.length}\n <details>\n <summary class=\"flex items-center gap-2 cursor-pointer list-none rounded-md px-2 py-2 hover:bg-primary/10\">\n <span class=\"flex-1\">{item.title}</span>\n <svg class=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </summary>\n <ul class=\"mt-1 ml-3 space-y-1\">\n {#each item.children as child}\n <li>\n {#if child.children?.length}\n <details>\n <summary class=\"flex items-center gap-2 cursor-pointer list-none rounded-md px-2 py-2 hover:bg-primary/10\">\n <span class=\"flex-1\">{child.title}</span>\n <svg class=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </summary>\n <ul class=\"mt-1 ml-3 space-y-1\">\n {#each child.children as leaf}\n <li><a href={leaf.href || '#'} target={leaf.external ? '_blank' : undefined} rel={leaf.external ? 'noreferrer' : undefined} class=\"block rounded-md px-2 py-2 hover:bg-primary/10\">{leaf.title}</a></li>\n {/each}\n </ul>\n </details>\n {:else}\n <a href={child.href || '#'} class=\"block rounded-md px-2 py-2 hover:bg-primary/10\">{child.title}</a>\n {/if}\n </li>\n {/each}\n </ul>\n </details>\n {:else}\n <a href={item.href || '#'} class=\"block rounded-md px-2 py-2 hover:bg-primary/10\">{item.title}</a>\n {/if}\n </li>\n {/each}\n </ul>\n </nav>\n {#if userItems.length}\n <div class=\"my-4 h-px bg-muted/20\"></div>\n <ul class=\"space-y-1\">\n {#each userItems as it}\n {#if it.divider}\n <li class=\"my-2 h-px bg-muted/20\"></li>\n {:else}\n <li><a href={it.href || '#'} class=\"block rounded-md px-2 py-2 hover:bg-primary/10\">{it.title}</a></li>\n {/if}\n {/each}\n </ul>\n {/if}\n </aside>\n </div>\n{/if}\n" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
] |
|
|
|
|
|
|
|
} |
|
|
|
|