diff --git a/packages/diffs/README.md b/packages/diffs/README.md index cc61b819c..bc3717423 100644 --- a/packages/diffs/README.md +++ b/packages/diffs/README.md @@ -27,6 +27,15 @@ JavaScript and React components. pnpm add @pierre/diffs ``` +## Agent skill + +Install the agent skill for this package with the +[Skills CLI](https://skills.sh/docs/cli): + +```bash +npx skills add pierrecomputer/pierre --skill diffs +``` + ## Development We use pnpm for workspace package management and Bun for tests. diff --git a/packages/theme/README.md b/packages/theme/README.md index 2e51b77e5..5123feae0 100644 --- a/packages/theme/README.md +++ b/packages/theme/README.md @@ -45,6 +45,15 @@ Usage: ``` +## Agent skill + +Install the agent skill for this package with the +[Skills CLI](https://skills.sh/docs/cli): + +```bash +npx skills add pierrecomputer/pierre --skill theme +``` + Contributing: - Development, build, and testing: [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/packages/theming/README.md b/packages/theming/README.md index 5457578b6..7fc683c1e 100644 --- a/packages/theming/README.md +++ b/packages/theming/README.md @@ -68,6 +68,15 @@ pnpm add @pierre/theming Add the optional peers only for the entries you use, e.g., `react` for `/react`, and `@pierre/theme`, `@shikijs/themes`, and `shiki` for `/themes`. +## Agent skill + +Install the agent skill for this package with the +[Skills CLI](https://skills.sh/docs/cli): + +```bash +npx skills add pierrecomputer/pierre --skill theming +``` + ## Collections and catalogs The core entry exports two closely-related composition primitives: diff --git a/packages/trees/README.md b/packages/trees/README.md index f062b8185..bbd70c544 100644 --- a/packages/trees/README.md +++ b/packages/trees/README.md @@ -19,6 +19,15 @@ path strings, not internal numeric IDs. pnpm add @pierre/trees ``` +## Agent skill + +Install the agent skill for this package with the +[Skills CLI](https://skills.sh/docs/cli): + +```bash +npx skills add pierrecomputer/pierre --skill trees +``` + ## Vanilla usage ```ts diff --git a/skills/INDEX.md b/skills/INDEX.md new file mode 100644 index 000000000..a84aa54b8 --- /dev/null +++ b/skills/INDEX.md @@ -0,0 +1,18 @@ +# Pierre package skills + +Use these skills when an app or site consumes a Pierre package. Read the +selected `SKILL.md` first. Load only the API or recipe reference that matches +the task. + +| Skill | Use it for | +| --------------------------- | --------------------------------------------------------------------------------------------------- | +| [diffs](diffs/SKILL.md) | Render or edit code files, diffs, patches, and merge conflicts with `@pierre/diffs`. | +| [theme](theme/SKILL.md) | Select and load a Pierre syntax or editor theme from `@pierre/theme`. | +| [theming](theming/SKILL.md) | Build runtime theme collections, resolvers, controllers, and color mappings with `@pierre/theming`. | +| [trees](trees/SKILL.md) | Render and control an interactive file tree with `@pierre/trees`. | + +## Package relationship + +`@pierre/theme` supplies theme objects. `@pierre/theming` selects, resolves, and +maps those objects. `@pierre/diffs` and `@pierre/trees` render code and file +trees with the selected theme. diff --git a/skills/diffs/SKILL.md b/skills/diffs/SKILL.md new file mode 100644 index 000000000..25fd3e0a7 --- /dev/null +++ b/skills/diffs/SKILL.md @@ -0,0 +1,48 @@ +--- +name: diffs +description: + Use when an app uses @pierre/diffs to render or edit code files, diffs, + patches, merge conflicts, or CodeView review surfaces, including React, + vanilla JavaScript, SSR, workers, annotations, selection, and custom Shiki + languages or themes. +--- + +# `@pierre/diffs` + +Use `@pierre/diffs` to render syntax-highlighted files and diffs. Use its +optional editor, SSR, and worker entries for those capabilities. + +## Install + +```bash +pnpm add @pierre/diffs +``` + +Install `react` and `react-dom` when the app uses the React entry. + +## Select an API reference + +| Surface | Reference | +| ------------------------------------------------------------ | ------------------------------------------------------ | +| Root components, parsing, and file extension APIs | [Core API](references/api-core.md) | +| Languages, themes, highlighter state, and streams | [Highlighting API](references/api-highlighting.md) | +| Renderers, managers, DOM helpers, comparisons, and constants | [Low-level rendering API](references/api-rendering.md) | +| Shared data, option, render, selection, and editor types | [Shared types](references/api-types.md) | +| `@pierre/diffs/react` | [React API](references/api-react.md) | +| `@pierre/diffs/editor` | [Editor API](references/api-editor.md) | +| `@pierre/diffs/ssr` | [SSR API](references/api-ssr.md) | +| `@pierre/diffs/worker` and worker scripts | [Worker API](references/api-worker.md) | + +## Select a recipe + +| Task | Recipe | +| ----------------------------------- | ------------------------------------------------------------------------ | +| Render a file or diff in React | [Render with React](references/recipe-react.md) | +| Render a file or diff without React | [Render with vanilla JavaScript](references/recipe-vanilla.md) | +| Build a virtualized review surface | [Use CodeView](references/recipe-code-view.md) | +| Edit a React surface or CodeView | [Edit with React](references/recipe-edit-react.md) | +| Edit a vanilla surface or CodeView | [Edit with vanilla JavaScript](references/recipe-edit-vanilla.md) | +| Preload markup on the server | [Use SSR](references/recipe-ssr.md) | +| Highlight through a worker pool | [Use workers](references/recipe-workers.md) | +| Add line annotations and selection | [Add annotations and selection](references/recipe-annotations.md) | +| Register a Shiki language or theme | [Register custom highlighting](references/recipe-custom-highlighting.md) | diff --git a/skills/diffs/references/api-core.md b/skills/diffs/references/api-core.md new file mode 100644 index 000000000..22b012862 --- /dev/null +++ b/skills/diffs/references/api-core.md @@ -0,0 +1,186 @@ +# Core API + +This reference covers components, parsing, merge conflicts, and file extension +APIs from `@pierre/diffs`. + +## Contents + +- [File components](#file-components) +- [Virtualized components](#virtualized-components) +- [`File` members](#file-members) +- [`FileDiff` members](#filediff-members) +- [`CodeView` members](#codeview-members) +- [Parsing and patch APIs](#parsing-and-patch-apis) +- [Merge conflict APIs](#merge-conflict-apis) +- [Annotation APIs](#annotation-apis) +- [File extension APIs](#file-extension-apis) + +## File components + +| Export | Kind | Purpose | +| --------------------------------------- | -------- | ------------------------------------------------------------- | +| `File` | Class | Renders one syntax-highlighted file. | +| `FileOptions` | Type | Configures file display, interaction, slots, and callbacks. | +| `FileRenderProps` | Type | Defines file input and a render target. | +| `FileHydrateProps` | Type | Defines file input and preloaded markup for hydration. | +| `FileDiff` | Class | Renders a file diff from files or parsed metadata. | +| `FileDiffOptions` | Type | Configures diff display, interaction, slots, and callbacks. | +| `FileDiffRenderBaseProps` | Type | Defines shared diff render input. | +| `FileDiffRenderProps` | Type | Adds files or parsed metadata to diff render input. | +| `FileDiffHydrationProps` | Type | Defines diff input and preloaded markup for hydration. | +| `FileDiffType` | Type | Identifies a standard or unresolved diff instance. | +| `UnresolvedFile` | Class | Renders one file with merge conflict controls. | +| `UnresolvedFileOptions` | Type | Configures merge conflict display and callbacks. | +| `UnresolvedFileRenderProps` | Type | Defines merge conflict render input. | +| `UnresolvedFileHydrationProps` | Type | Defines merge conflict input for hydration. | +| `MergeConflictActionsTypeOption` | Type | Selects no actions, default actions, or a custom renderer. | +| `RenderMergeConflictActions` | Type | Defines a vanilla conflict action renderer. | +| `getUnresolvedDiffHunksRendererOptions` | Function | Converts unresolved-file options to hunk renderer options. | +| `CodeView` | Class | Renders a virtualized list of files and diffs. | +| `CodeViewOptions` | Type | Configures list layout, items, slots, selection, and editing. | +| `CodeViewLineSelection` | Type | Associates a selected range with one item ID. | +| `CodeViewRenderedFileItem` | Type | Describes one mounted file item. | +| `CodeViewRenderedDiffItem` | Type | Describes one mounted diff item. | +| `CodeViewRenderedItem` | Type | Represents a mounted file or diff item. | +| `CodeViewCoordinator` | Type | Coordinates React slots with the vanilla list. | +| `CodeViewSlotSnapshot` | Type | Describes mounted items and list header or footer hosts. | +| `CodeViewScrollListener` | Type | Receives list scroll changes. | +| `CODE_VIEW_FILE_OPTION_KEYS` | Value | Lists file options that `CodeView` passes to an item. | +| `CODE_VIEW_DIFF_OPTION_KEYS` | Value | Lists diff options that `CodeView` passes to an item. | + +## Virtualized components + +| Export | Kind | Purpose | +| -------------------------------------------------- | ----- | ----------------------------------------------------------- | +| `Virtualizer` | Class | Tracks a simple viewport and connected render instances. | +| `VirtualizerConfig` | Type | Configures overscroll, observation margin, and resize logs. | +| `VirtualizedFile` | Class | Adds simple viewport behavior to `File`. | +| `VirtualizedFileDiff` | Class | Adds simple viewport behavior to `FileDiff`. | +| `VIRTUALIZED_FILE_DIFF_LAYOUT_CHECKPOINT_INTERVAL` | Value | Sets the line interval for virtual diff layout checkpoints. | + +## `File` members + +| Member | Purpose | +| --------------------------------------------- | ---------------------------------------------------- | +| `new File(options?, workerManager?)` | Creates one file renderer. | +| `render(props)` | Renders file contents. | +| `hydrate(props)` | Attaches to preloaded file markup. | +| `rerender()` | Renders the current file again. | +| `setOptions(options)` | Replaces file options. | +| `setThemeType(themeType)` | Selects system, light, or dark theme mode. | +| `onThemeChange()` | Applies a changed theme. | +| `setLineAnnotations(annotations)` | Replaces file annotations. | +| `setSelectedLines(range, options?)` | Replaces the selected line range. | +| `setEditorActiveLine(line, options?)` | Marks the editor's active line. | +| `getHoveredLine()` | Gets the current hovered line. | +| `getOrCreateLineCache(file?)` | Gets cached source lines. | +| `attachEditor(editor)` | Attaches an editor and returns a detach function. | +| `applyDocumentChange(document, annotations?)` | Applies an editor document update. | +| `updateRenderCache(tokens, themeType)` | Updates highlighted token cache entries. | +| `primeHighlightCache()` | Preloads the highlighted file result. | +| `renderPlaceholder(height)` | Renders a fixed-height placeholder. | +| `virtualizedSetup()` | Prepares the instance for a virtualizer. | +| `flushManagers()` | Applies deferred interaction and size manager state. | +| `cleanUp(recycle?)` | Releases rendered resources. | + +## `FileDiff` members + +`FileDiff` supports the shared `File` update, selection, annotation, editor, +hydration, placeholder, and cleanup members with diff data. + +| Member | Purpose | +| -------------------------------------------- | ------------------------------------------------ | +| `new FileDiff(options?, workerManager?)` | Creates one diff renderer. | +| `render(props)` | Parses or renders diff input. | +| `hydrate(props)` | Attaches to preloaded diff markup. | +| `rerender()` | Renders the current diff again. | +| `setOptions(options)` | Replaces diff options. | +| `getLineIndex(line, side?)` | Maps a displayed line to row and column indexes. | +| `handleExpandHunk(index, direction, count?)` | Handles a hunk expansion request. | +| `expandHunk(index, direction, count?)` | Expands hidden context around one hunk. | +| `completeEditSession()` | Recomputes diff metadata after an edit session. | +| `isLineRenderable(line)` | Tests whether an additions line is visible. | +| `getNearestRenderableLine(line, direction)` | Finds a visible additions line. | +| `revealLine(line)` | Expands context to show an additions line. | +| `primeHighlightCache(diff?)` | Preloads the highlighted diff result. | + +## `CodeView` members + +| Member | Purpose | +| ---------------------------------------- | -------------------------------------------- | +| `new CodeView(options?, workerManager?)` | Creates one virtualized list. | +| `setup(root)` | Attaches the list to its scroll root. | +| `setItems(items)` | Replaces all items. | +| `addItem(item)` | Appends one item. | +| `addItems(items)` | Appends several items. | +| `getItem(id)` | Gets one item by ID. | +| `updateItem(item)` | Replaces one item by ID. | +| `updateItemId(oldId, newId)` | Changes one item ID. | +| `getEditor(id)` | Gets the active editor for an item. | +| `scrollTo(target)` | Scrolls to a position, item, line, or range. | +| `setSelectedLines(selection, options?)` | Sets the selected item and range. | +| `getSelectedLines()` | Gets the selected item and range. | +| `clearSelectedLines(options?)` | Clears the selected lines. | +| `setOptions(options)` | Replaces list options. | +| `onThemeChange()` | Applies a changed theme to list items. | +| `render(immediate?)` | Schedules or performs a render. | +| `getWindowSpecs()` | Gets the current virtual window. | +| `getContainerElement()` | Gets the scroll content element. | +| `getHeaderElement()` | Gets the list header host. | +| `getFooterElement()` | Gets the list footer host. | +| `getRenderedItems()` | Gets mounted items. | +| `setSlotCoordinator(coordinator?)` | Sets the external slot coordinator. | +| `getSlotSnapshot(coordinator)` | Gets the coordinator's mounted slot state. | +| `subscribeToScroll(listener)` | Subscribes to scroll changes. | +| `getLocalTopForInstance(instance)` | Gets an instance offset inside the list. | +| `getTopForItem(id)` | Gets an item offset inside the list. | +| `instanceChanged(instance, layoutDirty)` | Reports a render instance change. | +| `reset()` | Clears items and render state. | +| `cleanUp()` | Releases list resources. | + +## Parsing and patch APIs + +| Export | Purpose | +| ---------------------------- | ----------------------------------------------------- | +| `parseDiffFromFile` | Creates `FileDiffMetadata` from old and new files. | +| `parsePatchFiles` | Parses a patch string into file diff metadata. | +| `processPatch` | Parses one patch section. | +| `processFile` | Converts one parsed patch file to `FileDiffMetadata`. | +| `getSingularPatch` | Selects one file patch from a patch string. | +| `trimPatchContext` | Limits unchanged context in patch text. | +| `hydratePartialDiff` | Adds loaded file contents to partial diff metadata. | +| `cloneFileDiffMetadata` | Creates a structural copy of diff metadata. | +| `cleanLastNewline` | Normalizes the final newline for diff input. | +| `getLineEndingType` | Detects a file's line-ending sequence. | +| `getTotalLineCountFromHunks` | Counts rendered rows across hunks. | +| `parseLineType` | Parses one patch line marker and content. | +| `ParsedLine` | Describes the result from `parseLineType`. | + +## Merge conflict APIs + +| Export | Purpose | +| ---------------------- | --------------------------------------------------------------- | +| `resolveConflict` | Applies one current, incoming, or combined conflict resolution. | +| `resolveRegion` | Resolves one parsed merge conflict region. | +| `diffAcceptRejectHunk` | Applies accept or reject behavior to one change hunk. | + +## Annotation APIs + +| Export | Purpose | +| ---------------------------- | ---------------------------------------------------------- | +| `isFileAnnotation` | Tests whether one annotation targets a file line. | +| `isDiffAnnotation` | Tests whether one annotation targets a diff side and line. | +| `isFileAnnotationCollection` | Tests whether an array contains file annotations. | +| `isDiffAnnotationCollection` | Tests whether an array contains diff annotations. | + +## File extension APIs + +| Export | Purpose | +| ---------------------------- | ------------------------------------------------ | +| `getFiletypeFromFileName` | Infers a language from a file name. | +| `setCustomExtension` | Maps one file name or extension to a language. | +| `replaceCustomExtensions` | Replaces all custom mappings. | +| `getCustomExtensionsMap` | Gets a copy of custom mappings. | +| `getCustomExtensionsVersion` | Gets the mapping revision. | +| `EXTENSION_TO_FILE_FORMAT` | Maps built-in extensions and names to languages. | +| `setLanguageOverride` | Assigns a language to parsed diff files. | diff --git a/skills/diffs/references/api-editor.md b/skills/diffs/references/api-editor.md new file mode 100644 index 000000000..0a139d7b2 --- /dev/null +++ b/skills/diffs/references/api-editor.md @@ -0,0 +1,94 @@ +# Editor API + +This reference lists every export from `@pierre/diffs/editor` and every public +member of its classes. + +## Exports + +| Export | Kind | Purpose | +| --------------------- | ----- | --------------------------------------------------------- | +| `Editor` | Class | Adds text editing to a `File` or `FileDiff` instance. | +| `EditorOptions` | Type | Configures history, state, selections, and callbacks. | +| `TextDocument` | Class | Stores text, positions, edits, search, and undo history. | +| `TextDocumentChange` | Type | Describes the lines and characters changed by an edit. | +| `ResolvedTextEdit` | Type | Describes an edit with absolute offsets. | +| `IStateStorage` | Type | Defines asynchronous or synchronous editor state storage. | +| `PersistStateStorage` | Type | Selects memory, IndexedDB, or custom state storage. | +| `Position` | Type | Identifies a zero-based line and character. | +| `Range` | Type | Identifies a start and end position. | +| `TextEdit` | Type | Replaces one range with new text. | + +## `EditorOptions` fields + +| Field | Purpose | +| ------------------------ | -------------------------------------------------------- | +| `historyMaxEntries` | Limits the undo stack. | +| `persistState` | Keeps editor state for each file cache key. | +| `persistStateStorage` | Selects the state store. | +| `roundedSelection` | Controls rounded selection corners. | +| `matchBrackets` | Controls matching-bracket highlights. | +| `autoSurround` | Controls quote and bracket insertion around a selection. | +| `languageCommentConfig` | Overrides comment tokens by language. | +| `enabledSelectionAction` | Enables the selection action surface. | +| `clipboard` | Supplies a text clipboard reader. | +| `renderSelectionAction` | Produces the selection action element. | +| `onAttach` | Receives the editor and attached surface. | +| `onChange` | Receives changed file contents and line annotations. | +| `onFocus` | Runs after the editor gains focus. | +| `onBlur` | Runs after the editor loses focus. | + +## `Editor` members + +| Member | Purpose | +| ----------------------------------- | --------------------------------------------------------- | +| `new Editor(options?)` | Creates one editor. | +| `edit(instance)` | Attaches to a file or diff and returns a detach function. | +| `setOptions(options)` | Replaces editor options. | +| `applyEdits(edits, updateHistory?)` | Applies programmatic text edits. | +| `canUndo` | Reports whether undo has an entry. | +| `canRedo` | Reports whether redo has an entry. | +| `undo()` | Reverts the latest edit. | +| `redo()` | Reapplies the latest reverted edit. | +| `getFile()` | Gets the current file contents. | +| `getText()` | Gets the current text. | +| `getState()` | Gets selections and view state. | +| `setState(state)` | Sets selections and view state. | +| `setSelections(selections)` | Sets directed selection ranges. | +| `setMarkers(markers)` | Sets diagnostic markers. | +| `focus(options?)` | Focuses the editor. | +| `blur()` | Removes editor focus. | +| `cleanUp(recycle?)` | Releases editor resources. | + +## `TextDocument` members + +| Member | Purpose | +| ---------------------------------------------------- | ----------------------------------------------------- | +| `new TextDocument(uri, text, languageId?, version?)` | Creates a text document. | +| `uri` | Gets the document identifier. | +| `languageId` | Gets the language identifier. | +| `version` | Gets the document version. | +| `lineCount` | Gets the line count. | +| `eol` | Gets the line-ending sequence. | +| `canUndo` | Reports whether undo has an entry. | +| `canRedo` | Reports whether redo has an entry. | +| `positionAt(offset)` | Converts an offset to a position. | +| `positionsAt(offsets)` | Converts several offsets to positions. | +| `offsetAt(position)` | Converts a position to an offset. | +| `getText(range?)` | Gets all text or one range. | +| `getLineText(line, includeLineBreak?)` | Gets one line. | +| `normalizeEol(text)` | Converts text to the document line ending. | +| `getLineLength(line, includeLineBreak?)` | Gets one line length. | +| `charAt(offsetOrPosition)` | Gets one character. | +| `getTextSlice(start, end)` | Gets text between two offsets. | +| `findNextNonOverlappingSubstring(needle, occupied)` | Finds an unused substring range. | +| `search(params)` | Finds text ranges. | +| `applyEdits(edits, ...)` | Resolves and applies position-based edits. | +| `resolveEdits(edits)` | Converts position-based edits to offset edits. | +| `applyResolvedEdits(edits, ...)` | Applies offset-based edits. | +| `setLastUndoSelectionsAfter(selections)` | Associates selections with the latest history entry. | +| `setLastUndoLineAnnotations(before, after)` | Associates annotations with the latest history entry. | +| `undo()` | Reverts one document history entry. | +| `redo()` | Reapplies one document history entry. | +| `normalizePosition(position)` | Clamps a position to the document. | + +`IStateStorage` has `get(cacheKey)` and `set(cacheKey, state)` methods. diff --git a/skills/diffs/references/api-highlighting.md b/skills/diffs/references/api-highlighting.md new file mode 100644 index 000000000..861d8884f --- /dev/null +++ b/skills/diffs/references/api-highlighting.md @@ -0,0 +1,113 @@ +# Highlighting API + +This reference lists every language, theme, shared highlighter, and stream +export from `@pierre/diffs`. + +## Contents + +- [Shiki passthrough APIs](#shiki-passthrough-apis) +- [Language APIs](#language-apis) +- [Theme APIs](#theme-apis) +- [Shared highlighter APIs](#shared-highlighter-apis) +- [Render APIs](#render-apis) +- [Stream APIs](#stream-apis) + +## Shiki passthrough APIs + +| Export | Kind | Purpose | +| ------------------------- | -------- | ------------------------------------------------ | +| `codeToHtml` | Function | Re-exports Shiki's complete code-to-HTML helper. | +| `createCSSVariablesTheme` | Function | Re-exports Shiki's CSS variable theme factory. | + +## Language APIs + +| Export | Kind | Purpose | +| ------------------------------ | -------- | --------------------------------------------------------- | +| `registerCustomLanguage` | Function | Registers a lazy language and optional file mappings. | +| `resolveLanguage` | Function | Loads and caches one language registration. | +| `resolveLanguages` | Function | Loads and caches several language registrations. | +| `getResolvedOrResolveLanguage` | Function | Returns one cached language or starts its load. | +| `getResolvedLanguages` | Function | Gets cached registrations for the supplied languages. | +| `hasResolvedLanguages` | Function | Tests whether language registrations are cached. | +| `attachResolvedLanguages` | Function | Adds resolved registrations to a highlighter. | +| `areLanguagesAttached` | Function | Tests whether a highlighter has the supplied languages. | +| `cleanUpResolvedLanguages` | Function | Clears language resolution state. | +| `RegisteredCustomLanguages` | Map | Stores registered custom language loaders. | +| `ResolvedLanguages` | Map | Stores resolved language registrations. | +| `ResolvingLanguages` | Map | Stores active language load promises. | +| `AttachedLanguages` | Set | Stores language names attached to the shared highlighter. | + +## Theme APIs + +| Export | Kind | Purpose | +| -------------------------------- | -------- | ------------------------------------------------------ | +| `registerCustomTheme` | Function | Registers a lazy Shiki theme loader. | +| `CustomThemeLoader` | Type | Defines a raw or resolved Shiki theme loader. | +| `registerCustomCSSVariableTheme` | Function | Registers a theme that reads CSS variables. | +| `resolveTheme` | Function | Loads and caches one theme. | +| `resolveThemes` | Function | Loads and caches several themes. | +| `getResolvedOrResolveTheme` | Function | Returns one cached theme or starts its load. | +| `getResolvedThemes` | Function | Gets cached themes by name. | +| `hasResolvedThemes` | Function | Tests whether themes are cached. | +| `attachResolvedThemes` | Function | Adds resolved themes to a highlighter. | +| `areThemesAttached` | Function | Tests whether a highlighter has the supplied themes. | +| `cleanUpResolvedThemes` | Function | Clears theme resolution state. | +| `AttachedThemes` | Set | Stores theme names attached to the shared highlighter. | + +## Shared highlighter APIs + +| Export | Purpose | +| --------------------------- | ----------------------------------------------------------------- | +| `getSharedHighlighter` | Gets or creates the shared highlighter for themes and languages. | +| `preloadHighlighter` | Loads the shared highlighter before a render. | +| `getHighlighterIfLoaded` | Gets the shared highlighter after load. | +| `isHighlighterLoaded` | Tests a highlighter cache value for a loaded instance. | +| `isHighlighterLoading` | Tests a highlighter cache value for an active promise. | +| `isHighlighterNull` | Tests a highlighter cache value for an empty state. | +| `disposeHighlighter` | Disposes and clears the shared highlighter. | +| `getHighlighterOptions` | Converts one language and component options to highlighter input. | +| `getHighlighterThemeStyles` | Creates theme CSS from a loaded highlighter. | +| `getThemes` | Converts one theme or light/dark pair to a name list. | +| `isWorkerContext` | Tests whether code runs in a worker global scope. | + +## Render APIs + +| Export | Purpose | +| ---------------------------- | ------------------------------------------------------- | +| `renderFileWithHighlighter` | Creates a highlighted file syntax tree. | +| `renderDiffWithHighlighter` | Creates highlighted deletion and addition syntax trees. | +| `createTransformerWithState` | Creates Shiki transformers with shared render state. | + +## Stream APIs + +| Export | Kind | Purpose | +| ----------------------------------- | ----- | ------------------------------------------------------------- | +| `FileStream` | Class | Renders a readable code stream as highlighted rows. | +| `FileStreamOptions` | Type | Configures stream language, theme, start line, and callbacks. | +| `CodeToTokenTransformStream` | Class | Converts code chunks to themed or recall tokens. | +| `CodeToTokenTransformStreamOptions` | Type | Configures stream tokenization and recall tokens. | +| `ShikiStreamTokenizer` | Class | Tracks stable and unstable tokens across code chunks. | +| `ShikiStreamTokenizerOptions` | Type | Supplies Shiki token options and a highlighter. | +| `ShikiStreamTokenizerEnqueueResult` | Type | Returns recalled, stable, and unstable tokens for one chunk. | +| `RecallToken` | Type | Requests removal of prior unstable tokens. | + +## `FileStream` members + +| Member | Purpose | +| -------------------------- | ------------------------------------------ | +| `new FileStream(options?)` | Creates a stream renderer. | +| `setup(source, wrapper)` | Connects a readable code stream to a host. | +| `setThemeType(themeType)` | Selects system, light, or dark theme mode. | +| `cleanUp()` | Aborts the stream and releases resources. | + +## `ShikiStreamTokenizer` members + +| Member | Purpose | +| ----------------------------------- | ------------------------------------ | +| `new ShikiStreamTokenizer(options)` | Creates a stateful tokenizer. | +| `enqueue(chunk)` | Tokenizes one code chunk. | +| `close()` | Finalizes and returns stable tokens. | +| `clear()` | Clears accumulated token state. | +| `clone()` | Copies current tokenizer state. | + +`CodeToTokenTransformStream` exposes its `tokenizer` and `options` values. diff --git a/skills/diffs/references/api-react.md b/skills/diffs/references/api-react.md new file mode 100644 index 000000000..ba70538ac --- /dev/null +++ b/skills/diffs/references/api-react.md @@ -0,0 +1,63 @@ +# React API + +This reference lists the React-specific exports from `@pierre/diffs/react`. The +entry also re-exports every type in [Shared types](api-types.md). + +## Components and hooks + +| Export | Kind | Purpose | +| --------------------------- | --------- | --------------------------------------------------------- | +| `File` | Component | Renders one code file. | +| `FileDiff` | Component | Renders pre-parsed diff metadata. | +| `MultiFileDiff` | Component | Parses and renders an old and new file pair. | +| `PatchDiff` | Component | Parses and renders one unified patch string. | +| `UnresolvedFile` | Component | Renders and resolves merge conflicts in one file. | +| `CodeView` | Component | Renders a virtualized list of files and diffs. | +| `Virtualizer` | Component | Provides simple viewport virtualization. | +| `useVirtualizer` | Hook | Gets the nearest simple `Virtualizer` instance. | +| `EditProvider` | Component | Supplies an editor factory to editable surfaces. | +| `useCreateEditor` | Hook | Gets the nearest editor factory. | +| `WorkerPoolContextProvider` | Component | Creates and supplies a worker pool. | +| `useWorkerPool` | Hook | Gets the nearest worker pool. | +| `useFileInstance` | Hook | Creates and manages a vanilla `File` instance. | +| `useFileDiffInstance` | Hook | Creates and manages a vanilla `FileDiff` instance. | +| `useStableCallback` | Hook | Returns a stable callback that reads the latest function. | + +## Component and provider types + +| Export | Purpose | +| ----------------------------------- | ----------------------------------------------------------------- | +| `FileProps` | Defines props for `File`. | +| `FileOptions` | Defines vanilla file options and the React `options` prop. | +| `FileDiffProps` | Defines props for `FileDiff`. | +| `MultiFileDiffProps` | Defines props for `MultiFileDiff`. | +| `PatchDiffProps` | Defines props for `PatchDiff`. | +| `UnresolvedFileProps` | Defines props for `UnresolvedFile`. | +| `UnresolvedFileReactOptions` | Defines merge-conflict options for React. | +| `DiffBasePropsReact` | Defines props shared by React diff components. | +| `CodeViewProps` | Defines controlled or uncontrolled `CodeView` props. | +| `ControlledCodeViewProps` | Defines `CodeView` props with `items`. | +| `UncontrolledCodeViewProps` | Defines `CodeView` props with `initialItems`. | +| `CodeViewReactOptions` | Defines the React-safe `CodeView` option set. | +| `CodeViewHandle` | Defines imperative list, selection, scroll, and editor controls. | +| `CreateEditor` | Defines the editor factory. | +| `EditProviderProps` | Defines `EditProvider` props. | +| `MergeConflictActionsTypeOption` | Selects no actions, default actions, or a custom action renderer. | +| `RenderMergeConflictActionContext` | Supplies conflict resolution to a custom action renderer. | +| `RenderMergeConflictActions` | Defines a custom conflict action renderer. | +| `WorkerInitializationRenderOptions` | Defines initial worker languages and render options. | +| `WorkerPoolOptions` | Defines the worker factory, pool size, and cache size. | + +## Contexts and render helpers + +| Export | Kind | Purpose | +| ------------------------- | -------- | ------------------------------------------------------ | +| `EditContext` | Context | Holds the editor factory. | +| `WorkerPoolContext` | Context | Holds the worker pool. | +| `VirtualizerContext` | Context | Holds the simple virtualizer. | +| `GutterUtilitySlotStyles` | Value | Supplies style keys for gutter utility slots. | +| `MergeConflictSlotStyles` | Value | Supplies style keys for merge conflict slots. | +| `noopRender` | Function | Returns no React output for an optional render slot. | +| `renderDiffChildren` | Function | Builds React portals for diff slots. | +| `renderFileChildren` | Function | Builds React portals for file slots. | +| `templateRender` | Function | Renders React content through a managed template slot. | diff --git a/skills/diffs/references/api-rendering.md b/skills/diffs/references/api-rendering.md new file mode 100644 index 000000000..5e9de9707 --- /dev/null +++ b/skills/diffs/references/api-rendering.md @@ -0,0 +1,183 @@ +# Low-level rendering API + +This reference lists the renderer, manager, DOM helper, comparison, and constant +exports from `@pierre/diffs`. + +## Contents + +- [Renderers](#renderers) +- [Interaction manager](#interaction-manager) +- [Size, scroll, and render managers](#size-scroll-and-render-managers) +- [Comparison helpers](#comparison-helpers) +- [Syntax tree and DOM helpers](#syntax-tree-and-dom-helpers) +- [Layout and CSS helpers](#layout-and-css-helpers) +- [Constants](#constants) + +## Renderers + +| Export | Kind | Purpose | +| -------------------------------------- | ----- | ------------------------------------------------------- | +| `FileRenderer` | Class | Converts one file to highlighted HAST, CSS, and HTML. | +| `FileRendererOptions` | Type | Adds header mode to base code options. | +| `FileRenderResult` | Type | Holds file HAST, CSS, row counts, and buffers. | +| `DiffHunksRenderer` | Class | Converts diff hunks to highlighted column HAST and CSS. | +| `DiffHunksRendererOptions` | Type | Configures one hunk renderer. | +| `DiffHunksRendererOptionsWithDefaults` | Type | Describes resolved hunk renderer options. | +| `HunksRenderResult` | Type | Holds rendered diff columns, metadata, and row count. | +| `RenderedLineContext` | Type | Supplies line state to a line decoration. | +| `LineDecoration` | Type | Defines a custom line wrapper and injected rows. | +| `InjectedRow` | Type | Defines one row inserted around a unified line. | +| `SplitInjectedRow` | Type | Defines one row inserted around a split line. | +| `UnifiedInjectedRowPlacement` | Type | Selects placement before or after a unified row. | +| `SplitInjectedRowPlacement` | Type | Selects side and placement for a split row. | +| `UnifiedLineDecorationProps` | Type | Supplies one unified row to a decoration. | +| `SplitLineDecorationProps` | Type | Supplies paired split rows to a decoration. | + +## Interaction manager + +| Export | Kind | Purpose | +| ------------------------------- | -------- | ------------------------------------------------------------------------ | +| `InteractionManager` | Class | Handles hover, token, gutter, and line selection events. | +| `InteractionManagerMode` | Type | Selects file or diff interaction data. | +| `InteractionManagerBaseOptions` | Type | Defines interaction callbacks and enabled features. | +| `InteractionManagerOptions` | Type | Adds required DOM access to base interaction options. | +| `GetHoveredLineResult` | Type | Describes the current hovered file or diff line. | +| `GetLineIndexUtility` | Type | Maps a logical line to row and column indexes. | +| `OnLineClickProps` | Type | Describes a file line click. | +| `OnLineEnterLeaveProps` | Type | Describes file line pointer entry or exit. | +| `OnDiffLineClickProps` | Type | Describes a diff line click. | +| `OnDiffLineEnterLeaveProps` | Type | Describes diff line pointer entry or exit. | +| `OnTokenEventProps` | Type | Selects file or diff token event data. | +| `SelectionWriteOptions` | Type | Configures callback emission, the active side, and line-only highlights. | +| `MergeConflictActionTarget` | Type | Describes a merge conflict action element. | +| `LogTypes` | Type | Selects interaction log categories. | +| `pluckInteractionOptions` | Function | Selects interaction fields from component options. | + +## Size, scroll, and render managers + +| Export | Kind | Purpose | +| --------------------------------- | -------- | ------------------------------------------------------------- | +| `ResizeManager` | Class | Measures rows, annotations, and column CSS values. | +| `ResizeManagerColumnVariableMode` | Type | Selects column variable measurement or application. | +| `ResizeManagerSetupOptions` | Type | Configures annotation and column measurement. | +| `ScrollSyncManager` | Class | Synchronizes additions and deletions column scroll positions. | +| `queueRender` | Function | Adds a callback to the shared animation render queue. | +| `dequeueRender` | Function | Removes a callback from the shared render queue. | +| `clearRenderQueue` | Function | Removes all callbacks from the shared render queue. | + +## Comparison helpers + +| Export | Purpose | +| ----------------------------- | ------------------------------------------------- | +| `areDiffLineAnnotationsEqual` | Compares two diff annotation arrays. | +| `areLineAnnotationsEqual` | Compares two file annotation arrays. | +| `areDiffRenderOptionsEqual` | Compares resolved diff render options. | +| `areFileRenderOptionsEqual` | Compares resolved file render options. | +| `areDiffTargetsEqual` | Compares two diff interaction targets. | +| `areFilesEqual` | Compares two file inputs. | +| `areHunkDataEqual` | Compares two hunk data objects. | +| `areObjectsEqual` | Performs the package's shallow object comparison. | +| `areOptionsEqual` | Compares component option objects. | +| `arePrePropertiesEqual` | Compares calculated `pre` properties. | +| `areRenderRangesEqual` | Compares two render ranges. | +| `areSelectionsEqual` | Compares editor selection arrays. | +| `areThemesEqual` | Compares theme names or light/dark pairs. | +| `areVirtualWindowSpecsEqual` | Compares two virtual window descriptions. | +| `areWorkerStatsEqual` | Compares two worker statistics objects. | + +## Syntax tree and DOM helpers + +| Export | Kind | Purpose | +| -------------------------------- | -------- | ---------------------------------------------------------- | +| `createAnnotationElement` | Function | Creates a HAST annotation row from an annotation span. | +| `createAnnotationWrapperNode` | Function | Creates a DOM host for an annotation slot. | +| `createDiffSpanDecoration` | Function | Creates one Shiki inline diff decoration. | +| `pushOrJoinSpan` | Function | Adds or joins one inline diff span. | +| `createEmptyRowBuffer` | Function | Creates an empty virtual row buffer. | +| `createFileHeaderElement` | Function | Creates a file or diff header HAST element. | +| `CreateFileHeaderElementProps` | Type | Defines header source, mode, and sticky state. | +| `createGutterGap` | Function | Creates a gutter gap HAST element. | +| `createGutterItem` | Function | Creates a gutter item HAST element. | +| `createGutterWrapper` | Function | Creates a gutter wrapper HAST element. | +| `createGutterUtilityElement` | Function | Creates a gutter utility HAST element. | +| `createGutterUtilityContentNode` | Function | Creates a gutter utility DOM content host. | +| `createHastElement` | Function | Creates a typed HAST element. | +| `createIconElement` | Function | Creates a sprite icon HAST element. | +| `createTextNodeElement` | Function | Creates a HAST text node. | +| `createNoNewlineElement` | Function | Creates the missing-final-newline HAST element. | +| `createPreElement` | Function | Creates the outer HAST `pre` element. | +| `createPreWrapperProperties` | Function | Creates HAST properties for a `pre` wrapper. | +| `createRowNodes` | Function | Creates DOM row and content elements for one line. | +| `createSeparator` | Function | Creates a hunk separator HAST element. | +| `createSpanFromToken` | Function | Creates a HAST span from one highlighted token. | +| `createStyleElement` | Function | Creates a DOM style element with an attribute marker. | +| `createThemeStyleElement` | Function | Creates a marked theme style element. | +| `createUnsafeCSSStyleNode` | Function | Creates a marked custom CSS style element. | +| `findCodeElement` | Function | Finds the code element in a HAST tree. | +| `getLineNodes` | Function | Gets rendered line nodes from a HAST root. | +| `getOrCreateCodeNode` | Function | Reuses or creates a code column DOM node. | +| `getLineAnnotationName` | Function | Creates the slot name for a line annotation. | +| `getHunkSeparatorSlotName` | Function | Creates the slot name for a hunk separator. | +| `getIconForType` | Function | Maps a file change type to a sprite icon. | +| `processLine` | Function | Applies line render state to one HAST line. | +| `setPreNodeProperties` | Function | Applies resolved render properties to a DOM `pre` element. | +| `prerenderHTMLIfNecessary` | Function | Adds preloaded HTML to an empty host element. | + +## Layout and CSS helpers + +| Export | Purpose | +| -------------------------------- | ------------------------------------------------------------- | +| `createWindowFromScrollPosition` | Calculates a virtual window from scroll measurements. | +| `isDefaultRenderRange` | Tests whether a render range covers the default full range. | +| `prefersReducedMotion` | Reads the reduced-motion media preference. | +| `formatCSSVariablePrefix` | Creates the global or token CSS variable prefix. | +| `wrapCoreCSS` | Places core CSS in its cascade layer. | +| `wrapThemeCSS` | Places theme CSS in its layer and mode selector. | +| `wrapUnsafeCSS` | Places custom CSS in its cascade layer. | +| `patchScrollbarGutterSize` | Updates the measured scrollbar gutter in theme CSS. | +| `detachString` | Copies a retained substring to an independent backing string. | +| `releaseStringDetachBuffer` | Resets the reusable string copy buffer. | +| `SVGSpriteSheet` | Contains the SVG symbols used by rendered controls. | +| `SVGSpriteNames` | Names a symbol in `SVGSpriteSheet`. | + +## Constants + +| Export | Purpose | +| ------------------------------------------ | ----------------------------------------------------- | +| `DEFAULT_THEMES` | Provides the default light and dark theme names. | +| `DEFAULT_TOKENIZE_MAX_LENGTH` | Provides the default total tokenization limit. | +| `DEFAULT_COLLAPSED_CONTEXT_THRESHOLD` | Provides the default hidden-context threshold. | +| `DEFAULT_EXPANDED_REGION` | Provides the default hunk expansion state. | +| `DEFAULT_RENDER_RANGE` | Provides the full render range. | +| `EMPTY_RENDER_RANGE` | Provides an empty render range. | +| `DEFAULT_VIRTUAL_FILE_METRICS` | Provides estimated file header and line heights. | +| `DEFAULT_CODE_VIEW_FILE_METRICS` | Provides list item height estimates. | +| `DEFAULT_CODE_VIEW_LAYOUT` | Provides an empty list layout. | +| `DEFAULT_SMOOTH_SCROLL_SETTINGS` | Provides the default smooth scroll settings. | +| `DIFFS_TAG_NAME` | Provides the `diffs-container` custom element name. | +| `CORE_CSS_ATTRIBUTE` | Provides the core style marker attribute. | +| `THEME_CSS_ATTRIBUTE` | Provides the theme style marker attribute. | +| `UNSAFE_CSS_ATTRIBUTE` | Provides the custom style marker attribute. | +| `DIFFS_SCROLLBAR_MEASURE_ATTRIBUTE` | Provides the scrollbar measurement attribute. | +| `DIFFS_SCROLLBAR_GUTTER_MEASURED_PROPERTY` | Provides the measured scrollbar CSS property. | +| `CODE_VIEW_HEADER_ATTRIBUTE` | Provides the list header host attribute. | +| `CODE_VIEW_FOOTER_ATTRIBUTE` | Provides the list footer host attribute. | +| `CUSTOM_HEADER_SLOT_ID` | Provides the custom header slot ID. | +| `HEADER_PREFIX_SLOT_ID` | Provides the header prefix slot ID. | +| `HEADER_FILENAME_SUFFIX_SLOT_ID` | Provides the filename suffix slot ID. | +| `HEADER_METADATA_SLOT_ID` | Provides the header metadata slot ID. | +| `HUNK_HEADER` | Provides the patch hunk header marker. | +| `FILE_CONTEXT_BLOB` | Matches a patch hunk boundary. | +| `INDEX_LINE_METADATA` | Provides the patch index metadata marker. | +| `COMMIT_METADATA_SPLIT` | Provides the commit metadata separator expression. | +| `FILENAME_HEADER_REGEX` | Matches a standard patch file header. | +| `FILENAME_HEADER_REGEX_GIT` | Matches a Git patch file header. | +| `GIT_DIFF_FILE_BREAK_REGEX` | Matches a Git patch file boundary. | +| `UNIFIED_DIFF_FILE_BREAK_REGEX` | Matches a unified patch file boundary. | +| `ALTERNATE_FILE_NAMES_GIT` | Matches alternate file names in a Git patch header. | +| `MERGE_CONFLICT_START_MARKER_REGEX` | Matches a conflict start marker. | +| `MERGE_CONFLICT_BASE_MARKER_REGEX` | Matches a conflict base marker. | +| `MERGE_CONFLICT_SEPARATOR_MARKER_REGEX` | Matches a conflict separator marker. | +| `MERGE_CONFLICT_END_MARKER_REGEX` | Matches a conflict end marker. | +| `SPLIT_WITH_NEWLINES` | Splits text while it preserves newline tokens. | +| `DIFFS_DEVELOPMENT_BUILD` | Reports whether the package uses a development build. | diff --git a/skills/diffs/references/api-ssr.md b/skills/diffs/references/api-ssr.md new file mode 100644 index 000000000..6f5b7e61f --- /dev/null +++ b/skills/diffs/references/api-ssr.md @@ -0,0 +1,35 @@ +# SSR API + +This reference lists the SSR-specific exports from `@pierre/diffs/ssr`. The +entry also re-exports every type in [Shared types](api-types.md). + +## Functions + +| Export | Purpose | +| --------------------------- | ------------------------------------------------------------- | +| `preloadFile` | Renders one file and returns props with `prerenderedHTML`. | +| `preloadFileDiff` | Renders pre-parsed diff metadata and returns component props. | +| `preloadMultiFileDiff` | Parses and renders an old and new file pair. | +| `preloadPatchDiff` | Parses and renders one patch for `PatchDiff`. | +| `preloadPatchFile` | Parses a multi-file patch and returns one result per file. | +| `preloadUnresolvedFile` | Renders one merge-conflict file and returns component props. | +| `preloadDiffHTML` | Renders a diff directly to an HTML string. | +| `preloadUnresolvedFileHTML` | Renders a merge-conflict file directly to an HTML string. | +| `renderHTML` | Serializes rendered HAST elements to HTML. | + +## Types + +| Export | Purpose | +| ------------------------------ | -------------------------------------------------------- | +| `PreloadFileOptions` | Defines input for `preloadFile`. | +| `PreloadedFileResult` | Adds `prerenderedHTML` to file input. | +| `PreloadDiffOptions` | Defines parsed or file-pair input for `preloadDiffHTML`. | +| `PreloadFileDiffOptions` | Defines input for `preloadFileDiff`. | +| `PreloadFileDiffResult` | Adds `prerenderedHTML` to parsed diff input. | +| `PreloadMultiFileDiffOptions` | Defines input for `preloadMultiFileDiff`. | +| `PreloadMultiFileDiffResult` | Adds `prerenderedHTML` to file-pair input. | +| `PreloadPatchDiffOptions` | Defines input for `preloadPatchDiff`. | +| `PreloadPatchDiffResult` | Adds `prerenderedHTML` to patch input. | +| `PreloadPatchFileOptions` | Defines input for `preloadPatchFile`. | +| `PreloadUnresolvedFileOptions` | Defines input for `preloadUnresolvedFile`. | +| `PreloadUnresolvedFileResult` | Adds `prerenderedHTML` to merge-conflict input. | diff --git a/skills/diffs/references/api-types.md b/skills/diffs/references/api-types.md new file mode 100644 index 000000000..deed8152d --- /dev/null +++ b/skills/diffs/references/api-types.md @@ -0,0 +1,176 @@ +# Shared types + +This reference lists every export from the shared `@pierre/diffs` type module. +The root, React, and SSR entries re-export these types. + +## Contents + +- [Files and patches](#files-and-patches) +- [Themes and options](#themes-and-options) +- [Annotations and selection](#annotations-and-selection) +- [`CodeView` types](#codeview-types) +- [Lines, hunks, and render state](#lines-hunks-and-render-state) +- [Render results and virtualization](#render-results-and-virtualization) +- [Component and editor contracts](#component-and-editor-contracts) +- [Shiki and diff types](#shiki-and-diff-types) + +## Files and patches + +| Export | Purpose | +| ------------------------------- | ----------------------------------------------------------------- | +| `FileContents` | Describes a file name, text, language, header, and cache key. | +| `DiffFileInput` | Accepts an old and new file for changes, additions, or deletions. | +| `MaybeDiffFileInput` | Accepts a file pair or no file input. | +| `FileDiffContentsLoader` | Loads complete files for partial diff metadata. | +| `FileDiffLoadedChangedFiles` | Returns both files for a loaded changed diff. | +| `FileDiffLoadedPureRenamedFile` | Returns the new file for a loaded pure rename. | +| `FileDiffLoadedFiles` | Represents either loaded-file result. | +| `ChangeTypes` | Names changed, renamed, added, or deleted file states. | +| `ParsedPatch` | Holds patch metadata and parsed files. | +| `ContextContent` | Describes one unchanged hunk block. | +| `ChangeContent` | Describes one additions and deletions block. | +| `Hunk` | Describes one parsed patch hunk. | +| `FileDiffMetadata` | Holds parsed file names, lines, hunks, and change metadata. | +| `MergeConflictMarkerRowType` | Names a merge conflict marker row. | +| `MergeConflictMarkerRow` | Describes one marker row and its source line. | +| `MergeConflictRegion` | Describes one parsed merge conflict region. | +| `MergeConflictResolution` | Selects current, incoming, or both conflict contents. | +| `MergeConflictActionPayload` | Describes one conflict action and region. | +| `ProcessFileConflictData` | Holds state while patch parsing processes conflicts. | +| `ConflictResolverTypes` | Names current, incoming, or both conflict choices. | + +## Themes and options + +| Export | Purpose | +| ------------------------------------ | ------------------------------------------------------------ | +| `SupportedLanguages` | Accepts a bundled, text, ANSI, or custom language name. | +| `HighlighterTypes` | Selects the JavaScript or WebAssembly Shiki engine. | +| `HighlightedToken` | Stores a character index, foreground, and token text. | +| `DiffsThemeNames` | Accepts a bundled or custom theme name. | +| `ThemesType` | Maps light and dark schemes to theme names. | +| `ThemeTypes` | Selects system, light, or dark mode. | +| `DiffsHighlighter` | Defines the package's configured Shiki highlighter. | +| `BaseCodeOptions` | Configures themes, wrapping, headers, tokenization, and CSS. | +| `BaseDiffOptions` | Adds layout, indicators, context, and line diff options. | +| `BaseDiffOptionsWithDefaults` | Describes required diff options after defaults apply. | +| `DiffIndicators` | Selects classic, bar, or hidden diff indicators. | +| `HunkSeparators` | Selects the hunk separator presentation. | +| `LineDiffTypes` | Selects word, alternate word, character, or no inline diff. | +| `FileHeaderRenderMode` | Selects a default or custom file header. | +| `CustomPreProperties` | Defines custom properties for the rendered `pre` element. | +| `PrePropertiesConfig` | Describes calculated `pre` element properties. | +| `ExtensionFormatMap` | Maps file names or extensions to languages. | +| `RenderHeaderPrefixCallback` | Produces prefix content for a diff header. | +| `RenderHeaderFilenameSuffixCallback` | Produces filename suffix content for a diff header. | +| `RenderHeaderMetadataCallback` | Produces metadata content for a diff header. | +| `RenderFileMetadata` | Produces header content for a file. | +| `PostRenderPhase` | Names mount, update, or unmount callback phases. | + +## Annotations and selection + +| Export | Purpose | +| ------------------------- | ------------------------------------------------------------ | +| `AnnotationSide` | Selects deletions or additions for an annotation. | +| `LineAnnotation` | Associates metadata with one file line. | +| `DiffLineAnnotation` | Associates metadata with one side and line. | +| `AnnotationLineMap` | Groups diff annotations by line number. | +| `SelectedLineRange` | Describes a selected start and end across diff sides. | +| `SelectionSide` | Selects deletions or additions for a selection. | +| `SelectionPoint` | Describes one line and optional side. | +| `SelectionDirection` | Describes backward, neutral, or forward selection direction. | +| `EditorActiveLineOptions` | Configures reveal behavior for an editor active line. | + +## `CodeView` types + +| Export | Purpose | +| ------------------------------ | -------------------------------------------------------- | +| `CodeViewFileItem` | Describes one file item in a virtualized list. | +| `CodeViewDiffItem` | Describes one diff item in a virtualized list. | +| `CodeViewItem` | Represents a file or diff list item. | +| `CodeViewCreateEditorOptions` | Adds an item ID to editor creation options. | +| `CodeViewScrollBehavior` | Selects instant, smooth, or automatic smooth scroll. | +| `CodeViewScrollTarget` | Represents any supported list scroll target. | +| `CodeViewPositionScrollTarget` | Scrolls to an absolute list position. | +| `CodeViewLineScrollTarget` | Scrolls to one item line. | +| `CodeViewRangeScrollTarget` | Scrolls to one item line range. | +| `CodeViewItemScrollTarget` | Scrolls to one item boundary. | +| `NumericScrollLineAnchor` | Describes a numeric position inside a line. | +| `CodeViewLayout` | Stores item offsets, heights, and total list height. | +| `PendingCodeViewLayoutReset` | Describes a deferred list layout reset. | +| `SmoothScrollSettings` | Configures duration and distance for smooth list scroll. | + +## Lines, hunks, and render state + +| Export | Purpose | +| ---------------------------- | --------------------------------------------------------------- | +| `HunkLineType` | Names context, expanded, addition, deletion, or metadata lines. | +| `HunkData` | Describes one hunk's render indexes and line ranges. | +| `HunkExpansionRegion` | Describes expanded context above and below a hunk. | +| `ExpansionDirections` | Selects up, down, or both expansion directions. | +| `DiffAcceptRejectHunkType` | Selects accept, reject, or both hunk controls. | +| `DiffAcceptRejectHunkConfig` | Configures hunk accept and reject behavior. | +| `GapSpan` | Describes an empty row span. | +| `AnnotationSpan` | Describes an annotation row span. | +| `LineSpans` | Represents a gap or annotation span. | +| `LineTypes` | Names rendered context and change line classes. | +| `LineInfo` | Describes a rendered line number, side, and type. | +| `CodeColumnType` | Selects unified, additions, or deletions columns. | +| `LineEventBaseProps` | Supplies a file line to an interaction callback. | +| `DiffLineEventBaseProps` | Supplies a diff line and side to an interaction callback. | +| `TokenEventBase` | Supplies a token and source event. | +| `DiffTokenEventBaseProps` | Adds diff side data to a token event. | +| `ObservedAnnotationNodes` | Stores DOM nodes for observed annotations. | +| `ObservedGridNodes` | Stores DOM nodes for observed grid columns. | +| `SharedRenderState` | Holds shared token transformer render state. | +| `StickySpecs` | Describes sticky header position and height. | + +## Render results and virtualization + +| Export | Purpose | +| --------------------------- | ------------------------------------------------------- | +| `RenderFileOptions` | Defines the resolved options for a highlighted file. | +| `RenderDiffOptions` | Defines the resolved options for a highlighted diff. | +| `ForceFilePlainTextOptions` | Selects a plain-text file range. | +| `ForceDiffPlainTextOptions` | Selects a plain-text diff range and hunk state. | +| `ThemedFileResult` | Holds the highlighted file syntax tree and line count. | +| `ThemedDiffResult` | Holds highlighted additions and deletions syntax trees. | +| `RenderDiffFilesResult` | Holds the resolved old and new file inputs. | +| `RenderFileResult` | Holds file output and the options that produced it. | +| `RenderDiffResult` | Holds diff output and the options that produced it. | +| `RenderedFileASTCache` | Stores one cached file syntax tree by theme. | +| `RenderedDiffASTCache` | Stores one cached diff syntax tree by theme. | +| `AppliedThemeStyleCache` | Stores applied light and dark theme CSS. | +| `RenderRange` | Describes a start row, row count, and buffer sizes. | +| `RenderWindow` | Describes first and last rows in a render window. | +| `VirtualWindowSpecs` | Describes viewport position, height, and row window. | +| `VirtualFileMetrics` | Describes estimated header and line heights. | + +## Component and editor contracts + +| Export | Purpose | +| ------------------------ | ----------------------------------------------------------- | +| `DiffsComponentOptions` | Defines shared options for a render component. | +| `DiffsBaseComponent` | Defines the common file and diff component methods. | +| `DiffsEditableComponent` | Adds editor attachment and document updates to a component. | +| `EditableInstance` | Selects an editable file or diff instance. | +| `DiffsEditor` | Defines the editor interface that render components use. | +| `DiffsTextDocument` | Defines the text document interface that components use. | +| `Position` | Identifies a zero-based line and character. | +| `Range` | Identifies start and end positions. | +| `TextEdit` | Replaces one range with new text. | +| `EditorSelection` | Adds direction to a range. | +| `EditorState` | Holds editor selections and view state. | + +## Shiki and diff types + +| Export | Purpose | +| -------------------------------- | ----------------------------------------------- | +| `BundledLanguage` | Names a language bundled by Shiki. | +| `CodeToHastOptions` | Configures Shiki code-to-HAST output. | +| `DecorationItem` | Describes a Shiki source decoration. | +| `LanguageRegistration` | Describes a Shiki language grammar. | +| `ShikiTransformer` | Defines a Shiki syntax tree transformer. | +| `ThemeRegistration` | Describes a raw Shiki theme. | +| `ThemeRegistrationResolved` | Describes a normalized Shiki theme. | +| `ThemedToken` | Describes one Shiki token with its theme style. | +| `CreatePatchOptionsNonabortable` | Configures the underlying patch algorithm. | diff --git a/skills/diffs/references/api-worker.md b/skills/diffs/references/api-worker.md new file mode 100644 index 000000000..cb204dac6 --- /dev/null +++ b/skills/diffs/references/api-worker.md @@ -0,0 +1,91 @@ +# Worker API + +This reference lists every export from `@pierre/diffs/worker`, every public +`WorkerPoolManager` member, and both worker script entries. + +## Runtime exports + +| Export | Kind | Purpose | +| -------------------------------- | -------- | ----------------------------------------------------- | +| `WorkerPoolManager` | Class | Runs file and diff highlighting across a worker pool. | +| `getOrCreateWorkerPoolSingleton` | Function | Gets or creates the module-wide worker pool. | +| `terminateWorkerPoolSingleton` | Function | Terminates and clears the module-wide worker pool. | + +## `WorkerPoolManager` members + +| Member | Purpose | +| -------------------------------------------------------------- | ------------------------------------------------- | +| `new WorkerPoolManager(options, renderOptions)` | Creates a worker pool. | +| `initialize(languages?)` | Starts workers and loads languages. | +| `isInitialized()` | Reports whether initialization finished. | +| `isWorkingPool()` | Reports whether workers can accept work. | +| `setRenderOptions(options)` | Updates theme and render settings in each worker. | +| `getFileRenderOptions()` | Gets active file render options. | +| `getDiffRenderOptions()` | Gets active diff render options. | +| `highlightFileAST(instance, file)` | Queues a highlighted file result for an instance. | +| `highlightDiffAST(instance, diff)` | Queues a highlighted diff result for an instance. | +| `primeFileHighlightCache(file)` | Preloads one highlighted file result. | +| `primeDiffHighlightCache(diff)` | Preloads one highlighted diff result. | +| `getFileResultCache(file)` | Gets one cached file result. | +| `getDiffResultCache(diff)` | Gets one cached diff result. | +| `getPlainFileAST(file, start, total, lines?)` | Gets a plain-text file result. | +| `getPlainDiffAST(diff, start, total, expansions?, threshold?)` | Gets a plain-text diff result. | +| `inspectCaches()` | Gets both result caches. | +| `evictFileFromCache(cacheKey)` | Removes one file cache entry. | +| `evictDiffFromCache(cacheKey)` | Removes one diff cache entry. | +| `subscribeToThemeChanges(instance)` | Subscribes a render instance to theme changes. | +| `unsubscribeToThemeChanges(instance)` | Removes a theme subscription. | +| `subscribeToStatChanges(callback)` | Subscribes to worker statistics. | +| `cleanUpTasks(instance)` | Removes queued and active tasks for an instance. | +| `getStats()` | Gets worker and cache statistics. | +| `terminate()` | Stops workers and clears pool resources. | + +## Configuration and state types + +| Export | Purpose | +| ----------------------------------- | ----------------------------------------------------------------- | +| `SetupWorkerPoolProps` | Combines pool and highlighter options for the singleton. | +| `WorkerPoolOptions` | Defines the worker factory, pool size, and cache size. | +| `WorkerInitializationRenderOptions` | Defines initial languages, theme, highlighter, and diff settings. | +| `WorkerRenderingOptions` | Defines the complete worker render settings. | +| `WorkerStats` | Describes pool state, work counts, subscribers, and cache sizes. | +| `WorkerRequestId` | Identifies one worker request. | +| `ResolvedLanguage` | Holds a resolved language registration. | +| `FileRendererInstance` | Defines callbacks for a file render consumer. | +| `DiffRendererInstance` | Defines callbacks for a diff render consumer. | + +## Request and response types + +| Export | Purpose | +| ------------------------------- | ------------------------------------------------------- | +| `WorkerRequest` | Represents any request sent to a worker. | +| `InitializeWorkerRequest` | Starts a worker with themes, languages, and options. | +| `SetRenderOptionsWorkerRequest` | Updates render options and themes. | +| `RenderFileRequest` | Requests one file render. | +| `RenderDiffRequest` | Requests one diff render. | +| `SubmitRequest` | Represents a file or diff request before ID assignment. | +| `WorkerResponse` | Represents any worker response. | +| `InitializeSuccessResponse` | Confirms worker initialization. | +| `RegisterThemeSuccessResponse` | Confirms a render-option and theme update. | +| `RenderSuccessResponse` | Represents a successful file or diff render. | +| `RenderFileSuccessResponse` | Returns one file render result. | +| `RenderDiffSuccessResponse` | Returns one diff render result. | +| `RenderErrorResponse` | Returns a serialized worker error. | + +## Task types + +| Export | Purpose | +| ---------------------------- | ------------------------------------------------- | +| `AllWorkerTasks` | Represents any manager task. | +| `InitializeWorkerTask` | Tracks one initialization request. | +| `SetRenderOptionsWorkerTask` | Tracks one render-option update. | +| `RenderFileTask` | Tracks one file render request and its consumers. | +| `RenderDiffTask` | Tracks one diff render request and its consumers. | +| `RenderTaskCallbacks` | Resolves or rejects one render task consumer. | + +## Worker script entries + +| Import | Purpose | +| ----------------------------------------- | ---------------------------------------------------------- | +| `@pierre/diffs/worker/worker.js` | Supplies the module worker that uses package dependencies. | +| `@pierre/diffs/worker/worker-portable.js` | Supplies a bundled module worker. | diff --git a/skills/diffs/references/recipe-annotations.md b/skills/diffs/references/recipe-annotations.md new file mode 100644 index 000000000..7707e93f3 --- /dev/null +++ b/skills/diffs/references/recipe-annotations.md @@ -0,0 +1,32 @@ +# Recipe: add line annotations and selection + +Pass annotation data and a renderer to the surface: + +```tsx +import type { DiffLineAnnotation } from '@pierre/diffs/react'; +import { MultiFileDiff } from '@pierre/diffs/react'; + +const annotations: DiffLineAnnotation<{ message: string }>[] = [ + { + side: 'additions', + lineNumber: 8, + metadata: { message: 'Review this line.' }, + }, +]; + +

{annotation.metadata.message}

} + options={{ + enableLineSelection: true, + onLineSelectionEnd(range) { + saveSelection(range); + }, + }} +/>; +``` + +Use `LineAnnotation` for a single file. Use `DiffLineAnnotation` for a diff. +Control the active selection with the `selectedLines` prop. diff --git a/skills/diffs/references/recipe-code-view.md b/skills/diffs/references/recipe-code-view.md new file mode 100644 index 000000000..36d818c0a --- /dev/null +++ b/skills/diffs/references/recipe-code-view.md @@ -0,0 +1,213 @@ +# Recipe: build a `CodeView` + +Use `CodeView` when one scroll region contains many files, diffs, or both. It +manages item virtualization, sticky headers, list-wide selection, and item or +line scroll targets. + +## Contents + +- [Select item ownership](#select-item-ownership) +- [Define items](#define-items) +- [Use controlled React state](#use-controlled-react-state) +- [Use imperative ownership](#use-imperative-ownership) +- [Enable item edit mode](#enable-item-edit-mode) + +## Select item ownership + +| Host and data flow | Input | Update API | +| ------------------------------------------- | ----------------- | ------------------------------------ | +| React owns the complete list | `items` | Publish a new `items` array. | +| React hosts a large or append-only list | `initialItems` | Use the `CodeViewHandle` methods. | +| Vanilla JavaScript owns the viewer instance | `setItems(items)` | Use the `CodeView` instance methods. | + +Keep one ownership mode for the life of a mounted React viewer. Use controlled +state when item data already belongs to React. Use imperative ownership for a +large or streamed list. + +## Define items + +Give each item a stable and unique `id`. Use a `file` item for `FileContents`. +Use a `diff` item for `FileDiffMetadata`. + +Increment `version` when an existing item changes its contents, annotations, +collapsed state, or edit state. `CodeView` uses the ID and version to select the +item that it must update. + +## Use controlled React state + +```tsx +import { + parseDiffFromFile, + type CodeViewItem, + type CodeViewLineSelection, +} from '@pierre/diffs'; +import { CodeView, type CodeViewHandle } from '@pierre/diffs/react'; +import { useRef, useState } from 'react'; + +const oldFile = { + name: 'src/value.ts', + contents: 'export const value = 1;', +}; +const newFile = { + name: 'src/value.ts', + contents: 'export const value = 2;', +}; +const codeViewStyle = { height: 600, overflow: 'auto' } as const; +const codeViewOptions = { + theme: { light: 'pierre-light', dark: 'pierre-dark' }, + stickyHeaders: true, + enableLineSelection: true, + layout: { paddingTop: 16, paddingBottom: 16, gap: 12 }, +} as const; + +export function ReviewSurface() { + const viewerRef = useRef | null>(null); + const [selection, setSelection] = useState( + null + ); + const [items, setItems] = useState(() => [ + { + id: 'diff:src/value.ts', + type: 'diff', + fileDiff: parseDiffFromFile(oldFile, newFile), + version: 0, + }, + { + id: 'file:README.md', + type: 'file', + file: { name: 'README.md', contents: '# Review notes' }, + version: 0, + }, + ]); + + function toggleDiff() { + setItems((current) => + current.map((item) => + item.id === 'diff:src/value.ts' + ? { + ...item, + collapsed: !item.collapsed, + version: (item.version ?? 0) + 1, + } + : item + ) + ); + } + + return ( + <> + + + + + ); +} +``` + +## Use imperative ownership + +In React, pass `initialItems` and keep `items` unset. Use the component ref to +call `addItems`, `getItem`, `updateItem`, `updateItemId`, or `scrollTo`. + +In vanilla JavaScript, configure and populate the instance directly: + +```ts +import { CodeView, parseDiffFromFile } from '@pierre/diffs'; + +const root = document.querySelector('#review'); +if (root == null) throw new Error('Missing review host'); + +const oldFile = { + name: 'src/value.ts', + contents: 'export const value = 1;', +}; +const newFile = { + name: 'src/value.ts', + contents: 'export const value = 2;', +}; + +const viewer = new CodeView({ + theme: { light: 'pierre-light', dark: 'pierre-dark' }, + stickyHeaders: true, + enableLineSelection: true, + onSelectedLinesChange(selection) { + console.log('selected lines', selection); + }, +}); + +root.style.height = '600px'; +root.style.overflow = 'auto'; +viewer.setup(root); +viewer.setItems([ + { + id: 'diff:src/value.ts', + type: 'diff', + fileDiff: parseDiffFromFile(oldFile, newFile), + version: 0, + }, +]); + +viewer.addItems([ + { + id: 'file:README.md', + type: 'file', + file: { name: 'README.md', contents: '# Review notes' }, + version: 0, + }, +]); +viewer.scrollTo({ + type: 'item', + id: 'diff:src/value.ts', + align: 'start', +}); + +const item = viewer.getItem('diff:src/value.ts'); +if (item != null) { + viewer.updateItem({ + ...item, + collapsed: true, + version: (item.version ?? 0) + 1, + }); +} + +export function removeReviewSurface() { + viewer.cleanUp(); +} +``` + +## Enable item edit mode + +In React, wrap `CodeView` in `EditProvider`. In vanilla JavaScript, pass +`createEditor` in `CodeViewOptions`. Set `edit: true` on each editable item and +increment its version. + +Use `onItemEditChange` for live contents and annotation changes. Use +`onItemEditComplete` to write the final contents into the item, disable edit +mode, assign a fresh `cacheKey`, and increment `version`. Use `getEditor(id)` +for editor commands such as undo, redo, markers, or programmatic edits. + +Read [Edit with React](recipe-edit-react.md) or +[Edit with vanilla JavaScript](recipe-edit-vanilla.md) for the complete editor +lifecycle. diff --git a/skills/diffs/references/recipe-custom-highlighting.md b/skills/diffs/references/recipe-custom-highlighting.md new file mode 100644 index 000000000..22efc967f --- /dev/null +++ b/skills/diffs/references/recipe-custom-highlighting.md @@ -0,0 +1,19 @@ +# Recipe: register custom highlighting + +Register a language or theme before the first surface uses it: + +```ts +import { registerCustomLanguage, registerCustomTheme } from '@pierre/diffs'; + +registerCustomLanguage( + 'my-language', + () => import('./my-language.tmLanguage.json'), + ['myext'] +); + +registerCustomTheme('my-theme', () => import('./my-theme.json')); +``` + +Set `file.lang` to the custom language name. Set `options.theme` to the custom +theme name. Use `registerCustomCSSVariableTheme` when CSS variables supply the +theme colors. diff --git a/skills/diffs/references/recipe-edit-react.md b/skills/diffs/references/recipe-edit-react.md new file mode 100644 index 000000000..1d8fcf87a --- /dev/null +++ b/skills/diffs/references/recipe-edit-react.md @@ -0,0 +1,121 @@ +# Recipe: edit with React + +Mount one stable `EditProvider` above the editable surfaces. The provider +supplies an editor factory. Each active surface or `CodeView` item owns a +separate editor instance. + +## Contents + +- [Edit a standalone file or diff](#edit-a-standalone-file-or-diff) +- [Keep annotations synchronized](#keep-annotations-synchronized) +- [Edit CodeView items](#edit-codeview-items) + +## Edit a standalone file or diff + +Set `edit` on `File`, `FileDiff`, `MultiFileDiff`, or `PatchDiff`. Pass editor +behavior through `editOptions`. + +```tsx +import type { FileContents, FileDiffOptions } from '@pierre/diffs'; +import { Editor, type EditorOptions } from '@pierre/diffs/editor'; +import { EditProvider, MultiFileDiff, Virtualizer } from '@pierre/diffs/react'; +import { useMemo, useRef, useState } from 'react'; + +const oldFile: FileContents = { + name: 'src/value.ts', + contents: 'export const value = 1;', +}; +const initialNewFile: FileContents = { + name: 'src/value.ts', + contents: 'export const value = 2;', +}; +const diffOptions: FileDiffOptions = { + theme: { light: 'pierre-light', dark: 'pierre-dark' }, + diffStyle: 'split', +}; + +function createEditor(options: EditorOptions) { + return new Editor(options); +} + +export function EditableDiff() { + const [edit, setEdit] = useState(false); + const [newFile, setNewFile] = useState(initialNewFile); + const draftRef = useRef(newFile); + const editorRef = useRef | null>(null); + const editOptions = useMemo>( + () => ({ + onAttach(editor) { + editorRef.current = editor; + }, + onChange(file) { + draftRef.current = file; + saveDraft(file); + }, + }), + [] + ); + + function toggleEdit() { + if (edit) setNewFile(draftRef.current); + setEdit((value) => !value); + } + + return ( + + + + + + + + ); +} +``` + +Mount the provider near the application root when many surfaces use edit mode. +Keep `createEditor` and `editOptions` stable. Use `onAttach` when controls need +`undo`, `redo`, `applyEdits`, selections, markers, focus, or other editor APIs. + +## Keep annotations synchronized + +The `onChange` callback can supply the complete current annotation collection. +Replace the application collection when the callback supplies a different array. +Use `isFileAnnotationCollection` or `isDiffAnnotationCollection` to narrow its +type. + +Publish a changed React annotation array inside `flushSync`. This keeps its +coordinates aligned with the edited contents before paint. Store annotation UI +state by a stable metadata ID instead of a line number. + +## Edit `CodeView` items + +Wrap `CodeView` in the same `EditProvider`. Set `edit: true` on an item and +increment its `version`. Pass shared creation options through the `CodeView` +`editOptions` prop. + +Use `onItemEditChange` for live contents and annotation changes. Use +`onItemEditComplete` to commit the final `file` or rebuild the `fileDiff`. In +the same item update, set `edit: false`, assign a fresh `cacheKey`, and +increment `version`. + +Use the `CodeViewHandle.getEditor(id)` method for imperative editor commands. +The item editor keeps its document and history when virtualization removes the +item from the rendered window. + +When a worker pool highlights an editable surface, set +`useTokenTransformer: true` in the worker `highlighterOptions`. diff --git a/skills/diffs/references/recipe-edit-vanilla.md b/skills/diffs/references/recipe-edit-vanilla.md new file mode 100644 index 000000000..35f705928 --- /dev/null +++ b/skills/diffs/references/recipe-edit-vanilla.md @@ -0,0 +1,163 @@ +# Recipe: edit with vanilla JavaScript + +Render a standalone surface first. Then attach one `Editor` to it. Use one +editor for each surface that can be edited at the same time. + +## Contents + +- [Edit a standalone diff](#edit-a-standalone-diff) +- [Edit CodeView items](#edit-codeview-items) + +## Edit a standalone diff + +```ts +import { + FileDiff, + isDiffAnnotationCollection, + type DiffLineAnnotation, + type FileContents, +} from '@pierre/diffs'; +import { Editor } from '@pierre/diffs/editor'; + +interface ThreadMetadata { + id: string; +} + +const hostElement = document.querySelector('#diff'); +if (hostElement == null) throw new Error('Missing diff host'); +const host: HTMLElement = hostElement; + +const oldFile: FileContents = { + name: 'src/value.ts', + contents: 'export const value = 1;', +}; +let newFile: FileContents = { + name: 'src/value.ts', + contents: 'export const value = 2;', +}; +let annotations: DiffLineAnnotation[] = [ + { + side: 'additions', + lineNumber: 1, + metadata: { id: 'value-review' }, + }, +]; + +const view = new FileDiff({ + theme: { light: 'pierre-light', dark: 'pierre-dark' }, + renderAnnotation(annotation) { + const element = document.createElement('p'); + element.textContent = 'Thread ' + annotation.metadata.id; + return element; + }, +}); + +function render() { + view.render({ + fileContainer: host, + oldFile, + newFile, + lineAnnotations: annotations, + }); +} + +render(); + +const editor = new Editor({ + onChange(file, nextAnnotations) { + newFile = { ...newFile, contents: file.contents }; + saveDraft(newFile); + + if ( + nextAnnotations != null && + isDiffAnnotationCollection(nextAnnotations) && + nextAnnotations !== annotations + ) { + annotations = nextAnnotations; + queueMicrotask(render); + } + }, +}); + +const detach = editor.edit(view); + +export function stopEditing() { + detach(); +} + +export function removeSurface() { + editor.cleanUp(); + view.cleanUp(); +} +``` + +The annotation array from `onChange` is the complete current collection. Save it +before a later render can apply old coordinates. Use stable metadata IDs for +application state that belongs to an annotation. + +Use `VirtualizedFile` or `VirtualizedFileDiff` with a `Virtualizer` for a large +standalone surface. Load `@pierre/diffs/editor` with `import()` when edit mode +is optional and the initial bundle must omit the editor. + +## Edit `CodeView` items + +Pass a factory through `CodeViewOptions.createEditor`: + +```ts +import { CodeView } from '@pierre/diffs'; +import { Editor } from '@pierre/diffs/editor'; + +export function mountEditableCodeView(root: HTMLElement) { + const viewer = new CodeView({ + createEditor(options) { + return new Editor(options); + }, + onItemEditChange(item, file, nextAnnotations) { + saveItemDraft(item.id, file, nextAnnotations); + }, + onItemEditComplete(item, file) { + const current = viewer.getItem(item.id); + if (current?.type !== 'file') return; + + const version = (current.version ?? 0) + 1; + viewer.updateItem({ + ...current, + edit: false, + version, + file: { + ...file, + cacheKey: current.id + ':v' + version, + }, + }); + }, + }); + + viewer.setup(root); + viewer.setItems([ + { + id: 'file:src/value.ts', + type: 'file', + file: { + name: 'src/value.ts', + contents: 'export const value = 1;', + }, + edit: true, + version: 0, + }, + ]); + + return viewer; +} +``` + +Set `edit: true` on an item and increment its `version`. In +`onItemEditComplete`, write the final contents into that item, set +`edit: false`, assign a fresh `cacheKey`, and increment `version` again. +`CodeView` creates and removes the item editors. + +Call `viewer.getEditor(id)` for `undo`, `redo`, `applyEdits`, selections, +markers, focus, or other editor commands. Call `viewer.cleanUp()` when the host +removes the viewer. + +When a worker pool highlights an editable surface, set +`useTokenTransformer: true` in the worker `highlighterOptions`. diff --git a/skills/diffs/references/recipe-react.md b/skills/diffs/references/recipe-react.md new file mode 100644 index 000000000..f1048230f --- /dev/null +++ b/skills/diffs/references/recipe-react.md @@ -0,0 +1,37 @@ +# Recipe: render with React + +## Select a surface + +| Input or layout | Component | +| ------------------------------------------- | ---------------- | +| One `FileContents` object | `File` | +| Old and new `FileContents` objects | `MultiFileDiff` | +| Existing `FileDiffMetadata` | `FileDiff` | +| One unified patch string | `PatchDiff` | +| One file with merge conflicts | `UnresolvedFile` | +| One scroll region with many files and diffs | `CodeView` | + +Use `MultiFileDiff` when the app has old and new file contents: + +```tsx +import { MultiFileDiff } from '@pierre/diffs/react'; + +; +``` + +Pass source data, annotations, and slot renderers as component props. Pass +display, theme, interaction, and highlighting settings through `options`. + +Keep file objects and option objects stable when their values do not change. +Wrap a large standalone surface in `Virtualizer`. Use `CodeView` when one scroll +region contains a list of files or diffs. + +Use the matching preload function from `@pierre/diffs/ssr` when the server must +render the initial highlighted markup. diff --git a/skills/diffs/references/recipe-ssr.md b/skills/diffs/references/recipe-ssr.md new file mode 100644 index 000000000..6899453e3 --- /dev/null +++ b/skills/diffs/references/recipe-ssr.md @@ -0,0 +1,20 @@ +# Recipe: preload a diff on the server + +Create props on the server and pass the result to the matching React component: + +```tsx +import { preloadMultiFileDiff } from '@pierre/diffs/ssr'; +import { MultiFileDiff } from '@pierre/diffs/react'; + +const preloaded = await preloadMultiFileDiff({ + oldFile: { name: 'src/value.ts', contents: oldSource }, + newFile: { name: 'src/value.ts', contents: newSource }, + options: { theme: 'pierre-dark', diffStyle: 'split' }, +}); + +; +``` + +Select the preload function that matches the client component. Use +`preloadDiffHTML` or `preloadUnresolvedFileHTML` when the host needs an HTML +string only. diff --git a/skills/diffs/references/recipe-vanilla.md b/skills/diffs/references/recipe-vanilla.md new file mode 100644 index 000000000..ebb8841b1 --- /dev/null +++ b/skills/diffs/references/recipe-vanilla.md @@ -0,0 +1,41 @@ +# Recipe: render with vanilla JavaScript + +## Select a surface + +| Input or layout | Class | +| ------------------------------------------- | --------------------- | +| One `FileContents` object | `File` | +| Existing or parsed `FileDiffMetadata` | `FileDiff` | +| One file with merge conflicts | `UnresolvedFile` | +| One large file | `VirtualizedFile` | +| One large diff | `VirtualizedFileDiff` | +| One scroll region with many files and diffs | `CodeView` | + +Parse the files, create the view, and render it into a host: + +```ts +import { FileDiff, parseDiffFromFile } from '@pierre/diffs'; + +const host = document.querySelector('#diff'); +if (host == null) throw new Error('Missing diff host'); + +const fileDiff = parseDiffFromFile( + { name: 'src/value.ts', contents: oldSource }, + { name: 'src/value.ts', contents: newSource } +); + +const view = new FileDiff({ + diffStyle: 'split', + theme: 'pierre-dark', +}); + +view.render({ fileContainer: host, fileDiff }); +``` + +Keep the class instance while the host remains mounted. Call `render` again when +the source data changes. Call `setOptions`, `setThemeType`, +`setLineAnnotations`, or `setSelectedLines` for targeted updates. + +Use a `Virtualizer` with `VirtualizedFile` or `VirtualizedFileDiff` for one +large surface. Use `CodeView` for a list that shares one scroll region. Call +`cleanUp()` when the host removes the surface. diff --git a/skills/diffs/references/recipe-workers.md b/skills/diffs/references/recipe-workers.md new file mode 100644 index 000000000..fbe7cc6f8 --- /dev/null +++ b/skills/diffs/references/recipe-workers.md @@ -0,0 +1,28 @@ +# Recipe: use a worker pool + +Wrap React diff surfaces in one provider: + +```tsx +import { WorkerPoolContextProvider } from '@pierre/diffs/react'; + + + new Worker(new URL('@pierre/diffs/worker/worker.js', import.meta.url), { + type: 'module', + }), + }} + highlighterOptions={{ + langs: ['typescript', 'tsx'], + theme: { light: 'pierre-light', dark: 'pierre-dark' }, + }} +> + {children} +; +``` + +For vanilla JavaScript, call `getOrCreateWorkerPoolSingleton` and pass the +result as the second constructor argument to a render class. Call +`terminateWorkerPoolSingleton()` when the application tears down the shared +pool. diff --git a/skills/theme/SKILL.md b/skills/theme/SKILL.md new file mode 100644 index 000000000..8b69b1c19 --- /dev/null +++ b/skills/theme/SKILL.md @@ -0,0 +1,35 @@ +--- +name: theme +description: + Use when an app needs a Pierre light or dark theme from @pierre/theme for + Shiki, VS Code, Cursor, or Zed, including soft, vibrant, protanopia, + deuteranopia, and tritanopia variants. +--- + +# `@pierre/theme` + +Use `@pierre/theme` for Pierre syntax and editor themes. The package supplies +ten immutable Shiki and VS Code theme objects. + +## Install + +```bash +pnpm add @pierre/theme +``` + +## Select an API reference + +| Need | Reference | +| --------------------------------------- | ---------------------------------------- | +| List every package entry and export | [API reference](references/api.md) | +| Compare all themes and select a variant | [Theme variants](references/variants.md) | + +## Select a recipe + +| Task | Recipe | +| ----------------------------------- | ------------------------------------------------------- | +| Load a Pierre theme in Shiki | [Use a theme with Shiki](references/recipe-shiki.md) | +| Install a Pierre theme in an editor | [Use a theme in an editor](references/recipe-editor.md) | + +Use the `theming` skill when the app must switch themes at runtime or follow a +color mode. diff --git a/skills/theme/references/api.md b/skills/theme/references/api.md new file mode 100644 index 000000000..e9f41981f --- /dev/null +++ b/skills/theme/references/api.md @@ -0,0 +1,45 @@ +# `@pierre/theme` API reference + +This reference lists every public package entry. + +## Main entry + +| Import | Export | Purpose | +| --------------- | ------------ | ------------------------------------------- | +| `@pierre/theme` | `themeNames` | Lists the ten theme names in package order. | + +## Theme entries + +Each entry has one default export. The export is an immutable Pierre theme +object. + +| Import | Theme name | Color scheme | Purpose | +| ---------------------------------------------------- | -------------------------------------- | ------------ | ---------------------------------------------------- | +| `@pierre/theme/pierre-light` | `pierre-light` | Light | Supplies the standard light theme. | +| `@pierre/theme/pierre-dark` | `pierre-dark` | Dark | Supplies the standard dark theme. | +| `@pierre/theme/pierre-light-soft` | `pierre-light-soft` | Light | Supplies the soft light theme. | +| `@pierre/theme/pierre-dark-soft` | `pierre-dark-soft` | Dark | Supplies the soft dark theme. | +| `@pierre/theme/pierre-light-vibrant` | `pierre-light-vibrant` | Light | Supplies the Display-P3 light theme. | +| `@pierre/theme/pierre-dark-vibrant` | `pierre-dark-vibrant` | Dark | Supplies the Display-P3 dark theme. | +| `@pierre/theme/pierre-light-protanopia-deuteranopia` | `pierre-light-protanopia-deuteranopia` | Light | Supplies the light red-green color-vision variant. | +| `@pierre/theme/pierre-dark-protanopia-deuteranopia` | `pierre-dark-protanopia-deuteranopia` | Dark | Supplies the dark red-green color-vision variant. | +| `@pierre/theme/pierre-light-tritanopia` | `pierre-light-tritanopia` | Light | Supplies the light blue-yellow color-vision variant. | +| `@pierre/theme/pierre-dark-tritanopia` | `pierre-dark-tritanopia` | Dark | Supplies the dark blue-yellow color-vision variant. | + +## Raw theme entries + +`@pierre/theme/themes/*` exposes each generated JSON file. Replace `*` with a +theme file name, including `.json`. + +## Theme object + +Each default export has these immutable fields: + +| Field | Purpose | +| --------------------- | ---------------------------------------------- | +| `name` | Provides the stable theme identifier. | +| `displayName` | Provides the label for a theme selector. | +| `type` | Identifies the `light` or `dark` color scheme. | +| `colors` | Maps VS Code workbench color keys to colors. | +| `tokenColors` | Defines TextMate token styles. | +| `semanticTokenColors` | Defines semantic token styles. | diff --git a/skills/theme/references/recipe-editor.md b/skills/theme/references/recipe-editor.md new file mode 100644 index 000000000..40fa45392 --- /dev/null +++ b/skills/theme/references/recipe-editor.md @@ -0,0 +1,16 @@ +# Recipe: use a Pierre theme in an editor + +## VS Code or Cursor + +1. Install the **Pierre Theme** extension from the editor marketplace. +2. Open the color theme selector. +3. Select a `Pierre` theme. + +## Zed + +1. Install the **Pierre** extension from the Zed extension registry. +2. Open the theme selector. +3. Select a `Pierre` theme. + +The editor extensions include the standard, soft, protanopia and deuteranopia, +and tritanopia treatments. diff --git a/skills/theme/references/recipe-shiki.md b/skills/theme/references/recipe-shiki.md new file mode 100644 index 000000000..0f3fdda7e --- /dev/null +++ b/skills/theme/references/recipe-shiki.md @@ -0,0 +1,22 @@ +# Recipe: use a Pierre theme with Shiki + +Install the package: + +```bash +pnpm add @pierre/theme shiki +``` + +Import the theme object and pass it to Shiki: + +```ts +import pierreDark from '@pierre/theme/pierre-dark'; +import { codeToHtml } from 'shiki'; + +const html = await codeToHtml(source, { + lang: 'typescript', + theme: pierreDark, +}); +``` + +Import a matching light and dark pair when the app supports both color schemes. +Use the `theming` skill when the user can change the active theme at runtime. diff --git a/skills/theme/references/variants.md b/skills/theme/references/variants.md new file mode 100644 index 000000000..1986a4142 --- /dev/null +++ b/skills/theme/references/variants.md @@ -0,0 +1,17 @@ +# Pierre theme variants + +Select one treatment and use its light and dark entries as a pair. + +| Treatment | Light entry | Dark entry | Select it for | +| --------------------------- | -------------------------------------- | ------------------------------------- | ----------------------------------------- | +| Standard | `pierre-light` | `pierre-dark` | The default Pierre appearance. | +| Soft | `pierre-light-soft` | `pierre-dark-soft` | A lower-contrast appearance. | +| Vibrant | `pierre-light-vibrant` | `pierre-dark-vibrant` | Display-P3 output in Shiki or a web page. | +| Protanopia and deuteranopia | `pierre-light-protanopia-deuteranopia` | `pierre-dark-protanopia-deuteranopia` | Red-green color-vision support. | +| Tritanopia | `pierre-light-tritanopia` | `pierre-dark-tritanopia` | Blue-yellow color-vision support. | + +The editor extensions include the eight sRGB themes. The vibrant themes use CSS +Display-P3 color syntax and target web output. + +Use the standard pair unless the product specifies another treatment. Use the +same treatment for both color schemes. diff --git a/skills/theming/SKILL.md b/skills/theming/SKILL.md new file mode 100644 index 000000000..230562d65 --- /dev/null +++ b/skills/theming/SKILL.md @@ -0,0 +1,49 @@ +--- +name: theming +description: + Use when an app uses @pierre/theming to list, resolve, select, switch, + persist, or apply themes, including color modes, theme controllers, React + state, UI colors, and bundled Pierre or Shiki collections. +--- + +# `@pierre/theming` + +Use `@pierre/theming` to manage runtime themes. The package separates theme +metadata, theme resolution, selected state, and UI color mapping. + +## Install + +```bash +pnpm add @pierre/theming +``` + +Install optional peer dependencies only for the selected entry. The React entry +needs `react`. The themes entry needs `@pierre/theme`, `@shikijs/themes`, and +`shiki`. + +## Select an API reference + +| Need | Reference | +| -------------------------------------------------------------- | -------------------------------------------- | +| Create a collection, catalog, descriptor, or bundled theme set | [Collections API](references/collections.md) | +| Register, load, seed, or cache theme objects | [Resolver API](references/resolver.md) | +| Track the color mode and selected light and dark themes | [Controller API](references/controller.md) | +| Normalize workbench colors or derive colors | [Colors API](references/colors.md) | +| Read controller state in React | [React API](references/react.md) | + +## Select a controller recipe + +| Situation | Recipe | +| ---------------------------------------------------------------------------- | --------------------------------------------------------------------- | +| One app or page shares one color mode and one light/dark selection | [Use one controller](references/recipe-one-controller.md) | +| Independent areas need separate selections or separate controller lifecycles | [Use multiple controllers](references/recipe-multiple-controllers.md) | + +Use one controller by default. Use multiple controllers only when the theme +state must differ between areas. + +## Select a framework recipe + +| Host | Recipe | +| --------------------------------------- | ------------------------------------------------------ | +| Vanilla JavaScript or another framework | [Use vanilla JavaScript](references/recipe-vanilla.md) | +| React | [Use React](references/recipe-react.md) | diff --git a/skills/theming/references/collections.md b/skills/theming/references/collections.md new file mode 100644 index 000000000..a5e1aa095 --- /dev/null +++ b/skills/theming/references/collections.md @@ -0,0 +1,61 @@ +# Collections API + +This reference covers collection APIs from `@pierre/theming` and all exports +from `@pierre/theming/themes`. + +## `@pierre/theming` exports + +| Export | Kind | Purpose | +| --------------------------- | -------- | ------------------------------------------------------------------ | +| `createThemeCollection` | Function | Creates an immutable ordered set of theme descriptors. | +| `ThemeCollection` | Type | Defines collection queries, transforms, and resolver registration. | +| `ThemeCollectionComparator` | Type | Compares two descriptors for `orderBy`. | +| `ThemeCollectionEntry` | Type | Accepts one descriptor or collection source. | +| `ThemeCollectionFilter` | Type | Filters by `collection` and `colorScheme`. | +| `ThemeCollectionInput` | Type | Accepts one entry or an iterable of entries. | +| `ThemeCollectionSource` | Type | Defines an object that returns theme descriptors. | +| `ThemeDescriptor` | Type | Describes one named theme and its lazy loader. | +| `createThemeCatalog` | Function | Creates a collection with default light and dark theme names. | +| `ThemeCatalog` | Type | Adds default theme names to `ThemeCollection`. | + +## `ThemeDescriptor` fields + +| Field | Purpose | +| ------------- | --------------------------------------- | +| `name` | Stores the stable theme identifier. | +| `load` | Loads the theme object. | +| `colorScheme` | Labels the descriptor as light or dark. | +| `collection` | Groups related themes. | +| `displayName` | Supplies a user-facing label. | + +## `ThemeCollection` methods + +| Method | Purpose | +| ------------------------ | ---------------------------------------------------- | +| `getTheme(name)` | Gets one descriptor by name. | +| `getThemes(filter?)` | Lists descriptors that match an optional filter. | +| `getThemeNames(filter?)` | Lists names that match an optional filter. | +| `hasTheme(name)` | Tests whether the collection has a name. | +| `orderBy(compare)` | Returns a new collection in comparator order. | +| `pick(names)` | Returns a new collection in the supplied name order. | +| `registerInto(resolver)` | Adds every descriptor loader to a resolver. | + +## `ThemeCatalog` fields + +| Field | Purpose | +| ----------------------- | ------------------------------ | +| `defaultLightThemeName` | Names the initial light theme. | +| `defaultDarkThemeName` | Names the initial dark theme. | + +## `@pierre/theming/themes` exports + +| Export | Kind | Purpose | +| -------------------- | -------- | ----------------------------------------------------------------------------- | +| `themes` | Value | Combines the Pierre and Shiki collections in stable order. | +| `pierreThemes` | Value | Provides lazy descriptors for the ten Pierre themes. | +| `shikiThemes` | Value | Provides lazy descriptors for the bundled Shiki themes. | +| `createTheme` | Function | Creates a descriptor whose loader normalizes a raw Shiki or VS Code theme. | +| `CreateThemeOptions` | Type | Defines the name, loader, and optional descriptor metadata for `createTheme`. | + +Use `createTheme` for a raw Shiki or VS Code theme. Use a `ThemeDescriptor` +directly when the loader already returns a compatible `ThemeLike` object. diff --git a/skills/theming/references/colors.md b/skills/theming/references/colors.md new file mode 100644 index 000000000..6c5156873 --- /dev/null +++ b/skills/theming/references/colors.md @@ -0,0 +1,30 @@ +# Colors API + +This reference covers every export from `@pierre/theming/color`. + +## Exports + +| Export | Kind | Purpose | +| ---------------------- | -------- | -------------------------------------------------------------- | +| `normalizeThemeColors` | Function | Resolves the UI color fallback chains in a `ThemeLike` object. | +| `colorUtils` | Object | Groups pure color checks and transforms. | + +`normalizeThemeColors(theme)` returns a memoized `ThemeLike` object. Use its +`colors` map for the UI around syntax-highlighted code. + +## `colorUtils` methods + +| Method | Purpose | +| ---------------------------------------------------- | -------------------------------------------------------------- | +| `relativeLuminance(color)` | Calculates WCAG luminance for a hex color. | +| `contrastRatio(a, b)` | Calculates the contrast ratio between two luminance values. | +| `compositeOverBg(foreground, background?)` | Flattens a translucent hex color over a background. | +| `isFullyTransparent(color?)` | Tests whether a hex color has zero alpha. | +| `isDarkSurface(background?, foregroundHint?)` | Classifies a surface as dark or light. | +| `surfacesMatch(a?, b?)` | Tests whether two hex surfaces produce the same visible color. | +| `hoverWouldEraseText(hover, background, foreground)` | Tests whether a hover surface removes text contrast. | +| `pickReadableForeground(background, candidates)` | Selects the candidate with the best readable contrast. | +| `deriveMutedFg(foreground, background)` | Derives a muted foreground with readable contrast. | + +The color methods measure hex colors. A method returns its documented empty +result when it cannot measure another CSS color format. diff --git a/skills/theming/references/controller.md b/skills/theming/references/controller.md new file mode 100644 index 000000000..fa94b39f8 --- /dev/null +++ b/skills/theming/references/controller.md @@ -0,0 +1,59 @@ +# Controller API + +This reference covers controller and shared theme types from `@pierre/theming`. + +## Exports + +| Export | Kind | Purpose | +| ----------------------------- | -------- | ------------------------------------------------------------------ | +| `createThemeController` | Function | Creates state for a color mode and selected light and dark themes. | +| `ThemeController` | Type | Defines controller state, mutations, subscription, and cleanup. | +| `ThemeControllerOptions` | Type | Selects a catalog or resolver and configures initial state. | +| `ThemeControllerState` | Type | Describes the current selection and resolved theme. | +| `PendingThemeResolution` | Type | Identifies the active theme load. | +| `ThemeResolutionError` | Type | Describes a failed active theme load. | +| `ThemeResolutionErrorContext` | Type | Identifies the name and scheme for a failed load callback. | +| `ThemePersistence` | Type | Defines custom `load` and `save` methods. | +| `ThemeSelection` | Type | Describes the persisted mode and theme names. | +| `ColorMode` | Type | Accepts `light`, `dark`, or `system`. | +| `ColorScheme` | Type | Accepts concrete `light` or `dark`. | +| `ThemeLike` | Type | Defines the shared resolved-theme shape. | + +## `ThemeControllerOptions` fields + +| Field | Purpose | +| ----------------------- | ------------------------------------------------------------------- | +| `catalog` | Supplies descriptors and catalog default names. | +| `resolver` | Supplies an existing resolver or shares one with other controllers. | +| `defaultLightThemeName` | Overrides the initial light theme name. | +| `defaultDarkThemeName` | Overrides the initial dark theme name. | +| `defaultMode` | Sets the initial color mode. | +| `storageKey` | Stores the selection in browser local storage. | +| `persistence` | Supplies custom selection storage. | +| `preloadInactive` | Resolves the inactive color-scheme theme too. | +| `onResolutionError` | Receives an active theme load error. | + +Pass a `catalog`, a `resolver`, or a catalog with a shared resolver. + +## `ThemeController` members + +| Member | Purpose | +| ------------------------------------- | ---------------------------------------------------------------- | +| `resolver` | Exposes the controller's resolver. | +| `getState()` | Returns the current immutable state object. | +| `setColorMode(mode)` | Selects light, dark, or system mode. | +| `setThemeNameForScheme(scheme, name)` | Selects the theme for one color scheme. | +| `subscribe(listener)` | Subscribes to state changes and returns an unsubscribe function. | +| `destroy()` | Removes the system color-scheme listener. | + +## `ThemeControllerState` fields + +| Field | Purpose | +| ------------------------ | -------------------------------------------------- | +| `lightThemeName` | Stores the selected light theme name. | +| `darkThemeName` | Stores the selected dark theme name. | +| `mode` | Stores the selected color mode. | +| `resolvedColorScheme` | Provides the active concrete light or dark scheme. | +| `resolvedTheme` | Provides the active resolved theme object. | +| `pendingThemeResolution` | Identifies an active theme load. | +| `resolutionError` | Describes the latest active load failure. | diff --git a/skills/theming/references/react.md b/skills/theming/references/react.md new file mode 100644 index 000000000..94e3bccb7 --- /dev/null +++ b/skills/theming/references/react.md @@ -0,0 +1,10 @@ +# React API + +This reference lists every export from `@pierre/theming/react`. + +| Export | Kind | Purpose | +| -------------------- | ---- | ------------------------------------------------------------------------- | +| `useThemeController` | Hook | Subscribes to a `ThemeController` and returns its `ThemeControllerState`. | + +The hook uses the controller state for both client and server snapshots. Create +the controller outside the component that reads it. diff --git a/skills/theming/references/recipe-multiple-controllers.md b/skills/theming/references/recipe-multiple-controllers.md new file mode 100644 index 000000000..8dfa29942 --- /dev/null +++ b/skills/theming/references/recipe-multiple-controllers.md @@ -0,0 +1,39 @@ +# Recipe: use multiple theme controllers + +Use multiple controllers when independent areas need different modes, +selections, storage, or lifecycles. Share one resolver when those areas use the +same theme loaders and cache. + +```ts +import { + createThemeCatalog, + createThemeController, + createThemeResolver, +} from '@pierre/theming'; +import { themes } from '@pierre/theming/themes'; + +const catalog = createThemeCatalog({ + themes, + defaultLightThemeName: 'pierre-light', + defaultDarkThemeName: 'pierre-dark', +}); + +const resolver = createThemeResolver(); + +export const workspaceTheme = createThemeController({ + catalog, + resolver, + storageKey: 'workspace-theme', +}); + +export const previewTheme = createThemeController({ + catalog, + resolver, + defaultMode: 'light', + storageKey: 'preview-theme', +}); +``` + +Subscribe each area to its own controller. Call `destroy()` on each controller +when its area is removed. Use distinct storage keys when selections must remain +independent. diff --git a/skills/theming/references/recipe-one-controller.md b/skills/theming/references/recipe-one-controller.md new file mode 100644 index 000000000..01e4ce9ed --- /dev/null +++ b/skills/theming/references/recipe-one-controller.md @@ -0,0 +1,26 @@ +# Recipe: use one theme controller + +Use one controller when the app shares one color mode and one light/dark theme +selection. + +Create the catalog and controller in one module: + +```ts +import { createThemeCatalog, createThemeController } from '@pierre/theming'; +import { themes } from '@pierre/theming/themes'; + +export const themeCatalog = createThemeCatalog({ + themes, + defaultLightThemeName: 'pierre-light', + defaultDarkThemeName: 'pierre-dark', +}); + +export const themeController = createThemeController({ + catalog: themeCatalog, + defaultMode: 'system', + storageKey: 'app-theme', +}); +``` + +Import the same controller wherever the app reads or changes theme state. Call +`destroy()` when the application tears down the controller module. diff --git a/skills/theming/references/recipe-react.md b/skills/theming/references/recipe-react.md new file mode 100644 index 000000000..b486b5637 --- /dev/null +++ b/skills/theming/references/recipe-react.md @@ -0,0 +1,34 @@ +# Recipe: use a theme controller in React + +Create the controller in a module. Read it with `useThemeController`: + +```tsx +import { useEffect } from 'react'; +import { useThemeController } from '@pierre/theming/react'; + +import { themeController } from './theme-controller'; + +export function ThemeControls() { + const state = useThemeController(themeController); + + useEffect(() => { + document.documentElement.dataset.colorScheme = state.resolvedColorScheme; + }, [state.resolvedColorScheme]); + + return ( + + ); +} +``` + +Pass `state.resolvedTheme` to code or tree surfaces. Use `normalizeThemeColors` +when a React component needs workbench UI colors. diff --git a/skills/theming/references/recipe-vanilla.md b/skills/theming/references/recipe-vanilla.md new file mode 100644 index 000000000..1a9a21f9e --- /dev/null +++ b/skills/theming/references/recipe-vanilla.md @@ -0,0 +1,32 @@ +# Recipe: use a theme controller in vanilla JavaScript + +Create a controller with the one-controller or multiple-controller recipe. Apply +each state change to the document: + +```ts +import { normalizeThemeColors } from '@pierre/theming/color'; +import { themeController } from './theme-controller'; + +function applyTheme() { + const state = themeController.getState(); + document.documentElement.dataset.colorScheme = state.resolvedColorScheme; + + if (state.resolvedTheme == null) return; + const { colors } = normalizeThemeColors(state.resolvedTheme); + document.documentElement.style.setProperty( + '--app-background', + colors?.['editor.background'] ?? state.resolvedTheme.bg ?? '' + ); + document.documentElement.style.setProperty( + '--app-foreground', + colors?.['editor.foreground'] ?? state.resolvedTheme.fg ?? '' + ); +} + +applyTheme(); +const unsubscribe = themeController.subscribe(applyTheme); +``` + +Call `themeController.setColorMode(mode)` from a mode control. Call +`setThemeNameForScheme(scheme, name)` from a theme selector. Call +`unsubscribe()` when the host no longer needs updates. diff --git a/skills/theming/references/resolver.md b/skills/theming/references/resolver.md new file mode 100644 index 000000000..73568b4f0 --- /dev/null +++ b/skills/theming/references/resolver.md @@ -0,0 +1,35 @@ +# Resolver API + +This reference covers the resolver exports from `@pierre/theming`. + +## Exports + +| Export | Kind | Purpose | +| ------------------------ | -------- | ------------------------------------------------------------- | +| `createThemeResolver` | Function | Creates an isolated loader registry and resolved-theme cache. | +| `ThemeResolver` | Type | Defines the resolver methods. | +| `ThemeLoader` | Type | Defines an asynchronous theme loader. | +| `DuplicateThemeError` | Class | Reports a second registration for the same name. | +| `UnregisteredThemeError` | Class | Reports a resolve request for a name with no loader. | +| `UnresolvedThemeError` | Class | Reports a synchronous batch read with an unresolved name. | + +## `ThemeResolver` methods + +| Method | Purpose | +| ------------------------------------- | -------------------------------------------------------- | +| `registerTheme(name, loader)` | Registers one loader. | +| `registerThemeIfAbsent(name, loader)` | Registers one loader only when the name is free. | +| `hasRegisteredTheme(name)` | Tests whether a loader exists. | +| `resolveTheme(name)` | Loads and caches one theme. | +| `resolveThemes(names)` | Loads and caches themes in input order. | +| `getResolvedTheme(name)` | Reads one cached theme or returns `undefined`. | +| `getResolvedThemes(names)` | Reads cached themes in input order. | +| `getResolvedOrResolveTheme(name)` | Returns a cached theme or starts its load. | +| `hasResolvedTheme(name)` | Tests whether one cached theme exists. | +| `hasResolvedThemes(names)` | Tests whether all named themes exist in the cache. | +| `seedResolvedTheme(name, theme)` | Adds one resolved object without a loader. | +| `seedResolvedThemes(entries)` | Adds several resolved objects without loaders. | +| `clearResolvedThemes()` | Clears cached themes and active loads but keeps loaders. | + +Each loader returns a `ThemeLike` object or a module object with that value on +`default`. diff --git a/skills/trees/SKILL.md b/skills/trees/SKILL.md new file mode 100644 index 000000000..d3cc23fb7 --- /dev/null +++ b/skills/trees/SKILL.md @@ -0,0 +1,39 @@ +--- +name: trees +description: + Use when an app uses @pierre/trees to render or control a file tree, including + React, vanilla JavaScript, SSR, web components, selection, search, rename, + drag and drop, icons, git status, and themes. +--- + +# `@pierre/trees` + +Use `@pierre/trees` for an interactive file tree. Public state and callbacks use +path strings. + +## Install + +```bash +pnpm add @pierre/trees +``` + +Install `react` and `react-dom` when the app uses the React entry. + +## Select an API reference + +| Entry | Reference | +| ------------------------------ | ------------------------------------------------------ | +| `@pierre/trees` | [Core API](references/api-core.md) | +| `@pierre/trees/react` | [React API](references/api-react.md) | +| `@pierre/trees/ssr` | [SSR API](references/api-ssr.md) | +| `@pierre/trees/web-components` | [Web components API](references/api-web-components.md) | + +## Select a recipe + +| Task | Recipe | +| ------------------------------------------------------- | ------------------------------------------------------ | +| Render and update a tree in React | [Use React](references/recipe-react.md) | +| Render and update a tree without React | [Use vanilla JavaScript](references/recipe-vanilla.md) | +| Preload a tree on the server | [Use SSR](references/recipe-ssr.md) | +| Apply a resolved Shiki or VS Code theme | [Apply a theme](references/recipe-theme.md) | +| Configure search, rename, drag and drop, and git status | [Add interactions](references/recipe-interactions.md) | diff --git a/skills/trees/references/api-core.md b/skills/trees/references/api-core.md new file mode 100644 index 000000000..22727767e --- /dev/null +++ b/skills/trees/references/api-core.md @@ -0,0 +1,173 @@ +# Core API + +This reference lists every export from `@pierre/trees` and every public +`FileTree` member. + +## Contents + +- [Runtime values](#runtime-values) +- [`FileTree` members](#filetree-members) +- [Configuration and state types](#configuration-and-state-types) +- [Mutation types](#mutation-types) +- [Interaction types](#interaction-types) +- [Presentation types](#presentation-types) +- [Constants](#constants) + +## Runtime values + +| Export | Kind | Purpose | +| ------------------------------- | -------- | ----------------------------------------------------------------- | +| `FileTree` | Class | Owns the tree model, renders it, and exposes path-based controls. | +| `prepareFileTreeInput` | Function | Prepares and optionally sorts a path list for reuse. | +| `preparePresortedFileTreeInput` | Function | Prepares a path list that already has final order. | +| `preloadFileTree` | Function | Creates server-rendered tree markup and hydration data. | +| `serializeFileTreeSsrPayload` | Function | Joins an SSR payload into one host markup string. | +| `themeToTreeStyles` | Function | Maps a resolved theme to tree host CSS properties. | +| `getBuiltInSpriteSheet` | Function | Gets the SVG sprite for a built-in icon set. | +| `createFileTreeIconResolver` | Function | Creates an icon resolver from an icon configuration. | + +## `FileTree` members + +| Member | Purpose | +| -------------------------------- | ----------------------------------------------------------- | +| `new FileTree(options)` | Creates a model from paths or prepared input. | +| `FileTree.LoadedCustomComponent` | Reports whether the file-tree custom element module loaded. | +| `render(props)` | Mounts the tree into a wrapper or host element. | +| `hydrate(props)` | Attaches the model to preloaded host markup. | +| `unmount()` | Removes the mounted view and keeps the model. | +| `cleanUp()` | Removes the view and destroys model resources. | +| `getFileTreeContainer()` | Gets the mounted host element. | +| `getItem(path)` | Gets a path handle or `null`. | +| `getFocusedItem()` | Gets the focused item handle or `null`. | +| `getFocusedPath()` | Gets the focused path or `null`. | +| `getSelectedPaths()` | Gets the selected paths. | +| `getComposition()` | Gets the current header and context-menu configuration. | +| `getItemHeight()` | Gets the resolved row height. | +| `getDensityFactor()` | Gets the resolved density factor. | +| `subscribe(listener)` | Subscribes to model changes. | +| `focusPath(path)` | Focuses a path. | +| `focusNearestPath(path)` | Focuses and returns the nearest available path. | +| `scrollToPath(path, options?)` | Scrolls a path into view. | +| `add(path)` | Adds one path. | +| `remove(path, options?)` | Removes one path. | +| `move(from, to, options?)` | Moves one path. | +| `batch(operations)` | Applies several path mutations together. | +| `resetPaths(paths, options?)` | Replaces the full path set. | +| `onMutation(type, handler)` | Subscribes to mutation events. | +| `setSearch(value)` | Sets or clears the search query. | +| `openSearch(initialValue?)` | Opens the search session. | +| `closeSearch()` | Closes the search session. | +| `isSearchOpen()` | Tests whether search is open. | +| `getSearchValue()` | Gets the search query. | +| `getSearchMatchingPaths()` | Gets paths that match the query. | +| `focusNextSearchMatch()` | Focuses the next search match. | +| `focusPreviousSearchMatch()` | Focuses the previous search match. | +| `startRenaming(path?, options?)` | Starts inline rename and reports whether it started. | +| `setGitStatus(status?)` | Replaces all git status entries. | +| `applyGitStatusPatch(patch)` | Applies a partial git status update. | +| `setIcons(icons?)` | Replaces the icon configuration. | +| `setComposition(composition?)` | Replaces header and context-menu configuration. | + +## Configuration and state types + +| Export | Purpose | +| -------------------------- | --------------------------------------------------------------- | +| `FileTreeOptions` | Defines input, behavior, rendering, and presentation options. | +| `FileTreePreparedInput` | Holds a reusable prepared path list. | +| `FileTreeInitialExpansion` | Selects closed, open, or depth-based initial expansion. | +| `FileTreeSortComparator` | Compares two path entries. | +| `FileTreeSortEntry` | Describes one path for a sort comparator. | +| `FileTreeRenderOptions` | Configures row height, row count, overscan, and sticky folders. | +| `FileTreeRenderProps` | Selects the wrapper or existing host for `render`. | +| `FileTreeHydrationProps` | Selects the host for `hydrate`. | +| `FileTreeVisibleRow` | Describes one visible tree row. | +| `FileTreeItemHandle` | Represents a file or directory item. | +| `FileTreeFileHandle` | Controls one file item. | +| `FileTreeDirectoryHandle` | Controls one directory item and its expansion. | +| `FileTreeListener` | Defines a model subscription callback. | +| `FileTreeSsrPayload` | Holds the host and shadow markup for server output. | + +## Mutation types + +| Export | Purpose | +| ----------------------------------- | -------------------------------------------------------------- | +| `FileTreeMutationHandle` | Defines the public path mutation methods. | +| `FileTreeBatchOperation` | Describes one add, remove, or move in a batch. | +| `FileTreeCollisionStrategy` | Selects error, replace, or skip behavior for a move collision. | +| `FileTreeMoveOptions` | Configures move collision behavior. | +| `FileTreeRemoveOptions` | Configures recursive removal. | +| `FileTreeResetOptions` | Configures a path reset and optional prepared input. | +| `FileTreeResetPreparedOptions` | Configures a reset that uses prepared input. | +| `FileTreeMutationEvent` | Represents any mutation event. | +| `FileTreeMutationSemanticEvent` | Represents an add, remove, move, or reset event. | +| `FileTreeMutationEventType` | Names a mutation operation. | +| `FileTreeMutationEventForType` | Selects the event shape for an operation name. | +| `FileTreeMutationEventInvalidation` | Describes the state invalidation from a mutation. | +| `FileTreeAddEvent` | Describes one add result. | +| `FileTreeRemoveEvent` | Describes one remove result. | +| `FileTreeMoveEvent` | Describes one move result. | +| `FileTreeResetEvent` | Describes one reset result. | +| `FileTreeBatchEvent` | Describes one batch result. | + +## Interaction types + +| Export | Purpose | +| --------------------------------- | -------------------------------------------------- | +| `FileTreeSelectionChangeListener` | Receives the selected path list. | +| `FileTreeSearchChangeListener` | Receives the current search query. | +| `FileTreeSearchSessionHandle` | Defines search session methods. | +| `FileTreeSearchMode` | Selects how nonmatching rows appear. | +| `FileTreeSearchBlurBehavior` | Selects search behavior after focus leaves. | +| `FileTreeScrollOffset` | Selects top, center, or nearest scroll alignment. | +| `FileTreeScrollToPathOptions` | Configures focus and alignment for `scrollToPath`. | +| `FileTreeDragAndDropConfig` | Configures drag rules and completion callbacks. | +| `FileTreeDropTarget` | Describes the current drop target. | +| `FileTreeDropContext` | Describes dragged paths and their target. | +| `FileTreeDropResult` | Describes the completed move or batch operation. | +| `FileTreeRenamingConfig` | Configures rename rules and callbacks. | +| `FileTreeRenamingItem` | Describes the item offered to a rename rule. | +| `FileTreeRenameEvent` | Describes a completed rename. | + +## Presentation types + +| Export | Purpose | +| ---------------------------------- | ----------------------------------------------------------- | +| `FileTreeCompositionOptions` | Configures header and context-menu composition. | +| `FileTreeHeaderCompositionOptions` | Supplies header HTML or a header renderer. | +| `ContextMenuItem` | Describes the file or directory for a context menu. | +| `ContextMenuOpenContext` | Supplies menu position, close, and focus controls. | +| `ContextMenuAnchorRect` | Describes the menu anchor rectangle. | +| `ContextMenuTriggerMode` | Selects right-click, button, or both triggers. | +| `ContextMenuButtonVisibility` | Selects when the row menu button appears. | +| `FileTreeRowDecoration` | Describes text or icon content in the decoration lane. | +| `FileTreeRowDecorationContext` | Supplies the item and visible row to a decoration renderer. | +| `FileTreeRowDecorationRenderer` | Produces one row decoration. | +| `GitStatus` | Names a supported git status. | +| `GitStatusEntry` | Assigns a git status to one path. | +| `FileTreeGitStatusPatch` | Adds, changes, or removes git status entries. | +| `FileTreeBuiltInIconSet` | Names a built-in icon set. | +| `FileTreeIconConfig` | Configures built-in and custom icon rules. | +| `FileTreeIcons` | Accepts an icon set name or icon configuration. | +| `RemappedIcon` | Names or defines a replacement SVG symbol. | +| `FileTreeDensity` | Accepts a density keyword or numeric factor. | +| `FileTreeDensityKeyword` | Names compact, default, or relaxed density. | +| `FileTreeDensityPreset` | Holds a density factor and row height. | +| `TreeThemeInput` | Defines the resolved theme accepted by `themeToTreeStyles`. | +| `TreeThemeStyles` | Maps tree CSS property names to values. | + +## Constants + +| Export | Purpose | +| ---------------------------------------------- | --------------------------------------------------------------- | +| `FILE_TREE_TAG_NAME` | Provides the `file-tree-container` element name. | +| `FILE_TREE_STYLE_ATTRIBUTE` | Provides the core style marker attribute. | +| `FILE_TREE_UNSAFE_CSS_ATTRIBUTE` | Provides the custom style marker attribute. | +| `FILE_TREE_SCROLLBAR_MEASURE_ATTRIBUTE` | Provides the scrollbar measurement attribute. | +| `FILE_TREE_SCROLLBAR_GUTTER_STYLE_ATTRIBUTE` | Provides the measured scrollbar style attribute. | +| `FILE_TREE_SCROLLBAR_GUTTER_MEASURED_PROPERTY` | Provides the measured scrollbar CSS property. | +| `FILE_TREE_DEFAULT_ITEM_HEIGHT` | Provides the default row height. | +| `FILE_TREE_DENSITY_PRESETS` | Maps each density keyword to its preset. | +| `FLATTENED_PREFIX` | Provides the identifier prefix for a flattened directory chain. | +| `HEADER_SLOT_NAME` | Provides the header slot name. | +| `CONTEXT_MENU_SLOT_NAME` | Provides the context-menu slot name. | +| `CONTEXT_MENU_TRIGGER_TYPE` | Provides the context-menu trigger type. | diff --git a/skills/trees/references/api-react.md b/skills/trees/references/api-react.md new file mode 100644 index 000000000..39ba73da3 --- /dev/null +++ b/skills/trees/references/api-react.md @@ -0,0 +1,27 @@ +# React API + +This reference lists every export from `@pierre/trees/react`. + +| Export | Kind | Purpose | +| -------------------------- | --------- | ---------------------------------------------------------------------- | +| `FileTree` | Component | Mounts a `FileTree` model in a React host element. | +| `FileTreeProps` | Type | Defines the model, header, context menu, preload data, and host props. | +| `FileTreePreloadedData` | Type | Selects the `id` and `shadowHtml` fields for hydration. | +| `useFileTree` | Hook | Creates one stable `FileTree` model. | +| `UseFileTreeResult` | Type | Holds the model returned by `useFileTree`. | +| `useFileTreeSelection` | Hook | Returns the selected path list and updates with the model. | +| `useFileTreeSearch` | Hook | Returns search state and search actions. | +| `FileTreeSearchState` | Type | Defines the search snapshot and actions. | +| `useFileTreeSelector` | Hook | Subscribes to a selected part of model state. | +| `FileTreeSelector` | Type | Selects a value from a model. | +| `FileTreeSelectorEquality` | Type | Compares two selected values. | + +`FileTreeProps` extends React host attributes except `children`. Its specific +fields are: + +| Field | Purpose | +| ------------------- | --------------------------------------------------- | +| `model` | Supplies the required `FileTree` model. | +| `header` | Supplies React content for the header slot. | +| `renderContextMenu` | Produces React content for the active context menu. | +| `preloadedData` | Supplies server markup for hydration. | diff --git a/skills/trees/references/api-ssr.md b/skills/trees/references/api-ssr.md new file mode 100644 index 000000000..3ef00b4c3 --- /dev/null +++ b/skills/trees/references/api-ssr.md @@ -0,0 +1,12 @@ +# SSR API + +This reference lists every export from `@pierre/trees/ssr`. + +| Export | Kind | Purpose | +| ----------------------------- | -------- | --------------------------------------------------------------- | +| `preloadFileTree` | Function | Renders a tree to a `FileTreeSsrPayload`. | +| `serializeFileTreeSsrPayload` | Function | Creates declarative or DOM-inserted host markup from a payload. | +| `FileTreeSsrPayload` | Type | Holds the host start, shadow HTML, host end, and stable ID. | + +`preloadFileTree` and `serializeFileTreeSsrPayload` are also available from +`@pierre/trees`. diff --git a/skills/trees/references/api-web-components.md b/skills/trees/references/api-web-components.md new file mode 100644 index 000000000..b03a9635b --- /dev/null +++ b/skills/trees/references/api-web-components.md @@ -0,0 +1,11 @@ +# Web components API + +Import `@pierre/trees/web-components` to register the `file-tree-container` +custom element. The entry exports these APIs: + +| Export | Kind | Purpose | +| --------------------------- | -------- | ------------------------------------------------------------------------- | +| `FileTreeContainerLoaded` | Value | Confirms that the registration module ran. | +| `adoptDeclarativeShadowDom` | Function | Copies a declarative template into an empty shadow root. | +| `ensureFileTreeStyles` | Function | Installs the core tree stylesheet in a shadow root. | +| `prepareFileTreeShadowRoot` | Function | Adopts server markup, installs styles, and measures the scrollbar gutter. | diff --git a/skills/trees/references/recipe-interactions.md b/skills/trees/references/recipe-interactions.md new file mode 100644 index 000000000..b96d98af4 --- /dev/null +++ b/skills/trees/references/recipe-interactions.md @@ -0,0 +1,30 @@ +# Recipe: add file tree interactions + +Enable only the interactions that the product exposes: + +```ts +const tree = new FileTree({ + paths, + search: true, + renaming: { + onRename(event) { + renamePath(event.sourcePath, event.destinationPath); + }, + }, + dragAndDrop: { + canDrop({ target }) { + return target.kind === 'directory'; + }, + onDropComplete(event) { + saveMove(event); + }, + }, + gitStatus, +}); +``` + +Use `openSearch()` to open search from an application command. Use +`startRenaming(path)` to start rename from a menu. Use `setGitStatus()` or +`applyGitStatusPatch()` after repository state changes. + +Directory input paths end with `/`. File input paths do not end with `/`. diff --git a/skills/trees/references/recipe-react.md b/skills/trees/references/recipe-react.md new file mode 100644 index 000000000..393c78e22 --- /dev/null +++ b/skills/trees/references/recipe-react.md @@ -0,0 +1,22 @@ +# Recipe: use a file tree in React + +Create the model once and pass it to the component: + +```tsx +'use client'; + +import { FileTree, useFileTree } from '@pierre/trees/react'; + +export function ProjectFiles({ paths }: { paths: readonly string[] }) { + const { model } = useFileTree({ + paths, + initialExpansion: 'open', + search: true, + }); + + return ; +} +``` + +Call model methods for updates after model creation. For example, call +`model.resetPaths(paths)` after the source path list changes. diff --git a/skills/trees/references/recipe-ssr.md b/skills/trees/references/recipe-ssr.md new file mode 100644 index 000000000..9b225804f --- /dev/null +++ b/skills/trees/references/recipe-ssr.md @@ -0,0 +1,31 @@ +# Recipe: preload a file tree on the server + +Create one payload and pass it to the React tree: + +```tsx +import { preloadFileTree } from '@pierre/trees/ssr'; +import { FileTree, useFileTree } from '@pierre/trees/react'; + +const options = { + id: 'project-files', + paths: ['README.md', 'src/', 'src/index.ts'], + initialExpansion: 'open' as const, + initialVisibleRowCount: 8, +}; + +const preloadedData = preloadFileTree(options); + +export function ProjectFiles() { + const { model } = useFileTree(options); + return ( + + ); +} +``` + +For a direct HTML response, call `serializeFileTreeSsrPayload(payload)`. Pass +`dom` as the second argument when a DOM API inserts the complete markup string. diff --git a/skills/trees/references/recipe-theme.md b/skills/trees/references/recipe-theme.md new file mode 100644 index 000000000..b7d90fbc5 --- /dev/null +++ b/skills/trees/references/recipe-theme.md @@ -0,0 +1,18 @@ +# Recipe: apply a resolved theme + +Convert one resolved Shiki or VS Code theme to host styles: + +```tsx +import { themeToTreeStyles } from '@pierre/trees'; +import { FileTree } from '@pierre/trees/react'; + +const treeStyle = { + height: 320, + ...themeToTreeStyles(resolvedTheme), +}; + +; +``` + +Recalculate the styles when the resolved theme changes. Set tree override CSS +properties on the same host style when the product needs a local color choice. diff --git a/skills/trees/references/recipe-vanilla.md b/skills/trees/references/recipe-vanilla.md new file mode 100644 index 000000000..ea258f439 --- /dev/null +++ b/skills/trees/references/recipe-vanilla.md @@ -0,0 +1,23 @@ +# Recipe: use a file tree in vanilla JavaScript + +Create the model and mount it in an element with a height: + +```ts +import { FileTree } from '@pierre/trees'; + +const mount = document.querySelector('#files'); +if (mount == null) throw new Error('Missing file tree mount'); + +mount.style.height = '320px'; + +const tree = new FileTree({ + paths: ['README.md', 'src/', 'src/index.ts'], + initialExpansion: 'open', + search: true, +}); + +tree.render({ containerWrapper: mount }); +``` + +Use `add`, `remove`, `move`, or `resetPaths` to update paths. Call `cleanUp()` +when the host removes the tree.