Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
44b712c
Blueprint bundle editor
adamziel Nov 25, 2025
f09a1ed
Support recreating the temporary Playground from the edited Blueprint…
adamziel Nov 26, 2025
0a259c8
Store the edited Blueprint bundle in OPFS
adamziel Nov 26, 2025
8a639aa
Tweak UX
adamziel Nov 26, 2025
de36471
Consistent icons UI
adamziel Nov 26, 2025
ae4b685
Display filename in the preview area even for binary files
adamziel Nov 26, 2025
19073f8
Add play icon to the recreate button
adamziel Nov 26, 2025
3f46226
Reuse BinaryFilePreview component
adamziel Nov 26, 2025
326688d
Move the bundle execution logic into the editor component
adamziel Nov 26, 2025
75a21b0
remove save-state.ts
adamziel Nov 26, 2025
d097add
lint
adamziel Nov 26, 2025
93e81e6
lint
adamziel Nov 26, 2025
cd60e91
lint
adamziel Nov 26, 2025
f36d676
Offer to restore the last autosaved bundle
adamziel Nov 27, 2025
64d9409
Clean up the blueprint bundle editor
adamziel Nov 27, 2025
66361f3
lint
adamziel Nov 27, 2025
6839c13
Export writable filesystem types from the in memory class
adamziel Nov 27, 2025
a9ba0be
Add missing file
adamziel Nov 27, 2025
12f4ab5
Simplify writable filesystem implementations
adamziel Nov 28, 2025
67829bb
Reorganize the blueprint editor code
adamziel Nov 28, 2025
c801d39
Split the bundle editor component in two files
adamziel Nov 28, 2025
0481948
Simplify the code further
adamziel Nov 28, 2025
78c92ca
Inline filesystem creation logic
adamziel Nov 28, 2025
b51304b
Use refs to avoid stale onChange callback
adamziel Nov 28, 2025
475612a
Adjust the E2E test
adamziel Nov 28, 2025
02c04b7
Merge branch 'trunk' into blueprint-bundle-editor-website
adamziel Nov 28, 2025
3b8eaed
Undo dev changes
adamziel Nov 28, 2025
303960b
Lint
adamziel Nov 28, 2025
ae30ad6
Lint
adamziel Nov 28, 2025
5319963
Fix InMemoryFilesystem to extend EventTarget
adamziel Nov 28, 2025
21ca979
Move Download/Run buttons to file path bar in Blueprint editor
adamziel Nov 28, 2025
e4cdb8f
Cleanup blueprint editor initialization logic
adamziel Nov 28, 2025
5148e72
Cleanup blueprint editor initialization logic
adamziel Nov 28, 2025
8ba1bdb
Only create an autosave after the first change
adamziel Nov 28, 2025
b58d755
Remember the answer to "should restore from autosave" prompt
adamziel Nov 28, 2025
c0e5bca
Persist blueprint bundle and set originalBlueprintSource to bundle-di…
adamziel Nov 28, 2025
76b3e56
Fix blueprint bundle persistence: pass originalBlueprintSource direct…
adamziel Nov 29, 2025
a0b291b
Make PersistedBlueprintBundle implement FilesystemBackend for editor …
adamziel Nov 29, 2025
44c89d9
persist bundles to OPFS
adamziel Nov 29, 2025
3be136b
simplify the code
adamziel Nov 29, 2025
9ff919e
harmonize the filesystem implementations
adamziel Nov 29, 2025
3977a1f
remove unnecessary export
adamziel Nov 29, 2025
9fb8a4c
Generic copyFilesystem helper
adamziel Nov 29, 2025
b4b333b
throw errors on failure
adamziel Nov 29, 2025
0f9f3bf
Move filesystem definitions to the storage package
adamziel Nov 29, 2025
4a30acb
Move AsyncWritableFilesystem definitions to the storage package
adamziel Nov 29, 2025
d1548e6
accept a path argument in OpfsFilesystemBackend
adamziel Nov 29, 2025
49cd822
Do not re-export asyncwritablefilesystem
adamziel Nov 29, 2025
0cc7f19
small style improvement
adamziel Nov 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
throw errors on failure
  • Loading branch information
adamziel committed Nov 29, 2025
commit b4b333bd8466e798f3372fac2489d3e3aef3a57e
23 changes: 9 additions & 14 deletions packages/playground/storage/src/lib/filesystems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,29 +394,24 @@ export class OpfsFilesystemBackend implements WritableFilesystemBackend {

/**
* Create a backend for a specific path in OPFS.
* The path will be created if it doesn't exist.
* The path will be created if `create` is true.
* @throws Error if OPFS is not available or path doesn't exist (when create=false)
*/
static async fromPath(
pathSegments: string[],
create = false
): Promise<OpfsFilesystemBackend | null> {
): Promise<OpfsFilesystemBackend> {
if (typeof navigator === 'undefined') {
return null;
throw new Error('OPFS not available: navigator is undefined');
}
if (!navigator.storage || !navigator.storage.getDirectory) {
return null;
throw new Error('OPFS not available: storage API not supported');
}
try {
let handle = await navigator.storage.getDirectory();
for (const segment of pathSegments) {
handle = await handle.getDirectoryHandle(segment, {
create,
});
}
return new OpfsFilesystemBackend(handle);
} catch {
return null;
let handle = await navigator.storage.getDirectory();
for (const segment of pathSegments) {
handle = await handle.getDirectoryHandle(segment, { create });
}
return new OpfsFilesystemBackend(handle);
}

async clear(): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ export class OpfsFilesystemBackend extends BaseOpfsFilesystemBackend {
* Check if there's a saved blueprint bundle in the default location.
*/
static async hasSavedBundle(): Promise<boolean> {
const backend =
await BaseOpfsFilesystemBackend.fromPath(OPFS_BASE_PATH);
if (!backend) {
try {
const backend =
await BaseOpfsFilesystemBackend.fromPath(OPFS_BASE_PATH);
const files = await backend.listFiles('/');
return files.length > 0;
} catch {
return false;
}
const files = await backend.listFiles('/');
return files.length > 0;
}

/**
Expand All @@ -42,9 +43,6 @@ export class OpfsFilesystemBackend extends BaseOpfsFilesystemBackend {
OPFS_BASE_PATH,
true
);
if (!backend) {
throw new Error('OPFS not available');
}
// Cast to the extended type since we know it's compatible
return backend as unknown as OpfsFilesystemBackend;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ function getBundlePathSegments(siteSlug: string): string[] {
* Check if a site has a persisted blueprint bundle.
*/
export async function hasBlueprintBundle(siteSlug: string): Promise<boolean> {
const backend = await OpfsFilesystemBackend.fromPath(
getBundlePathSegments(siteSlug)
);
if (!backend) {
try {
const backend = await OpfsFilesystemBackend.fromPath(
getBundlePathSegments(siteSlug)
);
const files = await backend.listFiles('/');
return files.length > 0;
} catch {
return false;
}
const files = await backend.listFiles('/');
return files.length > 0;
}

/**
Expand All @@ -48,21 +49,20 @@ export async function persistBlueprintBundle(
getBundlePathSegments(siteSlug),
true
);
if (!destination) {
throw new Error('OPFS not available');
}
await copyFilesystem(source, destination);
}

/**
* Delete a site's blueprint bundle.
*/
export async function deleteBlueprintBundle(siteSlug: string): Promise<void> {
const backend = await OpfsFilesystemBackend.fromPath(
getBundlePathSegments(siteSlug)
);
if (backend) {
try {
const backend = await OpfsFilesystemBackend.fromPath(
getBundlePathSegments(siteSlug)
);
await backend.clear();
} catch {
// Bundle doesn't exist, nothing to delete
}
}

Expand All @@ -73,11 +73,5 @@ export async function deleteBlueprintBundle(siteSlug: string): Promise<void> {
export async function loadPersistedBlueprintBundle(
siteSlug: string
): Promise<OpfsFilesystemBackend> {
const backend = await OpfsFilesystemBackend.fromPath(
getBundlePathSegments(siteSlug)
);
if (!backend) {
throw new Error(`Blueprint bundle not found for site: ${siteSlug}`);
}
return backend;
return OpfsFilesystemBackend.fromPath(getBundlePathSegments(siteSlug));
}
Loading