@cocoar/vue-file-explorer-core 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -0
- package/dist/asset-store.d.ts +284 -0
- package/dist/asset-store.d.ts.map +1 -0
- package/dist/create-asset-store.d.ts +60 -0
- package/dist/create-asset-store.d.ts.map +1 -0
- package/dist/describe-asset.d.ts +36 -0
- package/dist/describe-asset.d.ts.map +1 -0
- package/dist/file-meta.d.ts +21 -0
- package/dist/file-meta.d.ts.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +751 -0
- package/dist/use-file-explorer.d.ts +144 -0
- package/dist/use-file-explorer.d.ts.map +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# @cocoar/vue-file-explorer-core
|
|
2
|
+
|
|
3
|
+
VSCode-style file/asset explorer for Vue 3 — a single composable + a pluggable `AssetStore<T>` contract that decouples the UX from your backend (HTTP, IndexedDB, in-memory, whatever).
|
|
4
|
+
|
|
5
|
+
> **Status: Preview tier.** API is stable enough to consume, but small tweaks may land before the Preview marker drops. Full VitePress reference + live demos coming soon.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @cocoar/vue-file-explorer-core @cocoar/vue-ui
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`@cocoar/vue-ui` (for `CoarTree` + `CoarTreeNodeLabel`) is the only required peer. `@cocoar/vue-script-editor` is optional — it's only pulled if you want the resolver's Monaco-language-typed `language` field on `FileMeta`.
|
|
14
|
+
|
|
15
|
+
## What you get
|
|
16
|
+
|
|
17
|
+
| Piece | Role |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `AssetStore<T>` | The data-plane contract a backend implements (load, mutate, conflict-resolve). |
|
|
20
|
+
| `createInMemoryAssetStore` | Default impl with reactive latency / failure / sortMode / lazy / conflict knobs — perfect for demos, tests, and rapid prototyping. |
|
|
21
|
+
| `useFileExplorer({store})` | The composable. Owns tree + tab state machine, async-loading bookkeeping, blob-URL leases, `beforeunload` warning, keyboard-driven UX. Returns refs to bind to `<CoarTree>` plus ops to call on user input. |
|
|
22
|
+
| `defaultFileMetaFromName` + `resolveFileMeta` | 3-stage fallback (`asset.editor` → `config.getFileMeta` → extension heuristic) that picks an editor + language for every file. Extension table covers ~40 Monaco-supported languages plus markdown / PDF / image. |
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import {
|
|
28
|
+
createInMemoryAssetStore,
|
|
29
|
+
useFileExplorer,
|
|
30
|
+
type Asset,
|
|
31
|
+
} from '@cocoar/vue-file-explorer-core';
|
|
32
|
+
|
|
33
|
+
const seed: Asset[] = [
|
|
34
|
+
{ id: 'src', name: 'src', kind: 'folder', parentId: null },
|
|
35
|
+
{ id: 'utils', name: 'utils.ts', kind: 'file', parentId: 'src' },
|
|
36
|
+
{ id: 'docs', name: 'docs', kind: 'folder', parentId: null },
|
|
37
|
+
{ id: 'readme', name: 'README.md', kind: 'file', parentId: 'docs' },
|
|
38
|
+
];
|
|
39
|
+
const store = createInMemoryAssetStore({
|
|
40
|
+
initialTree: seed,
|
|
41
|
+
initialContent: {
|
|
42
|
+
utils: `export function clamp(...){}`,
|
|
43
|
+
readme: `# Hello`,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const fe = useFileExplorer({
|
|
48
|
+
store,
|
|
49
|
+
onError: (op, err, ctx) => console.warn(`[file-explorer] ${op}:`, err, ctx),
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Then bind into `<CoarTree>` and your own tab bar + editor dispatch:
|
|
54
|
+
|
|
55
|
+
```vue
|
|
56
|
+
<CoarTree
|
|
57
|
+
:nodes="fe.rootNodes.value"
|
|
58
|
+
:get-id="fe.getId"
|
|
59
|
+
:get-children="fe.getChildren"
|
|
60
|
+
:get-label="fe.getLabel"
|
|
61
|
+
:is-expandable="fe.isExpandable"
|
|
62
|
+
v-model:expanded="fe.expanded.value"
|
|
63
|
+
v-model:selected="fe.selectedId.value"
|
|
64
|
+
draggable
|
|
65
|
+
renamable
|
|
66
|
+
@activate="fe.activateNode"
|
|
67
|
+
@rename="({node, newName}) => fe.rename(node.id, newName)"
|
|
68
|
+
@node-move="fe.moveNode"
|
|
69
|
+
>
|
|
70
|
+
<template #default="{ node }">
|
|
71
|
+
<CoarIcon :name="node.kind === 'folder' ? 'folder' : 'file'" />
|
|
72
|
+
<CoarTreeNodeLabel :label="node.name" />
|
|
73
|
+
</template>
|
|
74
|
+
</CoarTree>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The composable also returns `openTabs`, `activeTab`, `breadcrumbPath`, `loadingNodes`, `savingNodes`, `pathOf`, plus imperative ops `openFile` / `saveTab` / `closeTab` / `pinTab` / `unpinTab` / `reorderTab` / `addFolder` / `addFiles` / `deleteNode` / `setContent` / `refresh`.
|
|
78
|
+
|
|
79
|
+
## Wiring your own backend
|
|
80
|
+
|
|
81
|
+
Implement the `AssetStore<T>` methods — `createAssetStore(config)` returns the typed store:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { createAssetStore, type AssetStore } from '@cocoar/vue-file-explorer-core';
|
|
85
|
+
|
|
86
|
+
interface MyAsset { mimeType: string; size: number }
|
|
87
|
+
|
|
88
|
+
const store: AssetStore<MyAsset> = createAssetStore({
|
|
89
|
+
async loadTree() {
|
|
90
|
+
return (await fetch('/api/assets').then(r => r.json())) as Asset<MyAsset>[];
|
|
91
|
+
},
|
|
92
|
+
async loadContent(id) { return await fetch(`/api/assets/${id}`).then(r => r.text()); },
|
|
93
|
+
async createFolder(parentId, name) { /* … */ },
|
|
94
|
+
async createFile(parentId, name) { /* … */ },
|
|
95
|
+
async uploadFile(parentId, file) { /* … */ },
|
|
96
|
+
async save(id, content) { /* … */ },
|
|
97
|
+
async rename(id, newName) { /* … */ },
|
|
98
|
+
async delete(id) { /* … */ },
|
|
99
|
+
async move(id, parentId, position?) { /* … */ },
|
|
100
|
+
// optional — opt into lazy loading
|
|
101
|
+
async loadChildren(parentId) { /* … */ },
|
|
102
|
+
onError: (op, err, ctx) => myToast.error(err.message),
|
|
103
|
+
getFileMeta: (asset) => asset.payload?.mimeType === 'application/pdf'
|
|
104
|
+
? { editor: 'pdf' }
|
|
105
|
+
: null,
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The composable's behavior is identical for any store implementation — eager / lazy mode is detected from the presence of `loadChildren`.
|
|
110
|
+
|
|
111
|
+
## v1 scope
|
|
112
|
+
|
|
113
|
+
- ✅ `AssetStore<T>` contract + `createAssetStore` passthrough + `createInMemoryAssetStore` reference impl
|
|
114
|
+
- ✅ Lazy mode (`loadChildren` opt-in)
|
|
115
|
+
- ✅ Sort modes: `'folders-first'` (default), `'alphabetical'`, `'manual'`, custom comparator
|
|
116
|
+
- ✅ Conflict policy: `'rename'` (default), `'overwrite'`, `'prompt'`, `'error'`, custom function
|
|
117
|
+
- ✅ Tab state machine: preview / pinned, auto-pin-on-edit, dirty marker, save flow, drag-to-reorder
|
|
118
|
+
- ✅ Composable-driven inline rename via `CoarTree :renamable` + `CoarTreeNodeLabel`
|
|
119
|
+
- ✅ Error funnel via single `onError(op, err, ctx)` callback
|
|
120
|
+
- ❌ `<CoarFileExplorer>` wrapper component — for v1, consumers compose the shell themselves (see the playground POC for a 1280-LoC worked example). A wrapper is a candidate for v2 once the API surface settles.
|
|
121
|
+
- ❌ Split-pane editors, find-in-files, ETag / optimistic-conflict — deferred to post-v1.
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { CoarScriptEditorLanguage } from '@cocoar/vue-script-editor';
|
|
2
|
+
import { Ref } from 'vue';
|
|
3
|
+
/**
|
|
4
|
+
* Which built-in editor renders an asset.
|
|
5
|
+
* - `script` — `<CoarScriptEditor>` (Monaco); `language` picks the grammar.
|
|
6
|
+
* - `markdown` — `<CoarMarkdownEditor>` (Milkdown WYSIWYG).
|
|
7
|
+
* - `pdf` — `<CoarDocumentViewer>` with `pdfSource()`.
|
|
8
|
+
* - `image` — `<CoarDocumentViewer>` with `imageSource()`.
|
|
9
|
+
*/
|
|
10
|
+
export type FileEditor = 'script' | 'markdown' | 'pdf' | 'image';
|
|
11
|
+
/** Editor dispatch metadata for one file. */
|
|
12
|
+
export interface FileMeta {
|
|
13
|
+
editor: FileEditor;
|
|
14
|
+
/** Only meaningful when `editor === 'script'`. */
|
|
15
|
+
language?: CoarScriptEditorLanguage;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The canonical shape the file-explorer works with.
|
|
19
|
+
*
|
|
20
|
+
* Hierarchy + name + kind live at the top level — the framework needs them.
|
|
21
|
+
* `payload` is the consumer's bag of domain data, fully generic.
|
|
22
|
+
*
|
|
23
|
+
* `editor` / `language` are optional escape hatches: when present they win
|
|
24
|
+
* over `config.getFileMeta` and the extension-based default.
|
|
25
|
+
*/
|
|
26
|
+
export interface Asset<T = unknown> {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
kind: 'folder' | 'file';
|
|
30
|
+
parentId: string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Lazy-load hint. When present and the store implements `loadChildren`,
|
|
33
|
+
* the folder renders as expandable even before its children are fetched.
|
|
34
|
+
* Ignored in eager mode.
|
|
35
|
+
*/
|
|
36
|
+
hasChildren?: boolean;
|
|
37
|
+
editor?: FileEditor;
|
|
38
|
+
language?: CoarScriptEditorLanguage;
|
|
39
|
+
/** Consumer's domain payload — anything not in the framework's contract. */
|
|
40
|
+
payload?: T;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What to do with the conflict the policy resolved.
|
|
44
|
+
*
|
|
45
|
+
* - `'overwrite'` — replace the existing entry; the store recursively
|
|
46
|
+
* deletes it and then performs the create/upload.
|
|
47
|
+
* - `'rename'` + newName — create with the supplied alternative name.
|
|
48
|
+
* - `'cancel'` — abort the op; the store throws a conflict error.
|
|
49
|
+
*/
|
|
50
|
+
export type ConflictResolution = {
|
|
51
|
+
action: 'overwrite';
|
|
52
|
+
} | {
|
|
53
|
+
action: 'rename';
|
|
54
|
+
newName: string;
|
|
55
|
+
} | {
|
|
56
|
+
action: 'cancel';
|
|
57
|
+
};
|
|
58
|
+
/** Context handed to a function-shaped policy so it can build a smart response. */
|
|
59
|
+
export interface ConflictInfo<T = unknown> {
|
|
60
|
+
/** The colliding existing asset. */
|
|
61
|
+
existing: Asset<T>;
|
|
62
|
+
/** What the caller asked to add (name + kind only — no id yet). */
|
|
63
|
+
incoming: {
|
|
64
|
+
name: string;
|
|
65
|
+
kind: 'folder' | 'file';
|
|
66
|
+
};
|
|
67
|
+
/** Where the new entry would have lived. */
|
|
68
|
+
parentId: string | null;
|
|
69
|
+
/** Auto-rename candidate ("foo.txt" → "foo (2).txt"). */
|
|
70
|
+
suggestedRename: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* How the store should handle a sibling-name collision on create / upload.
|
|
74
|
+
* Default: `'rename'` (silent auto-suffix — matches macOS Finder / VSCode).
|
|
75
|
+
*
|
|
76
|
+
* - `'rename'` — auto-rename: "foo.txt" → "foo (2).txt", etc.
|
|
77
|
+
* - `'overwrite'` — delete the existing entry (recursively) and proceed.
|
|
78
|
+
* - `'prompt'` — `window.prompt` for an alternative name, default to
|
|
79
|
+
* the auto-rename suggestion. Cancel → throws conflict.
|
|
80
|
+
* - `'error'` — never resolve; always throw the conflict error.
|
|
81
|
+
* - `(info) => …` — custom resolver; sync or async.
|
|
82
|
+
*
|
|
83
|
+
* `move` and `rename` deliberately bypass the policy — they're explicit
|
|
84
|
+
* user intent, not file additions, so silently changing the requested
|
|
85
|
+
* name would be surprising.
|
|
86
|
+
*/
|
|
87
|
+
export type ConflictPolicy<T = unknown> = 'rename' | 'overwrite' | 'prompt' | 'error' | ((info: ConflictInfo<T>) => ConflictResolution | Promise<ConflictResolution>);
|
|
88
|
+
/** Operation tag passed to `onError` so the consumer can format its message. */
|
|
89
|
+
export type AssetOp = 'loadTree' | 'loadChildren' | 'loadContent' | 'createFolder' | 'createFile' | 'uploadFile' | 'save' | 'rename' | 'delete' | 'move';
|
|
90
|
+
/** Context object passed alongside `op` + `error` to `onError`. */
|
|
91
|
+
export interface AssetOpContext {
|
|
92
|
+
/** Affected asset id, when known. */
|
|
93
|
+
id?: string;
|
|
94
|
+
/** Target parent id, for create/move ops. */
|
|
95
|
+
parentId?: string | null;
|
|
96
|
+
/** Asset name, for create/rename ops. */
|
|
97
|
+
name?: string;
|
|
98
|
+
/** Original file, for uploadFile. */
|
|
99
|
+
file?: File;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The contract a backend implements. The composable calls these and adapts
|
|
103
|
+
* the result into reactive state. Implementations should throw on failure;
|
|
104
|
+
* the composable funnels throws through `onError`.
|
|
105
|
+
*
|
|
106
|
+
* Returned `Asset<T>` objects on create/upload MUST have a stable `id`
|
|
107
|
+
* already assigned by the backend — optimistic UI uses that id to reconcile.
|
|
108
|
+
*/
|
|
109
|
+
export interface AssetStore<T = unknown> {
|
|
110
|
+
/**
|
|
111
|
+
* Eager: full hierarchy. Lazy: just root-level entries; mark folders with
|
|
112
|
+
* `hasChildren: true` so the tree knows they're expandable.
|
|
113
|
+
*/
|
|
114
|
+
loadTree(): Promise<Asset<T>[]>;
|
|
115
|
+
/**
|
|
116
|
+
* Present → store opts into lazy loading. Called on first expand of each
|
|
117
|
+
* folder. Absent → composable runs fully eager off `loadTree()`.
|
|
118
|
+
*/
|
|
119
|
+
loadChildren?(parentId: string): Promise<Asset<T>[]>;
|
|
120
|
+
/**
|
|
121
|
+
* Fetch the content of a file. Return value depends on the editor:
|
|
122
|
+
* - `script` / `markdown` → `string`
|
|
123
|
+
* - `pdf` / `image` → `string` (URL) OR `Blob`
|
|
124
|
+
*
|
|
125
|
+
* Composable caches the result; subsequent opens hit cache until invalidated.
|
|
126
|
+
*/
|
|
127
|
+
loadContent(id: string): Promise<string | Blob>;
|
|
128
|
+
createFolder(parentId: string | null, name: string): Promise<Asset<T>>;
|
|
129
|
+
/**
|
|
130
|
+
* Create a new empty file. The editor + language are derived from `name`
|
|
131
|
+
* via the standard fallback chain (or carried on the returned `Asset` if
|
|
132
|
+
* the backend wants explicit control).
|
|
133
|
+
*/
|
|
134
|
+
createFile(parentId: string | null, name: string): Promise<Asset<T>>;
|
|
135
|
+
/**
|
|
136
|
+
* Upload an OS File (from drop or `<input type=file>`). Distinct from
|
|
137
|
+
* `createFile` so backends can pick multipart / signed-URL transports.
|
|
138
|
+
*/
|
|
139
|
+
uploadFile(parentId: string | null, file: File): Promise<Asset<T>>;
|
|
140
|
+
/** Persist new file content. Composable calls this on Ctrl+S. */
|
|
141
|
+
save(id: string, content: string | Blob): Promise<void>;
|
|
142
|
+
rename(id: string, newName: string): Promise<void>;
|
|
143
|
+
delete(id: string): Promise<void>;
|
|
144
|
+
/**
|
|
145
|
+
* Move `id` under `newParentId`. `position` is the destination index
|
|
146
|
+
* inside the new parent's children. When `undefined`, append at end.
|
|
147
|
+
* `newParentId === null` means "to the root".
|
|
148
|
+
*/
|
|
149
|
+
move(id: string, newParentId: string | null, position?: number): Promise<void>;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* `createAssetStore(config)` produces an `AssetStore<T>`. The config is
|
|
153
|
+
* just a struct of the same method signatures plus framework-level concerns
|
|
154
|
+
* (`onError`, `getFileMeta`).
|
|
155
|
+
*
|
|
156
|
+
* Splitting `AssetStoreConfig` from `AssetStore` lets us add cross-cutting
|
|
157
|
+
* helpers (debouncing, retry, optimistic flags) inside `createAssetStore`
|
|
158
|
+
* without leaking them into the runtime interface every consumer of the
|
|
159
|
+
* store sees.
|
|
160
|
+
*/
|
|
161
|
+
export interface AssetStoreConfig<T = unknown> {
|
|
162
|
+
loadTree: AssetStore<T>['loadTree'];
|
|
163
|
+
loadChildren?: AssetStore<T>['loadChildren'];
|
|
164
|
+
loadContent: AssetStore<T>['loadContent'];
|
|
165
|
+
createFolder: AssetStore<T>['createFolder'];
|
|
166
|
+
createFile: AssetStore<T>['createFile'];
|
|
167
|
+
uploadFile: AssetStore<T>['uploadFile'];
|
|
168
|
+
save: AssetStore<T>['save'];
|
|
169
|
+
rename: AssetStore<T>['rename'];
|
|
170
|
+
delete: AssetStore<T>['delete'];
|
|
171
|
+
move: AssetStore<T>['move'];
|
|
172
|
+
/**
|
|
173
|
+
* Called whenever any store operation throws. The composable will already
|
|
174
|
+
* have rolled back the optimistic mutation by the time this fires.
|
|
175
|
+
* No return value — consumer decides what to show.
|
|
176
|
+
*/
|
|
177
|
+
onError?: (op: AssetOp, error: unknown, ctx: AssetOpContext) => void;
|
|
178
|
+
/**
|
|
179
|
+
* Per-asset editor/language override. Runs AFTER `asset.editor` is
|
|
180
|
+
* checked but BEFORE the extension-based default. Use this when the
|
|
181
|
+
* editor choice depends on something other than the filename — MIME
|
|
182
|
+
* type, a backend `type` field, user preferences.
|
|
183
|
+
*
|
|
184
|
+
* Return `null` to fall through to the default heuristic.
|
|
185
|
+
*/
|
|
186
|
+
getFileMeta?: (asset: Asset<T>) => FileMeta | null;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Comparator for two siblings, used in non-manual `sortMode`. Returns the
|
|
190
|
+
* standard `< 0 / 0 / > 0` semantics. Folders and files arrive mixed — the
|
|
191
|
+
* comparator decides any grouping (e.g. folders-first).
|
|
192
|
+
*/
|
|
193
|
+
export type AssetComparator<T = unknown> = (a: Asset<T>, b: Asset<T>) => number;
|
|
194
|
+
/**
|
|
195
|
+
* How siblings inside a folder are ordered. Default `'folders-first'`
|
|
196
|
+
* matches VSCode's explorer; pick `'manual'` when you have a backend that
|
|
197
|
+
* can persist per-entry order (asset DB, CMS, etc.).
|
|
198
|
+
*
|
|
199
|
+
* In any non-manual mode the composable disables drop-between-siblings at
|
|
200
|
+
* the tree level — only drop-INTO-folder works.
|
|
201
|
+
*/
|
|
202
|
+
export type SortMode<T = unknown> = 'manual' | 'folders-first' | 'alphabetical' | AssetComparator<T>;
|
|
203
|
+
/**
|
|
204
|
+
* Top-level config for `useFileExplorer(...)`. The store is the only
|
|
205
|
+
* required input — everything else is presentation polish.
|
|
206
|
+
*/
|
|
207
|
+
export interface FileExplorerConfig<T = unknown> {
|
|
208
|
+
store: AssetStore<T>;
|
|
209
|
+
/**
|
|
210
|
+
* Sibling ordering strategy. Default `'folders-first'` (VSCode-style).
|
|
211
|
+
* See the `SortMode` docs for the trade-offs.
|
|
212
|
+
*/
|
|
213
|
+
sortMode?: SortMode<T>;
|
|
214
|
+
/** Initial selection, if any. Pass the id of a file to preselect-and-open. */
|
|
215
|
+
initialSelectionId?: string;
|
|
216
|
+
/** Folder ids that should start expanded. Default: root-level folders. */
|
|
217
|
+
initialExpandedIds?: readonly string[];
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Imperative + reactive surface returned by `useFileExplorer`. Mirrors the
|
|
221
|
+
* shape of `useTree`'s api (loading refs first, then methods).
|
|
222
|
+
*
|
|
223
|
+
* Tabs / breadcrumb / context-menu plumbing builds on top of this — they're
|
|
224
|
+
* not part of the data plane and don't belong here.
|
|
225
|
+
*/
|
|
226
|
+
export interface FileExplorerApi<T = unknown> {
|
|
227
|
+
/** True while `store.loadTree()` is in flight (initial load only). */
|
|
228
|
+
readonly loading: Readonly<Ref<boolean>>;
|
|
229
|
+
/** Folder + file ids currently fetching children or content. */
|
|
230
|
+
readonly loadingNodes: Readonly<Ref<ReadonlySet<string>>>;
|
|
231
|
+
/** Ids with an in-flight save/rename/delete/move. */
|
|
232
|
+
readonly savingNodes: Readonly<Ref<ReadonlySet<string>>>;
|
|
233
|
+
/** Reactive tree, ordered. Pass directly to `<CoarTree :builder>`. */
|
|
234
|
+
readonly tree: Readonly<Ref<readonly Asset<T>[]>>;
|
|
235
|
+
/**
|
|
236
|
+
* Whether sibling reordering is honored by the configured `sortMode`.
|
|
237
|
+
* Read by the tree wiring to flip CoarTree between `'reorder'` and
|
|
238
|
+
* `'move'` draggable modes. Reactive so consumers can toggle sort modes
|
|
239
|
+
* at runtime (e.g. a "manual reorder" toggle in the toolbar).
|
|
240
|
+
*/
|
|
241
|
+
readonly reorderable: Readonly<Ref<boolean>>;
|
|
242
|
+
createFolder(parentId: string | null, name: string): Promise<Asset<T>>;
|
|
243
|
+
createFile(parentId: string | null, name: string): Promise<Asset<T>>;
|
|
244
|
+
uploadFiles(parentId: string | null, files: readonly File[]): Promise<Asset<T>[]>;
|
|
245
|
+
save(id: string, content: string | Blob): Promise<void>;
|
|
246
|
+
rename(id: string, newName: string): Promise<void>;
|
|
247
|
+
delete(id: string): Promise<void>;
|
|
248
|
+
/**
|
|
249
|
+
* `position` is honored only when `sortMode === 'manual'`; otherwise it
|
|
250
|
+
* is silently dropped (the comparator decides the final position after
|
|
251
|
+
* the parent change).
|
|
252
|
+
*/
|
|
253
|
+
move(id: string, newParentId: string | null, position?: number): Promise<void>;
|
|
254
|
+
/**
|
|
255
|
+
* Resolve a file's editor/language using the 3-stage fallback chain.
|
|
256
|
+
* Returns `null` for unsupported / likely-binary files (caller logs +
|
|
257
|
+
* skips, same as the POC does today).
|
|
258
|
+
*/
|
|
259
|
+
fileMeta(asset: Asset<T>): FileMeta | null;
|
|
260
|
+
/** Force a re-fetch of the tree (or one folder). */
|
|
261
|
+
refresh(folderId?: string | null): Promise<void>;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Wraps a config into a fully-typed `AssetStore<T>`. Today this is a thin
|
|
265
|
+
* passthrough; the wrapper exists so future cross-cutting concerns
|
|
266
|
+
* (request dedup, retry, telemetry) have a home.
|
|
267
|
+
*/
|
|
268
|
+
export declare function createAssetStore<T = unknown>(config: AssetStoreConfig<T>): AssetStore<T>;
|
|
269
|
+
/**
|
|
270
|
+
* Browser-only default implementation. Backed by a `ref<Asset<T>[]>` plus
|
|
271
|
+
* an `id → content` map. Used by the playground POC and by any consumer
|
|
272
|
+
* that wants offline / demo behaviour.
|
|
273
|
+
*/
|
|
274
|
+
export declare function createInMemoryAssetStore<T = unknown>(options?: {
|
|
275
|
+
/** Seed data. Optimistic ops mutate this ref in place. */
|
|
276
|
+
initialTree?: readonly Asset<T>[];
|
|
277
|
+
/** Seed file contents by id. */
|
|
278
|
+
initialContent?: Readonly<Record<string, string | Blob>>;
|
|
279
|
+
/** Artificial latency per op, for visualising loading states. Default 0. */
|
|
280
|
+
latencyMs?: number;
|
|
281
|
+
/** Random-failure rate (0..1), for exercising the error path. Default 0. */
|
|
282
|
+
failureRate?: number;
|
|
283
|
+
}): AssetStore<T>;
|
|
284
|
+
//# sourceMappingURL=asset-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-store.d.ts","sourceRoot":"","sources":["../src/asset-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';
|
|
2
|
+
import { Asset, AssetStore, AssetStoreConfig, ConflictPolicy } from './asset-store';
|
|
3
|
+
export declare function createAssetStore<T = unknown>(config: AssetStoreConfig<T>): AssetStore<T>;
|
|
4
|
+
export interface InMemoryAssetStoreOptions<T = unknown> {
|
|
5
|
+
/** Seed data. Mutations replace this ref's contents in place. */
|
|
6
|
+
initialTree?: readonly Asset<T>[];
|
|
7
|
+
/** Seed file contents by id. */
|
|
8
|
+
initialContent?: Readonly<Record<string, string | Blob>>;
|
|
9
|
+
/**
|
|
10
|
+
* Artificial latency per op, for visualising loading states. Default 0.
|
|
11
|
+
* Reactive: pass a `Ref<number>` / getter to retune at runtime (the
|
|
12
|
+
* playground simulator dropdown does exactly that).
|
|
13
|
+
*/
|
|
14
|
+
latencyMs?: MaybeRefOrGetter<number>;
|
|
15
|
+
/**
|
|
16
|
+
* Random-failure rate (0..1), for exercising the error path. Default 0.
|
|
17
|
+
* Reactive (see `latencyMs`).
|
|
18
|
+
*/
|
|
19
|
+
failureRate?: MaybeRefOrGetter<number>;
|
|
20
|
+
/**
|
|
21
|
+
* ID factory. Default: `crypto.randomUUID()`. Override for tests that
|
|
22
|
+
* need stable ids.
|
|
23
|
+
*/
|
|
24
|
+
idFactory?: () => string;
|
|
25
|
+
/**
|
|
26
|
+
* What to do when a create / upload collides with an existing sibling
|
|
27
|
+
* name. Default `'rename'` (auto-suffix, macOS Finder pattern). Reactive
|
|
28
|
+
* via `Ref` / getter so the simulator can flip policies live.
|
|
29
|
+
*/
|
|
30
|
+
onConflict?: MaybeRefOrGetter<ConflictPolicy<T>>;
|
|
31
|
+
/**
|
|
32
|
+
* Opt into lazy loading. When `true`:
|
|
33
|
+
* - `loadTree()` only returns root-level entries, enriched with
|
|
34
|
+
* `hasChildren: boolean` so the tree knows which folders are
|
|
35
|
+
* expandable without their kids loaded.
|
|
36
|
+
* - `loadChildren(parentId)` is exposed and returns one level deep.
|
|
37
|
+
* - `_assets` is a computed projection over a `_publishedIds` Set —
|
|
38
|
+
* only the published subset is visible to the composable. The full
|
|
39
|
+
* dataset still lives in `_complete` for the store's own bookkeeping.
|
|
40
|
+
* Default: `false` (eager — composable sees everything from the start).
|
|
41
|
+
*/
|
|
42
|
+
lazy?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* In-memory store + a peek at its raw state. Returned together so the POC
|
|
46
|
+
* (and tests) can inspect / mutate the underlying ref without going through
|
|
47
|
+
* the async API — useful for seeding, debugging, snapshotting.
|
|
48
|
+
*
|
|
49
|
+
* `_assets` is a reactive projection. In eager mode it equals the full data
|
|
50
|
+
* set; in lazy mode it equals only the published subset (root + whatever
|
|
51
|
+
* has been fetched on expand).
|
|
52
|
+
*/
|
|
53
|
+
export interface InMemoryAssetStore<T = unknown> extends AssetStore<T> {
|
|
54
|
+
/** Published subset (computed in lazy mode, equals everything in eager). */
|
|
55
|
+
readonly _assets: Ref<Asset<T>[]> | ComputedRef<Asset<T>[]>;
|
|
56
|
+
/** The raw content map. Mutate at your own risk. */
|
|
57
|
+
readonly _contents: Map<string, string | Blob>;
|
|
58
|
+
}
|
|
59
|
+
export declare function createInMemoryAssetStore<T = unknown>(options?: InMemoryAssetStoreOptions<T>): InMemoryAssetStore<T>;
|
|
60
|
+
//# sourceMappingURL=create-asset-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-asset-store.d.ts","sourceRoot":"","sources":["../src/create-asset-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAA0B,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAEhG,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAsB,MAAM,eAAe,CAAC;AAI7G,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAgBxF;AAID,MAAM,WAAW,yBAAyB,CAAC,CAAC,GAAG,OAAO;IACpD,iEAAiE;IACjE,WAAW,CAAC,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAClC,gCAAgC;IAChC,cAAc,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IACzD;;;;OAIG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACrC;;;OAGG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACvC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;IACzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,OAAO,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;IACpE,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5D,oDAAoD;IACpD,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;CAChD;AAED,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,OAAO,EAClD,OAAO,GAAE,yBAAyB,CAAC,CAAC,CAAM,GACzC,kBAAkB,CAAC,CAAC,CAAC,CAqVvB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Asset, FileMeta } from './asset-store';
|
|
2
|
+
/** One row in an asset info / details panel. */
|
|
3
|
+
export interface AssetProperty {
|
|
4
|
+
/**
|
|
5
|
+
* Stable identifier for the row (`'name'`, `'type'`, `'language'`,
|
|
6
|
+
* `'extension'`, `'path'`). Lets consumers key a `v-for`, filter out rows
|
|
7
|
+
* they don't want, or splice their own rows in at a known position.
|
|
8
|
+
*/
|
|
9
|
+
key: string;
|
|
10
|
+
/** Human-readable label, e.g. `'Type'`. */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Human-readable value, e.g. `'Markdown'`. */
|
|
13
|
+
value: string;
|
|
14
|
+
}
|
|
15
|
+
/** Inputs `buildAssetProperties` needs beyond the asset itself. */
|
|
16
|
+
export interface DescribeAssetContext {
|
|
17
|
+
/**
|
|
18
|
+
* Resolved editor/language for the asset, or `null` for folders and
|
|
19
|
+
* unrecognised files. `useFileExplorer().describeAsset` passes
|
|
20
|
+
* `fileMeta(asset)` for files and `null` for folders.
|
|
21
|
+
*/
|
|
22
|
+
meta: FileMeta | null;
|
|
23
|
+
/**
|
|
24
|
+
* Name path from the tree root to the asset, inclusive — i.e. the result of
|
|
25
|
+
* `pathOf(asset.id)`. A single-element path (a root-level asset) omits the
|
|
26
|
+
* `'path'` row, since it would just repeat the name.
|
|
27
|
+
*/
|
|
28
|
+
path: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build the default, framework-known property rows for an asset. Folders get
|
|
32
|
+
* Name + Type (+ Path); files additionally get Language (script editor only),
|
|
33
|
+
* Extension, and Path. Returns a fresh array each call.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildAssetProperties<T>(asset: Asset<T>, ctx: DescribeAssetContext): AssetProperty[];
|
|
36
|
+
//# sourceMappingURL=describe-asset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe-asset.d.ts","sourceRoot":"","sources":["../src/describe-asset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,mEAAmE;AACnE,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB;;;;OAIG;IACH,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB;AAkDD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,GAAG,EAAE,oBAAoB,GACxB,aAAa,EAAE,CAyBjB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Asset, AssetStoreConfig, FileMeta } from './asset-store';
|
|
2
|
+
/**
|
|
3
|
+
* Map a filename to its `{ editor, language }`. Returns `null` for
|
|
4
|
+
* extensions that are recognised as binary-only (.exe, .zip, .mp4, …).
|
|
5
|
+
*
|
|
6
|
+
* The caller is expected to skip unrecognised files with a warning —
|
|
7
|
+
* matching the POC's behaviour.
|
|
8
|
+
*/
|
|
9
|
+
export declare function defaultFileMetaFromName(name: string): FileMeta | null;
|
|
10
|
+
/**
|
|
11
|
+
* 3-stage fallback: `asset.editor` wins → then `config.getFileMeta(asset)`
|
|
12
|
+
* → then `defaultFileMetaFromName(asset.name)`. Returns `null` if all three
|
|
13
|
+
* decline (consumer override returned null + name is unrecognised).
|
|
14
|
+
*
|
|
15
|
+
* `language` is normalized to `'plaintext'` whenever the resolved editor is
|
|
16
|
+
* `'script'` but no language was supplied. Saves every consumer the
|
|
17
|
+
* `?? 'plaintext'` dance when binding to `<CoarScriptEditor :language>`;
|
|
18
|
+
* matches Monaco's own fallback for unrecognized files.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveFileMeta<T>(asset: Asset<T>, config?: Pick<AssetStoreConfig<T>, 'getFileMeta'>): FileMeta | null;
|
|
21
|
+
//# sourceMappingURL=file-meta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-meta.d.ts","sourceRoot":"","sources":["../src/file-meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEvE;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAyErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,MAAM,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,GAChD,QAAQ,GAAG,IAAI,CAOjB"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cocoar/vue-file-explorer-core` — VSCode-style file/asset explorer composable
|
|
3
|
+
* over a pluggable `AssetStore<T>` backend.
|
|
4
|
+
*
|
|
5
|
+
* Three layers:
|
|
6
|
+
*
|
|
7
|
+
* - **`AssetStore<T>` contract** + types — what a backend implements.
|
|
8
|
+
* The store is the data plane: load, mutate, conflict-resolve. The
|
|
9
|
+
* composable knows nothing about HTTP / IndexedDB / etc.
|
|
10
|
+
* - **`createInMemoryAssetStore`** — reference implementation for
|
|
11
|
+
* browser-only / demo use. Configurable latency, failure simulation,
|
|
12
|
+
* lazy mode, conflict policy.
|
|
13
|
+
* - **`useFileExplorer({store, ...})`** — the composable that wires the
|
|
14
|
+
* store into reactive tree + tab state + async-loading bookkeeping +
|
|
15
|
+
* keyboard-driven UX. Returns the full surface a file-explorer view
|
|
16
|
+
* needs: refs to bind to `<CoarTree>`, ops to call on user input,
|
|
17
|
+
* `revealInTree` / `breadcrumbPath` / `pathOf` for navigation.
|
|
18
|
+
*
|
|
19
|
+
* See the playground POC (apps/playground/src/views/FileExplorerPocView.vue)
|
|
20
|
+
* for a worked end-to-end example.
|
|
21
|
+
*/
|
|
22
|
+
export type { Asset, AssetComparator, AssetOp, AssetOpContext, AssetStore, AssetStoreConfig, ConflictInfo, ConflictPolicy, ConflictResolution, FileEditor, FileExplorerApi, FileExplorerConfig, FileMeta, SortMode, } from './asset-store';
|
|
23
|
+
export { createAssetStore, createInMemoryAssetStore, type InMemoryAssetStore, type InMemoryAssetStoreOptions, } from './create-asset-store';
|
|
24
|
+
export { defaultFileMetaFromName, resolveFileMeta, } from './file-meta';
|
|
25
|
+
export { buildAssetProperties, type AssetProperty, type DescribeAssetContext, } from './describe-asset';
|
|
26
|
+
export { useFileExplorer, type OpenTab, type UseFileExplorerOptions, type UseFileExplorerReturn, } from './use-file-explorer';
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,YAAY,EACV,KAAK,EACL,eAAe,EACf,OAAO,EACP,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,QAAQ,GACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,GAC/B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,uBAAuB,EACvB,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,oBAAoB,GAC1B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,eAAe,EACf,KAAK,OAAO,EACZ,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC"}
|