@silicajs/components 0.1.0

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.
Files changed (43) hide show
  1. package/dist/backlinks.d.ts +12 -0
  2. package/dist/backlinks.js +36 -0
  3. package/dist/backlinks.js.map +1 -0
  4. package/dist/breadcrumbs.d.ts +10 -0
  5. package/dist/breadcrumbs.js +37 -0
  6. package/dist/breadcrumbs.js.map +1 -0
  7. package/dist/dark-mode-toggle.d.ts +8 -0
  8. package/dist/dark-mode-toggle.js +107 -0
  9. package/dist/dark-mode-toggle.js.map +1 -0
  10. package/dist/index.d.ts +16 -0
  11. package/dist/index.js +45 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/not-allowed.d.ts +14 -0
  14. package/dist/not-allowed.js +27 -0
  15. package/dist/not-allowed.js.map +1 -0
  16. package/dist/not-found.d.ts +14 -0
  17. package/dist/not-found.js +27 -0
  18. package/dist/not-found.js.map +1 -0
  19. package/dist/page-properties.d.ts +10 -0
  20. package/dist/page-properties.js +80 -0
  21. package/dist/page-properties.js.map +1 -0
  22. package/dist/routing.d.ts +22 -0
  23. package/dist/routing.js +42 -0
  24. package/dist/routing.js.map +1 -0
  25. package/dist/search.d.ts +14 -0
  26. package/dist/search.js +158 -0
  27. package/dist/search.js.map +1 -0
  28. package/dist/slug.d.ts +6 -0
  29. package/dist/slug.js +20 -0
  30. package/dist/slug.js.map +1 -0
  31. package/dist/table-of-contents.d.ts +13 -0
  32. package/dist/table-of-contents.js +25 -0
  33. package/dist/table-of-contents.js.map +1 -0
  34. package/dist/tags-list.d.ts +11 -0
  35. package/dist/tags-list.js +68 -0
  36. package/dist/tags-list.js.map +1 -0
  37. package/dist/user-menu.d.ts +9 -0
  38. package/dist/user-menu.js +41 -0
  39. package/dist/user-menu.js.map +1 -0
  40. package/dist/vault-tree.d.ts +15 -0
  41. package/dist/vault-tree.js +248 -0
  42. package/dist/vault-tree.js.map +1 -0
  43. package/package.json +46 -0
package/dist/search.js ADDED
@@ -0,0 +1,158 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import {
5
+ Command,
6
+ CommandEmpty,
7
+ CommandInput,
8
+ CommandItem,
9
+ CommandList
10
+ } from "@silicajs/ui/components/command";
11
+ import {
12
+ Dialog,
13
+ DialogContent,
14
+ DialogDescription,
15
+ DialogHeader,
16
+ DialogTitle
17
+ } from "@silicajs/ui/components/dialog";
18
+ import { Button } from "@silicajs/ui/components/button";
19
+ import { SearchIcon } from "lucide-react";
20
+ import { useSilicaRouting } from "./routing.js";
21
+ import { slugToHref } from "./slug.js";
22
+ function SearchTrigger({
23
+ placeholder = "Search\u2026",
24
+ className
25
+ }) {
26
+ const [open, setOpen] = React.useState(false);
27
+ React.useEffect(() => {
28
+ const onKeyDown = (event) => {
29
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
30
+ event.preventDefault();
31
+ setOpen((value) => !value);
32
+ }
33
+ };
34
+ window.addEventListener("keydown", onKeyDown);
35
+ return () => window.removeEventListener("keydown", onKeyDown);
36
+ }, []);
37
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
38
+ /* @__PURE__ */ jsxs(
39
+ Button,
40
+ {
41
+ type: "button",
42
+ variant: "outline",
43
+ size: "sm",
44
+ onClick: () => setOpen(true),
45
+ className,
46
+ children: [
47
+ /* @__PURE__ */ jsx(
48
+ SearchIcon,
49
+ {
50
+ "data-icon": "inline-start",
51
+ className: "text-muted-foreground"
52
+ }
53
+ ),
54
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left text-muted-foreground", children: placeholder }),
55
+ /* @__PURE__ */ jsxs(
56
+ "kbd",
57
+ {
58
+ "data-icon": "inline-end",
59
+ className: "pointer-events-none ml-2 inline-flex h-5 select-none items-center gap-1 rounded border border-border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground",
60
+ children: [
61
+ /* @__PURE__ */ jsx("span", { className: "text-xs", children: "\u2318" }),
62
+ "K"
63
+ ]
64
+ }
65
+ )
66
+ ]
67
+ }
68
+ ),
69
+ /* @__PURE__ */ jsx(SearchPalette, { open, onOpenChange: setOpen })
70
+ ] });
71
+ }
72
+ function SearchPalette({ open, onOpenChange }) {
73
+ const { navigate } = useSilicaRouting();
74
+ const [query, setQuery] = React.useState("");
75
+ const [results, setResults] = React.useState([]);
76
+ const [isLoading, setIsLoading] = React.useState(false);
77
+ React.useEffect(() => {
78
+ if (!open) {
79
+ setQuery("");
80
+ setResults([]);
81
+ setIsLoading(false);
82
+ }
83
+ }, [open]);
84
+ React.useEffect(() => {
85
+ const controller = new AbortController();
86
+ const trimmed = query.trim();
87
+ if (!trimmed) {
88
+ setResults([]);
89
+ setIsLoading(false);
90
+ return () => controller.abort();
91
+ }
92
+ setIsLoading(true);
93
+ const timeout = window.setTimeout(() => {
94
+ fetch(`/api/search?q=${encodeURIComponent(trimmed)}`, {
95
+ signal: controller.signal
96
+ }).then((response) => response.ok ? response.json() : { results: [] }).then((payload) => {
97
+ setResults(payload.results ?? []);
98
+ }).catch((error) => {
99
+ if (error instanceof DOMException && error.name === "AbortError")
100
+ return;
101
+ setResults([]);
102
+ }).finally(() => setIsLoading(false));
103
+ }, 120);
104
+ return () => {
105
+ window.clearTimeout(timeout);
106
+ controller.abort();
107
+ };
108
+ }, [query]);
109
+ const close = () => onOpenChange(false);
110
+ return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange, children: [
111
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "sr-only", children: [
112
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Search" }),
113
+ /* @__PURE__ */ jsx(DialogDescription, { children: "Search your vault." })
114
+ ] }),
115
+ /* @__PURE__ */ jsx(
116
+ DialogContent,
117
+ {
118
+ className: "top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0",
119
+ showCloseButton: false,
120
+ children: /* @__PURE__ */ jsxs(Command, { shouldFilter: false, children: [
121
+ /* @__PURE__ */ jsx(
122
+ CommandInput,
123
+ {
124
+ autoFocus: true,
125
+ placeholder: "Search your vault\u2026",
126
+ value: query,
127
+ onValueChange: setQuery
128
+ }
129
+ ),
130
+ /* @__PURE__ */ jsxs(CommandList, { children: [
131
+ isLoading ? /* @__PURE__ */ jsx("div", { className: "px-4 py-6 text-center text-sm text-muted-foreground", children: "Searching\u2026" }) : null,
132
+ !isLoading && query.trim() && results.length === 0 ? /* @__PURE__ */ jsx(CommandEmpty, { children: "No results" }) : null,
133
+ results.map((result) => /* @__PURE__ */ jsx(
134
+ CommandItem,
135
+ {
136
+ value: `${result.title} ${result.slug}`,
137
+ onSelect: () => {
138
+ navigate(slugToHref(result.slug));
139
+ close();
140
+ },
141
+ children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [
142
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: result.title }),
143
+ result.excerpt ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: result.excerpt }) : null
144
+ ] })
145
+ },
146
+ result.slug
147
+ ))
148
+ ] })
149
+ ] })
150
+ }
151
+ )
152
+ ] });
153
+ }
154
+ export {
155
+ SearchPalette,
156
+ SearchTrigger
157
+ };
158
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/search.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n Command,\n CommandEmpty,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@silicajs/ui/components/command\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from \"@silicajs/ui/components/dialog\";\nimport { Button } from \"@silicajs/ui/components/button\";\nimport { SearchIcon } from \"lucide-react\";\n\nimport { useSilicaRouting } from \"./routing.js\";\nimport { slugToHref } from \"./slug.js\";\n\ntype SearchResult = {\n slug: string;\n title: string;\n excerpt: string;\n};\n\nexport type SearchTriggerProps = {\n placeholder?: string;\n className?: string;\n};\n\nexport function SearchTrigger({\n placeholder = \"Search…\",\n className,\n}: SearchTriggerProps) {\n const [open, setOpen] = React.useState(false);\n\n React.useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === \"k\") {\n event.preventDefault();\n setOpen((value) => !value);\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, []);\n\n return (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setOpen(true)}\n className={className}\n >\n <SearchIcon\n data-icon=\"inline-start\"\n className=\"text-muted-foreground\"\n />\n <span className=\"flex-1 text-left text-muted-foreground\">\n {placeholder}\n </span>\n <kbd\n data-icon=\"inline-end\"\n className=\"pointer-events-none ml-2 inline-flex h-5 select-none items-center gap-1 rounded border border-border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground\"\n >\n <span className=\"text-xs\">⌘</span>K\n </kbd>\n </Button>\n <SearchPalette open={open} onOpenChange={setOpen} />\n </>\n );\n}\n\nexport type SearchPaletteProps = {\n open: boolean;\n onOpenChange: (next: boolean) => void;\n};\n\nexport function SearchPalette({ open, onOpenChange }: SearchPaletteProps) {\n const { navigate } = useSilicaRouting();\n const [query, setQuery] = React.useState(\"\");\n const [results, setResults] = React.useState<SearchResult[]>([]);\n const [isLoading, setIsLoading] = React.useState(false);\n\n React.useEffect(() => {\n if (!open) {\n setQuery(\"\");\n setResults([]);\n setIsLoading(false);\n }\n }, [open]);\n\n React.useEffect(() => {\n const controller = new AbortController();\n const trimmed = query.trim();\n if (!trimmed) {\n setResults([]);\n setIsLoading(false);\n return () => controller.abort();\n }\n\n setIsLoading(true);\n const timeout = window.setTimeout(() => {\n fetch(`/api/search?q=${encodeURIComponent(trimmed)}`, {\n signal: controller.signal,\n })\n .then((response) => (response.ok ? response.json() : { results: [] }))\n .then((payload: { results?: SearchResult[] }) => {\n setResults(payload.results ?? []);\n })\n .catch((error: unknown) => {\n if (error instanceof DOMException && error.name === \"AbortError\")\n return;\n setResults([]);\n })\n .finally(() => setIsLoading(false));\n }, 120);\n\n return () => {\n window.clearTimeout(timeout);\n controller.abort();\n };\n }, [query]);\n\n const close = () => onOpenChange(false);\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogHeader className=\"sr-only\">\n <DialogTitle>Search</DialogTitle>\n <DialogDescription>Search your vault.</DialogDescription>\n </DialogHeader>\n <DialogContent\n className=\"top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0\"\n showCloseButton={false}\n >\n <Command shouldFilter={false}>\n <CommandInput\n autoFocus\n placeholder=\"Search your vault…\"\n value={query}\n onValueChange={setQuery}\n />\n <CommandList>\n {isLoading ? (\n <div className=\"px-4 py-6 text-center text-sm text-muted-foreground\">\n Searching…\n </div>\n ) : null}\n {!isLoading && query.trim() && results.length === 0 ? (\n <CommandEmpty>No results</CommandEmpty>\n ) : null}\n {results.map((result) => (\n <CommandItem\n key={result.slug}\n value={`${result.title} ${result.slug}`}\n onSelect={() => {\n navigate(slugToHref(result.slug));\n close();\n }}\n >\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"truncate font-medium text-foreground\">\n {result.title}\n </span>\n {result.excerpt ? (\n <span className=\"truncate text-xs text-muted-foreground\">\n {result.excerpt}\n </span>\n ) : null}\n </div>\n </CommandItem>\n ))}\n </CommandList>\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
package/dist/slug.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ declare function slugToHref(slug: string): string;
2
+ /** Returns an href when `segmentPath` maps to a real page; otherwise undefined. */
3
+ declare function breadcrumbSegmentHref(segmentPath: string, allSlugs: readonly string[]): string | undefined;
4
+ declare function prettySegment(segment: string): string;
5
+
6
+ export { breadcrumbSegmentHref, prettySegment, slugToHref };
package/dist/slug.js ADDED
@@ -0,0 +1,20 @@
1
+ function slugToHref(slug) {
2
+ if (slug === "index") return "/";
3
+ return `/${slug.replace(/\/index$/, "")}`;
4
+ }
5
+ function breadcrumbSegmentHref(segmentPath, allSlugs) {
6
+ const slugs = new Set(allSlugs);
7
+ if (slugs.has(segmentPath)) return slugToHref(segmentPath);
8
+ const indexSlug = `${segmentPath}/index`;
9
+ if (slugs.has(indexSlug)) return slugToHref(indexSlug);
10
+ return void 0;
11
+ }
12
+ function prettySegment(segment) {
13
+ return segment.replace(/[-_]/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
14
+ }
15
+ export {
16
+ breadcrumbSegmentHref,
17
+ prettySegment,
18
+ slugToHref
19
+ };
20
+ //# sourceMappingURL=slug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/slug.ts"],"sourcesContent":["export function slugToHref(slug: string): string {\n if (slug === \"index\") return \"/\";\n return `/${slug.replace(/\\/index$/, \"\")}`;\n}\n\n/** Returns an href when `segmentPath` maps to a real page; otherwise undefined. */\nexport function breadcrumbSegmentHref(\n segmentPath: string,\n allSlugs: readonly string[],\n): string | undefined {\n const slugs = new Set(allSlugs);\n if (slugs.has(segmentPath)) return slugToHref(segmentPath);\n const indexSlug = `${segmentPath}/index`;\n if (slugs.has(indexSlug)) return slugToHref(indexSlug);\n return undefined;\n}\n\nexport function prettySegment(segment: string): string {\n return segment\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n"],"mappings":"AAAO,SAAS,WAAW,MAAsB;AAC/C,MAAI,SAAS,QAAS,QAAO;AAC7B,SAAO,IAAI,KAAK,QAAQ,YAAY,EAAE,CAAC;AACzC;AAGO,SAAS,sBACd,aACA,UACoB;AACpB,QAAM,QAAQ,IAAI,IAAI,QAAQ;AAC9B,MAAI,MAAM,IAAI,WAAW,EAAG,QAAO,WAAW,WAAW;AACzD,QAAM,YAAY,GAAG,WAAW;AAChC,MAAI,MAAM,IAAI,SAAS,EAAG,QAAO,WAAW,SAAS;AACrD,SAAO;AACT;AAEO,SAAS,cAAc,SAAyB;AACrD,SAAO,QACJ,QAAQ,SAAS,GAAG,EACpB,QAAQ,SAAS,CAAC,WAAW,OAAO,YAAY,CAAC;AACtD;","names":[]}
@@ -0,0 +1,13 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import { TocItem } from '@silicajs/core/runtime';
4
+
5
+ type TableOfContentsProps = {
6
+ toc: TocItem[];
7
+ activeId?: string;
8
+ heading?: React.ReactNode;
9
+ className?: string;
10
+ };
11
+ declare function TableOfContents({ toc, activeId, heading, className, }: TableOfContentsProps): react_jsx_runtime.JSX.Element | null;
12
+
13
+ export { TableOfContents, type TableOfContentsProps };
@@ -0,0 +1,25 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { TocList } from "@silicajs/ui/components/toc-list";
3
+ function TableOfContents({
4
+ toc,
5
+ activeId,
6
+ heading = "On this page",
7
+ className
8
+ }) {
9
+ if (toc.length === 0) return null;
10
+ const minDepth = Math.min(...toc.map((item) => item.depth));
11
+ const items = toc.map((item) => ({
12
+ id: item.id,
13
+ label: item.text,
14
+ href: `#${item.id}`,
15
+ depth: Math.max(0, item.depth - minDepth)
16
+ }));
17
+ return /* @__PURE__ */ jsxs("nav", { "aria-label": "Table of contents", className, children: [
18
+ heading ? /* @__PURE__ */ jsx("p", { className: "mb-2 text-xs font-medium tracking-wider text-muted-foreground uppercase", children: heading }) : null,
19
+ /* @__PURE__ */ jsx(TocList, { items, activeId })
20
+ ] });
21
+ }
22
+ export {
23
+ TableOfContents
24
+ };
25
+ //# sourceMappingURL=table-of-contents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/table-of-contents.tsx"],"sourcesContent":["import * as React from \"react\";\nimport type { TocItem } from \"@silicajs/core/runtime\";\n\nimport { TocList, type TocListItem } from \"@silicajs/ui/components/toc-list\";\n\nexport type TableOfContentsProps = {\n toc: TocItem[];\n activeId?: string;\n heading?: React.ReactNode;\n className?: string;\n};\n\nexport function TableOfContents({\n toc,\n activeId,\n heading = \"On this page\",\n className,\n}: TableOfContentsProps) {\n if (toc.length === 0) return null;\n\n const minDepth = Math.min(...toc.map((item) => item.depth));\n const items: TocListItem[] = toc.map((item) => ({\n id: item.id,\n label: item.text,\n href: `#${item.id}`,\n depth: Math.max(0, item.depth - minDepth),\n }));\n\n return (\n <nav aria-label=\"Table of contents\" className={className}>\n {heading ? (\n <p className=\"mb-2 text-xs font-medium tracking-wider text-muted-foreground uppercase\">\n {heading}\n </p>\n ) : null}\n <TocList items={items} activeId={activeId} />\n </nav>\n );\n}\n"],"mappings":"AA6BI,SAEI,KAFJ;AA1BJ,SAAS,eAAiC;AASnC,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AACF,GAAyB;AACvB,MAAI,IAAI,WAAW,EAAG,QAAO;AAE7B,QAAM,WAAW,KAAK,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AAC1D,QAAM,QAAuB,IAAI,IAAI,CAAC,UAAU;AAAA,IAC9C,IAAI,KAAK;AAAA,IACT,OAAO,KAAK;AAAA,IACZ,MAAM,IAAI,KAAK,EAAE;AAAA,IACjB,OAAO,KAAK,IAAI,GAAG,KAAK,QAAQ,QAAQ;AAAA,EAC1C,EAAE;AAEF,SACE,qBAAC,SAAI,cAAW,qBAAoB,WACjC;AAAA,cACC,oBAAC,OAAE,WAAU,2EACV,mBACH,IACE;AAAA,IACJ,oBAAC,WAAQ,OAAc,UAAoB;AAAA,KAC7C;AAEJ;","names":[]}
@@ -0,0 +1,11 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { Manifest } from '@silicajs/core/runtime';
3
+
4
+ type TagsListProps = {
5
+ manifest: Manifest;
6
+ tag: string;
7
+ className?: string;
8
+ };
9
+ declare function TagsList({ manifest, tag, className }: TagsListProps): react_jsx_runtime.JSX.Element;
10
+
11
+ export { TagsList, type TagsListProps };
@@ -0,0 +1,68 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { tagMatches } from "@silicajs/remark-obsidian";
4
+ import { tagToHref } from "@silicajs/core/runtime";
5
+ import { TagBadge } from "@silicajs/ui/components/tag-badge";
6
+ import { SilicaLink } from "./routing.js";
7
+ import { slugToHref } from "./slug.js";
8
+ function TagsList({ manifest, tag, className }) {
9
+ const entries = React.useMemo(
10
+ () => manifest.entries.filter(
11
+ (entry) => entry.tags.some((entryTag) => tagMatches(entryTag, tag))
12
+ ).sort((a, b) => a.title.localeCompare(b.title)),
13
+ [manifest.entries, tag]
14
+ );
15
+ const relatedTags = React.useMemo(() => {
16
+ const counts = /* @__PURE__ */ new Map();
17
+ for (const entry of entries) {
18
+ for (const t of entry.tags) {
19
+ if (tagMatches(t, tag) && tagMatches(tag, t)) continue;
20
+ counts.set(t, (counts.get(t) ?? 0) + 1);
21
+ }
22
+ }
23
+ return [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 12).map(([t]) => t);
24
+ }, [entries, tag]);
25
+ return /* @__PURE__ */ jsxs("section", { className, "data-slot": "tags-list", children: [
26
+ /* @__PURE__ */ jsxs("header", { className: "mb-6 flex items-baseline gap-3", children: [
27
+ /* @__PURE__ */ jsxs("h1", { className: "text-2xl font-semibold tracking-tight", children: [
28
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "#" }),
29
+ tag
30
+ ] }),
31
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
32
+ entries.length,
33
+ " ",
34
+ entries.length === 1 ? "page" : "pages"
35
+ ] })
36
+ ] }),
37
+ relatedTags.length > 0 ? /* @__PURE__ */ jsx("div", { className: "mb-6 flex flex-wrap gap-2", children: relatedTags.map((t) => /* @__PURE__ */ jsx(
38
+ TagBadge,
39
+ {
40
+ tag: t,
41
+ href: tagToHref(t),
42
+ render: /* @__PURE__ */ jsx(
43
+ SilicaLink,
44
+ {
45
+ href: tagToHref(t),
46
+ className: "cursor-pointer text-foreground/80 no-underline transition-colors hover:text-foreground"
47
+ }
48
+ )
49
+ },
50
+ t
51
+ )) }) : null,
52
+ /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2", children: entries.map((entry) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
53
+ SilicaLink,
54
+ {
55
+ href: slugToHref(entry.slug),
56
+ className: "block rounded-md px-3 py-2 transition-colors hover:bg-muted",
57
+ children: [
58
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-foreground", children: entry.title }),
59
+ entry.description ? /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: entry.description }) : null
60
+ ]
61
+ }
62
+ ) }, entry.slug)) })
63
+ ] });
64
+ }
65
+ export {
66
+ TagsList
67
+ };
68
+ //# sourceMappingURL=tags-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tags-list.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { tagMatches } from \"@silicajs/remark-obsidian\";\nimport { tagToHref, type Manifest } from \"@silicajs/core/runtime\";\n\nimport { TagBadge } from \"@silicajs/ui/components/tag-badge\";\n\nimport { SilicaLink } from \"./routing.js\";\nimport { slugToHref } from \"./slug.js\";\n\nexport type TagsListProps = {\n manifest: Manifest;\n tag: string;\n className?: string;\n};\n\nexport function TagsList({ manifest, tag, className }: TagsListProps) {\n const entries = React.useMemo(\n () =>\n manifest.entries\n .filter((entry) =>\n entry.tags.some((entryTag) => tagMatches(entryTag, tag)),\n )\n .sort((a, b) => a.title.localeCompare(b.title)),\n [manifest.entries, tag],\n );\n const relatedTags = React.useMemo(() => {\n const counts = new Map<string, number>();\n for (const entry of entries) {\n for (const t of entry.tags) {\n if (tagMatches(t, tag) && tagMatches(tag, t)) continue;\n counts.set(t, (counts.get(t) ?? 0) + 1);\n }\n }\n return [...counts.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 12)\n .map(([t]) => t);\n }, [entries, tag]);\n\n return (\n <section className={className} data-slot=\"tags-list\">\n <header className=\"mb-6 flex items-baseline gap-3\">\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n <span className=\"text-muted-foreground\">#</span>\n {tag}\n </h1>\n <span className=\"text-sm text-muted-foreground\">\n {entries.length} {entries.length === 1 ? \"page\" : \"pages\"}\n </span>\n </header>\n {relatedTags.length > 0 ? (\n <div className=\"mb-6 flex flex-wrap gap-2\">\n {relatedTags.map((t) => (\n <TagBadge\n key={t}\n tag={t}\n href={tagToHref(t)}\n render={\n <SilicaLink\n href={tagToHref(t)}\n className=\"cursor-pointer text-foreground/80 no-underline transition-colors hover:text-foreground\"\n />\n }\n />\n ))}\n </div>\n ) : null}\n <ul className=\"flex flex-col gap-2\">\n {entries.map((entry) => (\n <li key={entry.slug}>\n <SilicaLink\n href={slugToHref(entry.slug)}\n className=\"block rounded-md px-3 py-2 transition-colors hover:bg-muted\"\n >\n <div className=\"font-medium text-foreground\">{entry.title}</div>\n {entry.description ? (\n <div className=\"text-sm text-muted-foreground\">\n {entry.description}\n </div>\n ) : null}\n </SilicaLink>\n </li>\n ))}\n </ul>\n </section>\n );\n}\n"],"mappings":"AA0CQ,SACE,KADF;AA1CR,YAAY,WAAW;AACvB,SAAS,kBAAkB;AAC3B,SAAS,iBAAgC;AAEzC,SAAS,gBAAgB;AAEzB,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAQpB,SAAS,SAAS,EAAE,UAAU,KAAK,UAAU,GAAkB;AACpE,QAAM,UAAU,MAAM;AAAA,IACpB,MACE,SAAS,QACN;AAAA,MAAO,CAAC,UACP,MAAM,KAAK,KAAK,CAAC,aAAa,WAAW,UAAU,GAAG,CAAC;AAAA,IACzD,EACC,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC;AAAA,IAClD,CAAC,SAAS,SAAS,GAAG;AAAA,EACxB;AACA,QAAM,cAAc,MAAM,QAAQ,MAAM;AACtC,UAAM,SAAS,oBAAI,IAAoB;AACvC,eAAW,SAAS,SAAS;AAC3B,iBAAW,KAAK,MAAM,MAAM;AAC1B,YAAI,WAAW,GAAG,GAAG,KAAK,WAAW,KAAK,CAAC,EAAG;AAC9C,eAAO,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,CAAC;AAAA,MACxC;AAAA,IACF;AACA,WAAO,CAAC,GAAG,OAAO,QAAQ,CAAC,EACxB,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAC1B,MAAM,GAAG,EAAE,EACX,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AAAA,EACnB,GAAG,CAAC,SAAS,GAAG,CAAC;AAEjB,SACE,qBAAC,aAAQ,WAAsB,aAAU,aACvC;AAAA,yBAAC,YAAO,WAAU,kCAChB;AAAA,2BAAC,QAAG,WAAU,yCACZ;AAAA,4BAAC,UAAK,WAAU,yBAAwB,eAAC;AAAA,QACxC;AAAA,SACH;AAAA,MACA,qBAAC,UAAK,WAAU,iCACb;AAAA,gBAAQ;AAAA,QAAO;AAAA,QAAE,QAAQ,WAAW,IAAI,SAAS;AAAA,SACpD;AAAA,OACF;AAAA,IACC,YAAY,SAAS,IACpB,oBAAC,SAAI,WAAU,6BACZ,sBAAY,IAAI,CAAC,MAChB;AAAA,MAAC;AAAA;AAAA,QAEC,KAAK;AAAA,QACL,MAAM,UAAU,CAAC;AAAA,QACjB,QACE;AAAA,UAAC;AAAA;AAAA,YACC,MAAM,UAAU,CAAC;AAAA,YACjB,WAAU;AAAA;AAAA,QACZ;AAAA;AAAA,MAPG;AAAA,IASP,CACD,GACH,IACE;AAAA,IACJ,oBAAC,QAAG,WAAU,uBACX,kBAAQ,IAAI,CAAC,UACZ,oBAAC,QACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,WAAW,MAAM,IAAI;AAAA,QAC3B,WAAU;AAAA,QAEV;AAAA,8BAAC,SAAI,WAAU,+BAA+B,gBAAM,OAAM;AAAA,UACzD,MAAM,cACL,oBAAC,SAAI,WAAU,iCACZ,gBAAM,aACT,IACE;AAAA;AAAA;AAAA,IACN,KAXO,MAAM,IAYf,CACD,GACH;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,9 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type UserMenuProps = {
4
+ label?: string;
5
+ className?: string;
6
+ };
7
+ declare function UserMenu({ label, className }: UserMenuProps): react_jsx_runtime.JSX.Element;
8
+
9
+ export { UserMenu, type UserMenuProps };
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { UserIcon } from "lucide-react";
4
+ import { Button } from "@silicajs/ui/components/button";
5
+ import {
6
+ DropdownMenu,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ DropdownMenuLabel,
10
+ DropdownMenuSeparator,
11
+ DropdownMenuTrigger
12
+ } from "@silicajs/ui/components/dropdown-menu";
13
+ function UserMenu({ label = "Signed in", className }) {
14
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
15
+ /* @__PURE__ */ jsx(
16
+ DropdownMenuTrigger,
17
+ {
18
+ render: /* @__PURE__ */ jsx(
19
+ Button,
20
+ {
21
+ type: "button",
22
+ variant: "ghost",
23
+ size: "icon-sm",
24
+ "aria-label": "User menu",
25
+ className,
26
+ children: /* @__PURE__ */ jsx(UserIcon, { "aria-hidden": "true" })
27
+ }
28
+ )
29
+ }
30
+ ),
31
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", sideOffset: 6, className: "min-w-44", children: [
32
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: label }),
33
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
34
+ /* @__PURE__ */ jsx(DropdownMenuItem, { render: /* @__PURE__ */ jsx("a", { href: "/api/auth/sign-out", children: "Sign out" }) })
35
+ ] })
36
+ ] });
37
+ }
38
+ export {
39
+ UserMenu
40
+ };
41
+ //# sourceMappingURL=user-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/user-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { UserIcon } from \"lucide-react\";\n\nimport { Button } from \"@silicajs/ui/components/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@silicajs/ui/components/dropdown-menu\";\n\nexport type UserMenuProps = {\n label?: string;\n className?: string;\n};\n\nexport function UserMenu({ label = \"Signed in\", className }: UserMenuProps) {\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label=\"User menu\"\n className={className}\n >\n <UserIcon aria-hidden=\"true\" />\n </Button>\n }\n />\n <DropdownMenuContent align=\"end\" sideOffset={6} className=\"min-w-44\">\n <DropdownMenuLabel>{label}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem render={<a href=\"/api/auth/sign-out\">Sign out</a>} />\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"mappings":";AA+BY,cAIN,YAJM;AA7BZ,SAAS,gBAAgB;AAEzB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAOA,SAAS,SAAS,EAAE,QAAQ,aAAa,UAAU,GAAkB;AAC1E,SACE,qBAAC,gBACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,QACE;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,cAAW;AAAA,YACX;AAAA,YAEA,8BAAC,YAAS,eAAY,QAAO;AAAA;AAAA,QAC/B;AAAA;AAAA,IAEJ;AAAA,IACA,qBAAC,uBAAoB,OAAM,OAAM,YAAY,GAAG,WAAU,YACxD;AAAA,0BAAC,qBAAmB,iBAAM;AAAA,MAC1B,oBAAC,yBAAsB;AAAA,MACvB,oBAAC,oBAAiB,QAAQ,oBAAC,OAAE,MAAK,sBAAqB,sBAAQ,GAAM;AAAA,OACvE;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,15 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type VaultTreeEntry = {
4
+ slug: string;
5
+ title: string;
6
+ sortKey?: string;
7
+ };
8
+ type VaultTreeProps = {
9
+ entries: VaultTreeEntry[];
10
+ currentSlug?: string;
11
+ showHomeLink?: boolean;
12
+ };
13
+ declare function VaultTree({ entries, currentSlug: currentSlugProp, showHomeLink, }: VaultTreeProps): react_jsx_runtime.JSX.Element;
14
+
15
+ export { VaultTree, type VaultTreeEntry, type VaultTreeProps };