@immediately-run/grove 0.1.1

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 (92) hide show
  1. package/README.md +122 -0
  2. package/docs/ENGINE_BOUNDARY.md +233 -0
  3. package/llms.txt +115 -0
  4. package/package.json +82 -0
  5. package/src/App.tsx +131 -0
  6. package/src/GroveApp.css +2774 -0
  7. package/src/GroveWiki.tsx +386 -0
  8. package/src/components/AssetImage.tsx +58 -0
  9. package/src/components/Backlinks.tsx +104 -0
  10. package/src/components/Callout.tsx +26 -0
  11. package/src/components/ChildPages.tsx +40 -0
  12. package/src/components/DefaultLayout.tsx +27 -0
  13. package/src/components/Directory.tsx +65 -0
  14. package/src/components/DirectoryList.test.tsx +275 -0
  15. package/src/components/DirectoryList.tsx +189 -0
  16. package/src/components/DirectoryView.tsx +68 -0
  17. package/src/components/DocList.tsx +109 -0
  18. package/src/components/DocsByTag.tsx +13 -0
  19. package/src/components/Drawer.tsx +45 -0
  20. package/src/components/EntryHeader.tsx +51 -0
  21. package/src/components/FamilyTree.tsx +95 -0
  22. package/src/components/GroveAgent.tsx +264 -0
  23. package/src/components/GroveFooter.tsx +20 -0
  24. package/src/components/GroveNav.tsx +102 -0
  25. package/src/components/Icon.tsx +56 -0
  26. package/src/components/Infobox.tsx +19 -0
  27. package/src/components/Kbd.tsx +10 -0
  28. package/src/components/KeyValue.tsx +32 -0
  29. package/src/components/Lede.tsx +6 -0
  30. package/src/components/More.tsx +10 -0
  31. package/src/components/Outlet.tsx +11 -0
  32. package/src/components/PageMeta.tsx +24 -0
  33. package/src/components/PageView.tsx +98 -0
  34. package/src/components/Quote.tsx +36 -0
  35. package/src/components/RecentlyUpdated.tsx +6 -0
  36. package/src/components/SafeEntryBody.tsx +72 -0
  37. package/src/components/SafeLayout.tsx +35 -0
  38. package/src/components/ScrollToFragment.tsx +63 -0
  39. package/src/components/Search.tsx +127 -0
  40. package/src/components/Sidebar.tsx +118 -0
  41. package/src/components/TableOfContents.test.tsx +163 -0
  42. package/src/components/TableOfContents.tsx +101 -0
  43. package/src/components/TagCloud.tsx +46 -0
  44. package/src/components/TagList.tsx +31 -0
  45. package/src/components/Timeline.tsx +55 -0
  46. package/src/components/Toc.tsx +14 -0
  47. package/src/components/WikiLink.tsx +112 -0
  48. package/src/data/themes.ts +14 -0
  49. package/src/devfs.d.ts +4 -0
  50. package/src/hooks/useContentComponents.ts +122 -0
  51. package/src/hooks/useCorpusMetadata.ts +43 -0
  52. package/src/hooks/useDirectoryListing.ts +56 -0
  53. package/src/hooks/useHeadings.ts +96 -0
  54. package/src/hooks/useOpenWikiBoot.ts +95 -0
  55. package/src/index.css +120 -0
  56. package/src/lib/compose.test.ts +92 -0
  57. package/src/lib/compose.ts +99 -0
  58. package/src/lib/content.test.ts +269 -0
  59. package/src/lib/content.ts +267 -0
  60. package/src/lib/contentRoot.ts +61 -0
  61. package/src/lib/corpusComponents.test.ts +101 -0
  62. package/src/lib/corpusComponents.ts +117 -0
  63. package/src/lib/corpusScan.test.ts +157 -0
  64. package/src/lib/corpusScan.ts +105 -0
  65. package/src/lib/directory.test.ts +216 -0
  66. package/src/lib/directory.ts +262 -0
  67. package/src/lib/fragment.test.ts +88 -0
  68. package/src/lib/fragment.ts +55 -0
  69. package/src/lib/frontmatter.ts +26 -0
  70. package/src/lib/layout.ts +84 -0
  71. package/src/lib/openWiki.test.ts +216 -0
  72. package/src/lib/openWiki.ts +84 -0
  73. package/src/lib/queries.test.ts +74 -0
  74. package/src/lib/queries.ts +84 -0
  75. package/src/lib/safeIntrinsics.test.tsx +99 -0
  76. package/src/lib/safeIntrinsics.tsx +77 -0
  77. package/src/lib/safeRender.test.ts +359 -0
  78. package/src/lib/safeSources.ts +25 -0
  79. package/src/lib/shell.ts +71 -0
  80. package/src/lib/sourceCache.test.ts +66 -0
  81. package/src/lib/sourceCache.ts +42 -0
  82. package/src/lib/tocScroll.test.ts +71 -0
  83. package/src/lib/tocScroll.ts +93 -0
  84. package/src/lib/wiki.test.ts +194 -0
  85. package/src/lib/wiki.ts +175 -0
  86. package/src/lib.ts +54 -0
  87. package/src/main.tsx +19 -0
  88. package/src/mdx.d.ts +9 -0
  89. package/src/mdxComponents.ts +89 -0
  90. package/src/test/setup.ts +19 -0
  91. package/viewer-manifest.schema.json +62 -0
  92. package/viewer.manifest.json +267 -0
package/src/App.tsx ADDED
@@ -0,0 +1,131 @@
1
+ // The entry point immediately.run renders — deliberately a GATE, not the wiki.
2
+ //
3
+ // Grove ships in two packagings and they disagree about two things: where the corpus is,
4
+ // and who knows what is in it.
5
+ //
6
+ // • FORK — `/app/content/`, indexed by the bundler at build time. Both answers
7
+ // arrive before the app boots, so the gate falls straight through.
8
+ // • DISPATCH — a host-minted chroot at someone else's directory (R3-169), which the
9
+ // bundler never scanned, so the frontmatter index has to be BUILT
10
+ // (R3-265). Neither answer exists at first render.
11
+ //
12
+ // Every helper the wiki is made of — which keys are entries, what their hrefs are, which
13
+ // layouts wrap them — is a function of the root; every surface a reader sees — nav,
14
+ // sidebar, search, backlinks, routing, the 404 index — is a function of the index. A
15
+ // component that rendered before either was settled would show the VIEWER's corpus, or an
16
+ // empty one, and then not correct itself. Both are outcomes dispatch may never produce.
17
+ //
18
+ // Hence the split: this file resolves, `GroveWiki` renders.
19
+
20
+ import { use, useEffect, useRef } from 'react';
21
+ import * as sdk from '@immediately-run/sdk';
22
+ import { sendMessage } from '@immediately-run/sdk/sandboxUtils';
23
+ import { MetadataSource } from '@immediately-run/sdk';
24
+ import { TinkerableContext } from '@immediately-run/sdk/TinkerableContext';
25
+ import { MDXProvider } from '@immediately-run/sdk/MDXProvider';
26
+ import { useOpenWikiBoot } from './hooks/useOpenWikiBoot';
27
+ import { useCorpusMetadata } from './hooks/useCorpusMetadata';
28
+ import { useContentComponents } from './hooks/useContentComponents';
29
+ import { getContentRoot } from './lib/contentRoot';
30
+ import { viewedDocumentForTarget } from './lib/content';
31
+ import GroveWiki from './GroveWiki';
32
+
33
+ // R3-268 — the viewed-document rule, registered ONCE at module load: every
34
+ // `navigate()`/`<Link>` navigation declares which file the destination renders,
35
+ // so the host's file explorer can highlight it. The mapping is
36
+ // `viewedDocumentForTarget` (lib/content): repo-relative for a fork,
37
+ // CORPUS-relative under dispatch — the host joins its chroot prefix, since the
38
+ // corpus's repo-side location is host knowledge this app cannot see. Resolved
39
+ // PER NAVIGATION, not captured: the content root is a function of dispatch
40
+ // state (see `contentRoot`). Guarded with `?.` so Grove still runs against an
41
+ // SDK generation without the hook (the host then falls back to the URL
42
+ // convention, which under dispatch simply yields no highlight). Highlight-only
43
+ // by contract — this never scrolls, never moves focus, never opens the editor.
44
+ (sdk as { setViewedDocumentResolver?: (r: (href: string) => string | null | undefined) => void })
45
+ .setViewedDocumentResolver?.(viewedDocumentForTarget);
46
+
47
+ export default function App() {
48
+ const host = use(TinkerableContext);
49
+ const boot = useOpenWikiBoot();
50
+ // R3-268 deep links: declare the INITIALLY rendered document once at boot. A
51
+ // fresh page load performs no navigation, so without this the host records no
52
+ // viewed document for a deep link and the explorer shows no highlight (nor
53
+ // ancestor dot) until the first in-app click. Carries no gesture: the host
54
+ // records it userInitiated=false — highlight-only, never a reveal. Waits for
55
+ // the boot resolution because the content root (and so the mapping) is a
56
+ // function of dispatch state. Fire-and-forget: no transport (plain `vite dev`)
57
+ // must not crash the wiki.
58
+ const declaredBootRef = useRef(false);
59
+ const outerHref = host?.outerHref;
60
+ useEffect(() => {
61
+ if (declaredBootRef.current) return;
62
+ if (boot.status !== 'ready' && boot.status !== 'fork') return;
63
+ if (!outerHref) return;
64
+ declaredBootRef.current = true;
65
+ try {
66
+ sendMessage('viewed-document-declare', { value: viewedDocumentForTarget(outerHref) });
67
+ } catch {
68
+ /* no host transport — a standalone dev server render */
69
+ }
70
+ }, [boot.status, outerHref]);
71
+ // Only a dispatched viewer scans; a fork's index is already in the context.
72
+ const corpus = useCorpusMetadata(boot.status === 'ready' ? getContentRoot() : null);
73
+ // BOTH packagings, deliberately (R3-174). A corpus's own component vocabulary must not
74
+ // depend on how it was composed — `PLATFORM_LAYERING_SPEC` §1.1's mode-invariance rule —
75
+ // so a fork reads its marker too; that is one cheap open of a file already in `/app`.
76
+ // Gated on the boot status for the same reason the scan is: before the delegation
77
+ // resolves, `getContentRoot()` is still the fork default, and reading THAT marker under
78
+ // dispatch would register the viewer's own sample-corpus components against someone
79
+ // else's content.
80
+ const contentComponents = useContentComponents(
81
+ boot.status === 'ready' || boot.status === 'fork' ? getContentRoot() : null,
82
+ );
83
+
84
+ if (boot.status === 'failed') {
85
+ return (
86
+ <div className="grove-boot">
87
+ <p className="grove-boot__msg">{boot.message}</p>
88
+ </div>
89
+ );
90
+ }
91
+
92
+ // The provider must be COMPLETE before content paints (MDX_FROM_MOUNT_SPEC §2's
93
+ // invariant): rendering into a half-composed map would flash a missing-component error
94
+ // for `<RoadmapBoard>` until registration landed — the very error content components
95
+ // exist to remove — and a nested provider patched in afterwards would do the same.
96
+ // Holding here costs nothing, because the gate already exists for the corpus scan.
97
+ if (boot.status === 'waiting' || corpus.status === 'scanning' || contentComponents.status === 'loading') {
98
+ return (
99
+ <div className="grove-boot">
100
+ <p className="grove-boot__msg">Opening…</p>
101
+ </div>
102
+ );
103
+ }
104
+
105
+ // Corpus-declared components (R3-174) go on as a nested provider, which the SDK's
106
+ // `useMDXComponents` resolves as `{...stock, ...content}` — so CONTENT WINS a name
107
+ // clash, and a wiki may override even `<DocsByTag>` with its own (value 4, max
108
+ // hackability). §2's "single-map merge, never provider nesting" was written against
109
+ // patching a provider in AFTER a partial render; nesting it INSIDE the gate above is
110
+ // that same merge, composed before anything paints.
111
+ const wiki = <GroveWiki readOnly={boot.readOnly} rejectedComponents={contentComponents.rejected} />;
112
+ const withComponents =
113
+ contentComponents.status === 'ready' && Object.keys(contentComponents.components).length > 0 ? (
114
+ <MDXProvider components={contentComponents.components}>{wiki}</MDXProvider>
115
+ ) : (
116
+ wiki
117
+ );
118
+
119
+ if (corpus.status === 'ready' && corpus.metadata) {
120
+ // Provide the scanned corpus as the metadata SOURCE through the supported
121
+ // surface (R3-276), not a wholesale TinkerableContext re-provision: the
122
+ // platform stays free to grow its own state, and the hooks read the nearest
123
+ // MetadataSource — so every consumer works unchanged, and nothing re-states
124
+ // host fields it does not own.
125
+ return (
126
+ <MetadataSource value={corpus.metadata}>{withComponents}</MetadataSource>
127
+ );
128
+ }
129
+
130
+ return withComponents;
131
+ }