idcmd 0.0.5 → 0.0.7

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 (51) hide show
  1. package/README.md +29 -9
  2. package/package.json +2 -2
  3. package/src/build.ts +6 -5
  4. package/src/cli/commands/build.ts +11 -7
  5. package/src/cli/commands/client.ts +328 -0
  6. package/src/cli/commands/dev.ts +92 -34
  7. package/src/cli/commands/init.ts +93 -2
  8. package/src/cli/main.ts +12 -0
  9. package/src/cli/runtime-assets.ts +89 -0
  10. package/src/cli.ts +0 -0
  11. package/src/client/index.ts +7 -1
  12. package/src/content/icons.ts +1 -1
  13. package/src/content/paths.ts +1 -1
  14. package/src/project/paths.ts +26 -30
  15. package/src/render/layout-loader.ts +7 -4
  16. package/src/render/layout.tsx +10 -2
  17. package/src/render/page-renderer.ts +12 -2
  18. package/src/render/right-rail-loader.ts +49 -0
  19. package/src/render/right-rail.tsx +10 -6
  20. package/src/search/page.tsx +4 -2
  21. package/src/search/search-page-loader.ts +51 -0
  22. package/src/search/server-page.ts +52 -18
  23. package/src/server/live-reload.ts +2 -6
  24. package/src/server/static.ts +1 -1
  25. package/src/server.ts +0 -1
  26. package/src/site/config.ts +2 -10
  27. package/templates/default/.github/workflows/ci.yml +24 -0
  28. package/templates/default/README.md +31 -5
  29. package/templates/default/package.json +2 -1
  30. package/templates/default/scripts/check-internal.ts +56 -0
  31. package/templates/default/scripts/check.ts +332 -0
  32. package/templates/default/scripts/smoke.ts +223 -0
  33. package/templates/default/site/{public/_idcmd/llm-menu.js → code/runtime/llm-menu.ts} +27 -18
  34. package/templates/default/site/{public/_idcmd/nav-prefetch.js → code/runtime/nav-prefetch.ts} +3 -3
  35. package/templates/default/site/{public/_idcmd/right-rail-scrollspy.js → code/runtime/right-rail-scrollspy.ts} +73 -32
  36. package/templates/default/site/{server → code}/server.ts +1 -1
  37. package/templates/default/site/code/ui/layout.tsx +237 -0
  38. package/templates/default/site/code/ui/right-rail.tsx +246 -0
  39. package/templates/default/site/code/ui/search-page.tsx +87 -0
  40. package/templates/default/tsconfig.json +1 -1
  41. package/templates/default/site/client/layout.tsx +0 -2
  42. package/templates/default/site/client/right-rail.tsx +0 -1
  43. package/templates/default/site/client/search-page.tsx +0 -1
  44. /package/templates/default/site/{public → assets}/anthropic-white.svg +0 -0
  45. /package/templates/default/site/{public → assets}/favicon.svg +0 -0
  46. /package/templates/default/site/{icons → assets/icons}/file.svg +0 -0
  47. /package/templates/default/site/{icons → assets/icons}/home.svg +0 -0
  48. /package/templates/default/site/{icons → assets/icons}/info.svg +0 -0
  49. /package/templates/default/site/{public → assets}/openai-white.svg +0 -0
  50. /package/templates/default/site/{server → code}/routes/api/hello.ts +0 -0
  51. /package/templates/default/site/{public/_idcmd/live-reload.js → code/runtime/live-reload.ts} +0 -0
@@ -0,0 +1,237 @@
1
+ import type { LayoutProps } from "idcmd/client";
2
+ /* eslint-disable react/no-danger */
3
+ import type { JSX } from "preact";
4
+
5
+ import { render } from "preact-render-to-string";
6
+
7
+ import { RightRail } from "./right-rail";
8
+
9
+ type NavItem = LayoutProps["navigation"][number]["items"][number];
10
+
11
+ const Icon = ({ svg }: { svg: string }): JSX.Element => (
12
+ <span
13
+ class="inline-flex h-[18px] w-[18px]"
14
+ dangerouslySetInnerHTML={{ __html: svg }}
15
+ />
16
+ );
17
+
18
+ const isActiveLink = (item: NavItem, currentPath: string): boolean =>
19
+ currentPath === item.href ||
20
+ (item.href !== "/" && currentPath.startsWith(item.href));
21
+
22
+ const Sidebar = ({
23
+ siteName,
24
+ navigation,
25
+ currentPath,
26
+ }: {
27
+ siteName: LayoutProps["siteName"];
28
+ navigation: LayoutProps["navigation"];
29
+ currentPath: LayoutProps["currentPath"];
30
+ }): JSX.Element => (
31
+ <aside class="sidebar">
32
+ <div class="sidebar-header">
33
+ <a
34
+ href="/"
35
+ class="text-sm font-medium tracking-tight"
36
+ data-prefetch="hover"
37
+ >
38
+ <span class="text-muted-foreground">~/</span>
39
+ {siteName}
40
+ </a>
41
+ </div>
42
+ <div class="sidebar-content">
43
+ {navigation.map((group) => (
44
+ <div key={group.id} class="py-2">
45
+ <p class="px-3 py-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
46
+ {group.label}
47
+ </p>
48
+ <nav class="space-y-1">
49
+ {group.items.map((item) => (
50
+ <a
51
+ key={item.href}
52
+ href={item.href}
53
+ data-prefetch="hover"
54
+ class={`flex items-center gap-3 px-3 py-1.5 text-sm transition-colors hover:text-sidebar-foreground ${
55
+ isActiveLink(item, currentPath)
56
+ ? "border-l-2 border-sidebar-primary font-medium text-sidebar-foreground"
57
+ : "border-l-2 border-transparent"
58
+ }`}
59
+ >
60
+ <Icon svg={item.iconSvg} />
61
+ <span>{item.title}</span>
62
+ </a>
63
+ ))}
64
+ </nav>
65
+ </div>
66
+ ))}
67
+ </div>
68
+ </aside>
69
+ );
70
+
71
+ const SearchForm = ({ query }: { query?: string }): JSX.Element => (
72
+ <form
73
+ method="get"
74
+ action="/search/"
75
+ class="flex w-full items-center"
76
+ role="search"
77
+ noValidate
78
+ >
79
+ <label htmlFor="site-search" class="sr-only">
80
+ Search pages
81
+ </label>
82
+ <input
83
+ id="site-search"
84
+ name="q"
85
+ type="search"
86
+ autoComplete="off"
87
+ spellcheck={false}
88
+ placeholder="Search..."
89
+ defaultValue={query ?? ""}
90
+ class="w-full border-b border-input bg-transparent px-1 py-1.5 text-sm placeholder:text-muted-foreground focus:border-foreground focus:outline-none transition-colors"
91
+ />
92
+ </form>
93
+ );
94
+
95
+ const TopNavbar = ({
96
+ query,
97
+ siteName,
98
+ }: {
99
+ query?: LayoutProps["searchQuery"];
100
+ siteName: LayoutProps["siteName"];
101
+ }): JSX.Element => (
102
+ <header class="sticky top-0 z-30 border-b border-border bg-background/80 backdrop-blur-sm">
103
+ <div class="mx-auto max-w-6xl px-8 py-3">
104
+ <div class="flex items-center gap-4">
105
+ <a
106
+ href="/"
107
+ class="text-sm font-mono font-medium tracking-tight md:hidden"
108
+ data-prefetch="hover"
109
+ >
110
+ <span class="text-muted-foreground">~/</span>
111
+ {siteName}
112
+ </a>
113
+ <div class="not-prose ml-auto w-full max-w-xs">
114
+ <SearchForm query={query} />
115
+ </div>
116
+ </div>
117
+ </div>
118
+ </header>
119
+ );
120
+
121
+ const buildHtmlClass = (
122
+ smoothScroll: LayoutProps["rightRail"]["smoothScroll"]
123
+ ): string => (smoothScroll ? "dark smooth-scroll" : "dark");
124
+
125
+ const buildScrollSpyDataset = (props: {
126
+ isScrollSpyEnabled: boolean;
127
+ rightRail: LayoutProps["rightRail"];
128
+ }): {
129
+ scrollspy?: string;
130
+ scrollspyCenter?: string;
131
+ scrollspyUpdateHash?: string;
132
+ } =>
133
+ props.isScrollSpyEnabled
134
+ ? {
135
+ scrollspy: "1",
136
+ scrollspyCenter: props.rightRail.scrollSpy.centerActiveItem
137
+ ? "1"
138
+ : undefined,
139
+ scrollspyUpdateHash: props.rightRail.scrollSpy.updateHash,
140
+ }
141
+ : {};
142
+
143
+ const Layout = ({
144
+ title,
145
+ siteName,
146
+ description,
147
+ canonicalUrl,
148
+ content,
149
+ cssPath,
150
+ inlineCss,
151
+ currentPath,
152
+ navigation,
153
+ scriptPaths = [],
154
+ searchQuery,
155
+ showRightRail = true,
156
+ rightRail,
157
+ tocItems,
158
+ }: LayoutProps): JSX.Element => {
159
+ const resolvedCssPath = inlineCss ? undefined : (cssPath ?? "/styles.css");
160
+ const shouldShowRightRail = showRightRail && rightRail.enabled;
161
+ const isScrollSpyEnabled =
162
+ shouldShowRightRail && rightRail.scrollSpy.enabled && tocItems.length > 0;
163
+ const scrollSpyDataset = buildScrollSpyDataset({
164
+ isScrollSpyEnabled,
165
+ rightRail,
166
+ });
167
+
168
+ return (
169
+ <html lang="en" class={buildHtmlClass(rightRail.smoothScroll)}>
170
+ <head>
171
+ <meta charset="utf-8" />
172
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
173
+ <title>{title}</title>
174
+ {description ? <meta name="description" content={description} /> : null}
175
+ {canonicalUrl ? <link rel="canonical" href={canonicalUrl} /> : null}
176
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
177
+ <link
178
+ rel="preconnect"
179
+ href="https://fonts.gstatic.com"
180
+ crossOrigin="anonymous"
181
+ />
182
+ <link
183
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap"
184
+ rel="stylesheet"
185
+ />
186
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
187
+ {inlineCss ? <style>{inlineCss}</style> : null}
188
+ {resolvedCssPath ? (
189
+ <link rel="stylesheet" href={resolvedCssPath} />
190
+ ) : null}
191
+ </head>
192
+ <body
193
+ class="bg-background font-sans text-foreground"
194
+ data-scrollspy={scrollSpyDataset.scrollspy}
195
+ data-scrollspy-center={scrollSpyDataset.scrollspyCenter}
196
+ data-scrollspy-update-hash={scrollSpyDataset.scrollspyUpdateHash}
197
+ >
198
+ <Sidebar
199
+ siteName={siteName}
200
+ navigation={navigation}
201
+ currentPath={currentPath}
202
+ />
203
+ <div class="main-wrapper">
204
+ <TopNavbar query={searchQuery} siteName={siteName} />
205
+ <main class="main-content">
206
+ <div class="mx-auto flex w-full max-w-6xl items-start gap-10">
207
+ <article
208
+ class={`prose min-w-0 flex-1${
209
+ currentPath === "/" ? " prose-home" : ""
210
+ }`}
211
+ dangerouslySetInnerHTML={{ __html: content }}
212
+ />
213
+ {shouldShowRightRail ? (
214
+ <RightRail
215
+ canonicalUrl={canonicalUrl}
216
+ currentPath={currentPath}
217
+ tocItems={tocItems}
218
+ rightRailConfig={rightRail}
219
+ />
220
+ ) : null}
221
+ </div>
222
+ </main>
223
+ <footer class="site-footer">
224
+ Built with Preact SSR + Tailwind &nbsp;|&nbsp; Zero JavaScript on
225
+ content pages
226
+ </footer>
227
+ </div>
228
+ {scriptPaths.map((scriptPath) => (
229
+ <script key={scriptPath} defer src={scriptPath} />
230
+ ))}
231
+ </body>
232
+ </html>
233
+ );
234
+ };
235
+
236
+ export const renderLayout = (props: LayoutProps): string =>
237
+ `<!DOCTYPE html>${render(<Layout {...props} />)}`;
@@ -0,0 +1,246 @@
1
+ import type { RightRailProps } from "idcmd/client";
2
+ import type { JSX } from "preact";
3
+
4
+ const CaretDownIcon = (): JSX.Element => (
5
+ <svg
6
+ width="16"
7
+ height="16"
8
+ viewBox="0 0 24 24"
9
+ fill="none"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ aria-hidden="true"
12
+ >
13
+ <path
14
+ d="M7 10l5 5 5-5"
15
+ stroke="currentColor"
16
+ stroke-width="2"
17
+ stroke-linecap="round"
18
+ stroke-linejoin="round"
19
+ />
20
+ </svg>
21
+ );
22
+
23
+ const CopyIcon = (): JSX.Element => (
24
+ <svg
25
+ width="18"
26
+ height="18"
27
+ viewBox="0 0 24 24"
28
+ fill="none"
29
+ xmlns="http://www.w3.org/2000/svg"
30
+ aria-hidden="true"
31
+ >
32
+ <path
33
+ d="M9 9h10v12H9V9z"
34
+ stroke="currentColor"
35
+ stroke-width="2"
36
+ stroke-linecap="round"
37
+ stroke-linejoin="round"
38
+ />
39
+ <path
40
+ d="M5 15H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1"
41
+ stroke="currentColor"
42
+ stroke-width="2"
43
+ stroke-linecap="round"
44
+ stroke-linejoin="round"
45
+ />
46
+ </svg>
47
+ );
48
+
49
+ const buildSlugFromCurrentPath = (currentPath: string): string => {
50
+ if (currentPath === "/") {
51
+ return "index";
52
+ }
53
+
54
+ const trimmed = currentPath.replaceAll(/^\/+|\/+$/g, "");
55
+ return trimmed || "index";
56
+ };
57
+
58
+ const buildAskUrls = ({
59
+ canonicalUrl,
60
+ currentPath,
61
+ }: {
62
+ canonicalUrl?: string;
63
+ currentPath: string;
64
+ }): { chatgptUrl: string; claudeUrl: string; markdownPath: string } => {
65
+ const slug = buildSlugFromCurrentPath(currentPath);
66
+ const markdownPath = `/${slug}.md`;
67
+ const markdownUrl = canonicalUrl
68
+ ? new URL(markdownPath, canonicalUrl).toString()
69
+ : markdownPath;
70
+
71
+ const llmsTxtUrl = canonicalUrl
72
+ ? new URL("/llms.txt", canonicalUrl).toString()
73
+ : "/llms.txt";
74
+
75
+ const prompt = `Investigate this document and explain it to the user: ${markdownUrl}\ndirectory for further exploration: ${llmsTxtUrl}`;
76
+
77
+ const chatgpt = new URL("https://chatgpt.com/");
78
+ chatgpt.searchParams.set("prompt", prompt);
79
+
80
+ const claude = new URL("https://claude.ai/new");
81
+ claude.searchParams.set("q", prompt);
82
+
83
+ return {
84
+ chatgptUrl: chatgpt.toString(),
85
+ claudeUrl: claude.toString(),
86
+ markdownPath,
87
+ };
88
+ };
89
+
90
+ const AskInDropdown = ({
91
+ claudeUrl,
92
+ chatgptUrl,
93
+ markdownPath,
94
+ }: {
95
+ claudeUrl: string;
96
+ chatgptUrl: string;
97
+ markdownPath: string;
98
+ }): JSX.Element => (
99
+ <details class="llm-menu relative">
100
+ <summary class="flex w-full cursor-pointer select-none items-center justify-between gap-3 rounded-full border border-white/20 bg-card/30 px-4 py-2 text-sm shadow-sm hover:border-white/30 hover:bg-card/40">
101
+ <span class="flex items-center gap-2">
102
+ <img
103
+ src="/openai-white.svg"
104
+ alt=""
105
+ width={18}
106
+ height={18}
107
+ class="shrink-0"
108
+ />
109
+ <span>Ask in ChatGPT</span>
110
+ </span>
111
+ <span class="text-muted-foreground">
112
+ <CaretDownIcon />
113
+ </span>
114
+ </summary>
115
+
116
+ <div class="absolute left-0 right-0 z-50 mt-2 rounded-xl border border-border bg-popover p-1 shadow-sm">
117
+ <a
118
+ href={chatgptUrl}
119
+ target="_blank"
120
+ rel="noopener noreferrer"
121
+ class="flex items-center gap-2 rounded-lg px-3 py-2 text-sm hover:bg-muted"
122
+ >
123
+ <img
124
+ src="/openai-white.svg"
125
+ alt=""
126
+ width={18}
127
+ height={18}
128
+ class="shrink-0"
129
+ />
130
+ <span>Ask in ChatGPT</span>
131
+ </a>
132
+ <a
133
+ href={claudeUrl}
134
+ target="_blank"
135
+ rel="noopener noreferrer"
136
+ class="flex items-center gap-2 rounded-lg px-3 py-2 text-sm hover:bg-muted"
137
+ >
138
+ <img
139
+ src="/anthropic-white.svg"
140
+ alt=""
141
+ width={18}
142
+ height={18}
143
+ class="shrink-0"
144
+ />
145
+ <span>Ask in Claude</span>
146
+ </a>
147
+ <a
148
+ href={markdownPath}
149
+ target="_blank"
150
+ rel="noopener noreferrer"
151
+ data-copy-markdown="1"
152
+ class="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm hover:bg-muted"
153
+ >
154
+ <span class="shrink-0 text-muted-foreground">
155
+ <CopyIcon />
156
+ </span>
157
+ <span data-copy-markdown-label="1">Copy Markdown to Clipboard</span>
158
+ </a>
159
+ </div>
160
+ </details>
161
+ );
162
+
163
+ const OnThisPage = ({
164
+ items,
165
+ }: {
166
+ items: RightRailProps["tocItems"];
167
+ }): JSX.Element => (
168
+ <section class="flex min-h-0 flex-1 flex-col" data-toc-root="1">
169
+ <div class="px-0.5 pb-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
170
+ On this page
171
+ </div>
172
+ <nav aria-label="Table of contents" class="min-h-0 flex flex-1 flex-col">
173
+ <div class="toc-scroll min-h-0 flex-1" data-toc-scroll-container="1">
174
+ <ul class="space-y-2 text-sm text-muted-foreground">
175
+ {items.map((item) => (
176
+ <li key={item.id} class={item.level >= 3 ? "pl-3" : ""}>
177
+ <a
178
+ href={`#${encodeURIComponent(item.id)}`}
179
+ class="hover:text-foreground"
180
+ data-toc-link="1"
181
+ >
182
+ {item.text}
183
+ </a>
184
+ </li>
185
+ ))}
186
+ </ul>
187
+ </div>
188
+ </nav>
189
+ </section>
190
+ );
191
+
192
+ const getVisibilityClass = (
193
+ visibleFrom: RightRailProps["rightRailConfig"]["visibleFrom"]
194
+ ): string => {
195
+ switch (visibleFrom) {
196
+ case "always": {
197
+ return "block";
198
+ }
199
+ case "never": {
200
+ return "hidden";
201
+ }
202
+ case "md": {
203
+ return "hidden md:block";
204
+ }
205
+ case "lg": {
206
+ return "hidden lg:block";
207
+ }
208
+ default: {
209
+ return "hidden xl:block";
210
+ }
211
+ }
212
+ };
213
+
214
+ const getPanelClass = (
215
+ placement: RightRailProps["rightRailConfig"]["placement"]
216
+ ): string =>
217
+ placement === "viewport"
218
+ ? "fixed top-24 bottom-0 right-8 z-20 w-64 flex flex-col gap-6 min-h-0"
219
+ : "sticky top-24 h-[calc(100vh-6rem)] flex flex-col gap-6 min-h-0";
220
+
221
+ export const RightRail = ({
222
+ canonicalUrl,
223
+ currentPath,
224
+ tocItems,
225
+ rightRailConfig,
226
+ }: RightRailProps): JSX.Element => {
227
+ const { chatgptUrl, claudeUrl, markdownPath } = buildAskUrls({
228
+ canonicalUrl,
229
+ currentPath,
230
+ });
231
+
232
+ return (
233
+ <aside
234
+ class={`${getVisibilityClass(rightRailConfig.visibleFrom)} w-64 shrink-0`}
235
+ >
236
+ <div class={getPanelClass(rightRailConfig.placement)}>
237
+ <AskInDropdown
238
+ chatgptUrl={chatgptUrl}
239
+ claudeUrl={claudeUrl}
240
+ markdownPath={markdownPath}
241
+ />
242
+ {tocItems.length > 0 ? <OnThisPage items={tocItems} /> : null}
243
+ </div>
244
+ </aside>
245
+ );
246
+ };
@@ -0,0 +1,87 @@
1
+ import type { SearchPageProps } from "idcmd/client";
2
+ import type { JSX } from "preact";
3
+
4
+ import { render as renderToString } from "preact-render-to-string";
5
+
6
+ const ResultItem = ({
7
+ result,
8
+ }: {
9
+ result: SearchPageProps["results"][number];
10
+ }): JSX.Element => (
11
+ <li class="rounded-md border border-border p-3">
12
+ <a
13
+ href={result.slug}
14
+ class="font-medium underline decoration-border underline-offset-4"
15
+ >
16
+ {result.title}
17
+ </a>
18
+ <p class="mt-1 text-sm text-muted-foreground">{result.description}</p>
19
+ </li>
20
+ );
21
+
22
+ const EmptyState = ({
23
+ minQueryLength,
24
+ topPages,
25
+ }: {
26
+ minQueryLength: number;
27
+ topPages: SearchPageProps["topPages"];
28
+ }): JSX.Element => (
29
+ <div class="text-sm text-muted-foreground">
30
+ <p>{`Type at least ${minQueryLength} characters to search.`}</p>
31
+ {topPages.length > 0 ? (
32
+ <>
33
+ <p class="mt-4 font-medium text-foreground">Popular pages</p>
34
+ <ul class="mt-2 space-y-1">
35
+ {topPages.map((page) => (
36
+ <li key={page.href}>
37
+ <a
38
+ href={page.href}
39
+ class="underline decoration-border underline-offset-4"
40
+ >
41
+ {page.title}
42
+ </a>
43
+ </li>
44
+ ))}
45
+ </ul>
46
+ </>
47
+ ) : null}
48
+ </div>
49
+ );
50
+
51
+ const SearchPage = ({
52
+ query,
53
+ minQueryLength,
54
+ results,
55
+ topPages,
56
+ }: SearchPageProps): JSX.Element => {
57
+ const trimmed = query.trim();
58
+ const showResults = trimmed.length >= minQueryLength;
59
+
60
+ return (
61
+ <section class="not-prose rounded-lg border border-border bg-card/30 p-4">
62
+ <h1 class="text-lg font-semibold">Search</h1>
63
+ {showResults ? (
64
+ <p class="mt-2 text-sm text-muted-foreground">
65
+ {results.length === 0
66
+ ? `No matches for "${trimmed}".`
67
+ : `Found ${results.length} result(s) for "${trimmed}".`}
68
+ </p>
69
+ ) : (
70
+ <div class="mt-2">
71
+ <EmptyState minQueryLength={minQueryLength} topPages={topPages} />
72
+ </div>
73
+ )}
74
+
75
+ {showResults ? (
76
+ <ul class="mt-4 space-y-2">
77
+ {results.map((result) => (
78
+ <ResultItem key={result.slug} result={result} />
79
+ ))}
80
+ </ul>
81
+ ) : null}
82
+ </section>
83
+ );
84
+ };
85
+
86
+ export const renderSearchPageContent = (props: SearchPageProps): string =>
87
+ renderToString(<SearchPage {...props} />);
@@ -2,7 +2,7 @@
2
2
  "compilerOptions": {
3
3
  "jsx": "react-jsx",
4
4
  "jsxImportSource": "preact",
5
- "lib": ["ESNext", "DOM"],
5
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
6
6
  "target": "ESNext",
7
7
  "module": "Preserve",
8
8
  "moduleDetection": "force",
@@ -1,2 +0,0 @@
1
- export { renderLayout } from "idcmd/client";
2
- export type { LayoutProps } from "idcmd/client";
@@ -1 +0,0 @@
1
- export { RightRail } from "idcmd/client";
@@ -1 +0,0 @@
1
- export { renderSearchPageContent } from "idcmd/client";