diff --git a/.gitignore b/.gitignore index f8da83ce7..b090aafd7 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,4 @@ tsconfig.tsbuildinfo .vercel .claude/* .codemogger/* -.omx/* +.omx/ diff --git a/apps/docs/app/trees-dev/_components/ExampleCard.tsx b/apps/docs/app/trees-dev/_components/ExampleCard.tsx index 13aab5e91..b07030410 100644 --- a/apps/docs/app/trees-dev/_components/ExampleCard.tsx +++ b/apps/docs/app/trees-dev/_components/ExampleCard.tsx @@ -16,7 +16,7 @@ export function ExampleCard({ footer?: ReactNode; }) { return ( -
{description}
diff --git a/apps/docs/app/trees-dev/path-store-powered/PathStorePoweredRenderDemoClient.tsx b/apps/docs/app/trees-dev/path-store-powered/PathStorePoweredRenderDemoClient.tsx
index 8f5b1df0b..08810c499 100644
--- a/apps/docs/app/trees-dev/path-store-powered/PathStorePoweredRenderDemoClient.tsx
+++ b/apps/docs/app/trees-dev/path-store-powered/PathStorePoweredRenderDemoClient.tsx
@@ -3,19 +3,26 @@
import {
PathStoreFileTree,
type PathStoreFileTreeOptions,
+ type PathStoreTreesContextMenuItem,
+ type PathStoreTreesContextMenuOpenContext,
+ type PathStoreTreesMutationEvent,
} from '@pierre/trees/path-store';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
-import type { Root as ReactDomRoot } from 'react-dom/client';
+import { createRoot, type Root as ReactDomRoot } from 'react-dom/client';
import { ExampleCard } from '../_components/ExampleCard';
import { StateLog, useStateLog } from '../_components/StateLog';
-import {
- clearVanillaContextMenuSlot,
- renderVanillaContextMenuSlot,
-} from '../_components/TreeDemoContextMenu';
import { pathStoreCapabilityMatrix } from './capabilityMatrix';
import { createPresortedPreparedInput } from './createPresortedPreparedInput';
import { PATH_STORE_CUSTOM_ICONS } from './pathStoreDemoIcons';
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from '@/components/ui/dropdown-menu';
interface SharedDemoOptions extends Omit<
PathStoreFileTreeOptions,
@@ -27,6 +34,319 @@ interface PathStorePoweredRenderDemoClientProps {
sharedOptions: SharedDemoOptions;
}
+type PathStoreMutationOperation =
+ | { path: string; type: 'add' }
+ | { from: string; to: string; type: 'move' };
+
+interface PathStoreMutationDemoTargets {
+ batchOperations: readonly PathStoreMutationOperation[];
+ moveFromPath: string | null;
+ moveToPath: string | null;
+}
+
+function getParentPath(path: string): string {
+ if (path.endsWith('/')) {
+ const trimmedPath = path.slice(0, -1);
+ const lastSlashIndex = trimmedPath.lastIndexOf('/');
+ return lastSlashIndex < 0
+ ? ''
+ : `${trimmedPath.slice(0, lastSlashIndex + 1)}`;
+ }
+
+ const lastSlashIndex = path.lastIndexOf('/');
+ return lastSlashIndex < 0 ? '' : path.slice(0, lastSlashIndex + 1);
+}
+
+function getPathBasename(path: string): string {
+ const trimmedPath = path.endsWith('/') ? path.slice(0, -1) : path;
+ const lastSlashIndex = trimmedPath.lastIndexOf('/');
+ return lastSlashIndex < 0
+ ? trimmedPath
+ : trimmedPath.slice(lastSlashIndex + 1);
+}
+
+// Creates a stable suffixed path so repeated demo-target derivation can avoid collisions.
+function getSuffixedPath(path: string, suffix: number): string {
+ if (path.endsWith('/')) {
+ return `${path.slice(0, -1)}-${String(suffix)}/`;
+ }
+
+ const lastSlashIndex = path.lastIndexOf('/');
+ const lastDotIndex = path.lastIndexOf('.');
+ if (lastDotIndex > lastSlashIndex) {
+ return `${path.slice(0, lastDotIndex)}-${String(suffix)}${path.slice(lastDotIndex)}`;
+ }
+
+ return `${path}-${String(suffix)}`;
+}
+
+// Picks a unique demo path under the existing tree so mutation buttons can be re-used after reset.
+function getUniquePath(
+ path: string,
+ existingPaths: ReadonlySet
- The path-store lane keeps the landed focus and selection model,
- preserves the header slot, proves the built-in Minimal, Standard, and
- Complete icon sets, and now restores the Phase 5 context-menu shell
- plus simple row decorations.
+ Phase 6 turns the path-store lane into a mutation-first tree: use the
+ shared handle to add, move, batch, and reset paths, use the existing
+ context menu for low-cost delete and narrow rename proof, and watch
+ the live tree plus mutation log stay coherent under virtualization.
- Focus + Selection + Header Slot + Icon Sets
+ Mutation API + Context Menu Proof + Icon Sets