feat: F4.6f3 the file explorer and the image viewer

Oracle FileExplorerWindowRun uses Java's explorer and viewer windows as a
person does (the right-click menu, Java's questions answered, the import
window, the viewer's open). Both windows work on the document's files
through the core's operations: the tree of folders and files, the chosen
folder's entries, Java's menu (also as buttons, no right click needed),
copy that stays and cut that goes once pasted, the import; the viewer's
picture and title. Not taken from Java: a file chosen in the tree taken
for a folder, pictures laid over each other, a text file as an empty
picture. writeFiles numbers the entries in the file system's order again
when a rename made a new key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 18:13:22 +00:00
co-authored by Claude Opus 5.5
parent 2c7733b575
commit 3dc2713f81
21 changed files with 2951 additions and 39 deletions
+10 -2
View File
@@ -11,7 +11,7 @@
import * as Y from 'yjs';
import type { Id, VirtualFileContent, VirtualFileSystem } from '@mdg-filius/core';
import { VirtualFileSystem as FileSystem } from '@mdg-filius/core';
import { fileEntry, fileSystemOf, ROOTS } from './projectDoc.js';
import { fileEntry, fileSystemOf, inOrder, ROOTS } from './projectDoc.js';
export function sameBytes(left: Uint8Array, right: Uint8Array): boolean {
return left.length === right.length && left.every((byte, index) => byte === right[index]);
@@ -36,7 +36,11 @@ function holds(entry: Y.Map<unknown>, content: VirtualFileContent): boolean {
return sameBytes(entry.get('data') as Uint8Array, content.data) && entry.get('mediaType') === content.mediaType;
}
/** A node's files as the file system has them: new ones after the others, changed ones in their place, gone ones removed. */
/**
* A node's files as the file system has them: new ones after the others,
* changed ones in their place, gone ones removed; when the order is no
* longer the file system's (a renamed entry), numbered anew.
*/
export function writeFiles(node: Y.Map<unknown>, fileSystem: VirtualFileSystem): void {
let files = node.get('files') as Y.Map<Y.Map<unknown>> | undefined;
const wanted: [string, VirtualFileContent][] = [];
@@ -65,6 +69,10 @@ export function writeFiles(node: Y.Map<unknown>, fileSystem: VirtualFileSystem):
replaceText(entry.get('text') as Y.Text, content.text);
}
}
// An entry renamed is new to the document: the order is the file system's again (a rename keeps the entry's place).
if (inOrder(files).some(([path], index) => path !== wanted[index]![0])) {
for (const [index, [path]] of wanted.entries()) if (files.get(path)!.get('order') !== index) files.get(path)!.set('order', index);
}
}
function nodeMap(doc: Y.Doc, id: Id): Y.Map<unknown> | undefined {