201 lines
16 KiB
Markdown
201 lines
16 KiB
Markdown
# Content Workspace
|
|
|
|
Blacksite treats the project `assets/` tree as a user-organized content workspace. Folders are for
|
|
organization and do not determine asset type. The stable asset registry ID is authoritative; a path
|
|
is mutable metadata that moves, validation, and packaging repair through the shared content catalog.
|
|
|
|
## Content boundaries
|
|
|
|
- User assets may live in any normal nested folder beneath `assets/`.
|
|
- External absolute linked assets are unsupported so projects remain portable and packageable.
|
|
- `.index`, `.trash`, thumbnails, recovery data, and derived artifacts are hidden and protected.
|
|
- Authored document schemas and typed registry records identify RON assets. Ambiguous or invalid
|
|
documents report diagnostics instead of being guessed from their folder.
|
|
|
|
## Content Browser workflow
|
|
|
|
The Content Browser toolbar provides **New Folder**, **Rename**, **Duplicate**, **Cut**, **Copy**,
|
|
and **Paste**. The selected folder is the destination for paste and **File > Import Assets**. These
|
|
operations use the shared content transaction engine: it previews collisions, rejects managed or
|
|
symlink paths, stages filesystem and cached-reference changes together, preserves IDs on moves,
|
|
assigns new IDs to copies, and rolls back on failure. Successful move, rename, create-folder, copy,
|
|
duplicate, and authored-file creation transactions add a content-specific **Undo Content** entry.
|
|
Move/rename undo applies the inverse reference-safe transaction. Undoing newly created content
|
|
moves the unchanged result into transactional Trash, so it remains restorable instead of being
|
|
permanently deleted. Every undo is blocked if any affected file or folder has changed externally
|
|
since commit. Registry and stripped runtime-catalog publication are part of the same rollback
|
|
boundary.
|
|
|
|
Before create-folder, rename, move, copy, duplicate, paste, or drag-to-folder commits, **Review
|
|
Content Operation** lists every source/destination, affected registered-asset count, and each cached
|
|
RON document whose path hint will be rewritten. Collisions are shown in the same window and disable
|
|
commit. Read-only files, provider locks, and source-control conflicts are visible and block commit;
|
|
modified/untracked state remains visible but does not. Source and rewrite-document fingerprints are
|
|
captured for the review, so any external edit before confirmation also blocks the stale transaction.
|
|
Cancel restores the pre-review folder, selection, anchor, status, clipboard, and drag state; a
|
|
cancelled drag review clears only the stale drag gesture. Folder-scoped **Paste Into** and
|
|
**Import To** pin their destinations without navigating the browser.
|
|
|
|
Delete remains trash-first. **Trash** in the toolbar, **Open Trash** in a folder/empty-space context
|
|
menu, and the restore window expose complete deletion batches. A new batch records the original
|
|
paths and registry records before moving bytes under `assets/.trash/`. Model batches also carry
|
|
their generated static/animation manifests so deletion or guarded undo cannot leave orphan runtime
|
|
artifacts; restoring the batch restores those exact derived bytes with the source. Restore preserves
|
|
stable IDs and is blocked if a destination path, derived path, or registry ID is now occupied.
|
|
Legacy trash batches are shown when their original paths can be inferred, but have no historical
|
|
registry IDs to recover. A normal Move to Trash action also records the exact batch in Content Undo,
|
|
so Ctrl/Cmd+Z restores it when the batch fingerprint and destinations remain valid.
|
|
Before confirmation, the trash review enumerates external authored RON documents, model mappings,
|
|
project-default selection, and dependency records that will become unresolved; references inside
|
|
the same selected tree are excluded from that warning.
|
|
|
|
Selection follows desktop file-manager conventions in both grid and list views: click selects one,
|
|
Ctrl/Cmd-click toggles an item, Shift-click selects the visible range from the anchor, and
|
|
Ctrl/Cmd+A selects every visible result. Folders are first-class selectable and draggable items;
|
|
double-click opens them and clears the previous folder's selection so hidden content cannot be
|
|
changed accidentally. Starting a drag on an unselected item selects that item first, while dragging
|
|
one member of an existing multi-selection preserves the complete batch. Batch Cut, Copy, Duplicate,
|
|
drag-to-folder, and Move to Trash operate on the complete selection while removing nested duplicates
|
|
such as a selected folder and one of its children; the menu reports those children as covered by
|
|
the parent rather than mislabeling them as ignored. Changing search text, recursive scope, or the
|
|
kind filter clears the selection so a now-hidden item cannot receive a keyboard operation. Sorting
|
|
and switching grid/list view preserve selection because they do not hide results. Content shortcuts only take ownership while the
|
|
pointer is over the content pane: Ctrl/Cmd+C, X, V, D, A and Z, Ctrl/Cmd+Shift+N, F2, and Delete.
|
|
Ctrl/Cmd+Z invokes the most recent guarded Content Browser operation; it does not consume scene
|
|
history while the pointer is outside the content pane. The browser surface and its shortcuts are
|
|
disabled while any Content Browser review/modal or popup menu is open, so a confirmation click or
|
|
keystroke cannot also mutate the selection behind it. Cut/Paste keeps the exact requested basename; an occupied destination is
|
|
shown as a collision in **Review Content Operation** instead of being silently renamed.
|
|
Embedded mesh, material, texture, skeleton, and animation rows are authoring subassets rather than
|
|
filesystem entries; generic rename/clipboard/drag-to-folder/trash commands never reinterpret one as
|
|
its parent model file.
|
|
|
|
Right-clicking an unselected file, folder, or embedded model subasset makes it the primary selection
|
|
before opening its menu. Right-clicking one member of an existing multi-selection preserves the
|
|
complete batch. File menus expose type-specific actions plus Rename, Cut, Copy, Duplicate, and Move
|
|
to Trash. Folder menus add destination-scoped import, paste, and creation actions. Right-clicking
|
|
unused content space clears any old selection and opens workspace actions for **Import Here**,
|
|
**Import To**, **Create Material Here**, **Create Materials From Folder**, **New Folder**, **Paste**,
|
|
**Select All**, **Refresh**, and grid/list view selection. New Materials are authored directly in the
|
|
current folder; the Material Library's create button retains `assets/materials/` as its convenient
|
|
default.
|
|
Double-clicking a scene opens it, a model toggles its embedded contents, and other asset types reveal
|
|
their editable Details. The footer and Details pane summarize multi-selections; single-selection
|
|
Details remains the authoritative editor for import and material settings. Drag the Details divider
|
|
to resize it for long slot/shader forms; double-click the divider to restore its default width.
|
|
Texture Details uses the same pane for semantic/color-space intent, mip generation, UASTC or
|
|
uncompressed KTX2 output, resize limits, sampler settings, and normal-Y conversion. Changes become
|
|
an **UNSAVED** Texture document immediately; explicit Save publishes the settings and queues
|
|
affected-only derived processing without rewriting the editable source image.
|
|
|
|
Thumbnail cache keys are typed by asset category. Texture cards may use direct image loads, while
|
|
model cards always queue the offscreen geometry studio—even when a glTF has a base-color texture.
|
|
Expanded mesh rows request their exact mesh-subasset render, and source materials use a separate
|
|
material-sphere key. Missing dependencies produce an explicit failed thumbnail; a model or mesh
|
|
card never substitutes an albedo texture. Replacing, retrying, or invalidating a thumbnail also
|
|
unregisters its last Egui image handle, so repeated catalog refreshes release the prior GPU image
|
|
instead of accumulating hidden render targets.
|
|
|
|
**Import Here** pins the current/selected folder before the native picker opens. **Import To...**
|
|
first opens a project-folder chooser without navigating away from the current browser location.
|
|
After files are chosen, a second review lists every source dependency and exact final project path;
|
|
collisions disable commit and cancel publishes nothing. Source/dependency fingerprints and target
|
|
absence are checked again at commit so external changes after review cannot overwrite project data.
|
|
|
|
External FBX and textual glTF imports preserve relative texture/buffer layouts as one staged bundle.
|
|
The complete multi-source picker batch is preflighted before mutation: existing targets and two
|
|
sources mapping to one target are reported as collisions, and any publication failure removes all
|
|
new files and directories from that batch. Missing dependencies, traversal, and symlink escapes
|
|
also fail before the destination changes. GLB is self-contained. Files already under `assets/` are
|
|
shown as **Adopt in place** in the review and processed without copying or type-folder sorting.
|
|
|
|
Folder/file Move preserves registry IDs and rewrites cached paths in project documents, model
|
|
defaults, the runtime catalog, and generated manifests. Copy/Duplicate assigns new IDs, remaps only
|
|
references internal to the copied tree, and leaves the originals plus external references
|
|
untouched. Copied models discard inherited generated-manifest paths so normal processing publishes
|
|
fresh artifacts under the copied model ID.
|
|
Out-of-editor moves reconcile imported assets only when one missing record has the matching kind
|
|
and source fingerprint. If the watcher finds one new file matching multiple missing records, it
|
|
opens **Resolve Ambiguous External Moves** and leaves registry/manifests unchanged. Each row must
|
|
either preserve exactly one candidate ID or intentionally **Register as New Asset**; one old ID
|
|
cannot be assigned to two new paths. **Review Later** leaves a **Resolve Moves** toolbar action, and
|
|
Apply rechecks the registry, files, and candidate set before publishing. Headless processing fails
|
|
with the same candidate paths until the choice is completed in the editor; neither path silently
|
|
replaces stable identity.
|
|
|
|
Imported static and skinned model Details expose stable material slots. Each slot selects its source
|
|
material, a project Material/Instance, or **Default**; bulk Source and Default actions are
|
|
available. Default intentionally skips the imported source and resolves through the project
|
|
fallback, then immutable DefaultGrid. Reimport reconciles by stable slot ID and retains removed project assignments as explicit
|
|
orphans. The displayed source health makes a missing imported material distinct from an intentional
|
|
Default selection. **Locate** reveals the referenced project Material/Instance, while **Clear**
|
|
returns an active model slot to Source. Orphan rows provide **Locate** and **Clear Orphan** so a
|
|
removed source slot can be reviewed and resolved explicitly before applying the import settings.
|
|
|
|
The former whole-model Source Materials/Authoring Override switch is migration input only. Explicit
|
|
processing expands legacy Authoring Override into a Default selection for each stable slot, resets
|
|
the legacy field, and publishes only per-slot selections to current registry/manifests. Validation
|
|
requires an FBX texture bundle while any slot uses Source; an all-Project/Default model keeps missing
|
|
source textures visible as informational provenance instead of a release blocker.
|
|
|
|
For glTF, GLB, and FBX, **Extract Editable Materials Here** opens a conversion review containing
|
|
every source material, converted metallic/roughness and render state, detected external texture
|
|
channels, and the editable final project paths. Embedded texture payloads remain source-owned; the
|
|
review still converts supported scalar PBR values. Confirmed entries become normal
|
|
`*.material.ron` assets and every model draw slot using that source material is changed to a
|
|
`Project(...)` selection in the same rollback-capable content transaction. The generated model
|
|
manifest is refreshed immediately so editor and packaged runtime bindings agree. A first
|
|
extraction uses a collision-free path. On a later extraction into the same folder, matching source
|
|
path and stable source-slot provenance opens an explicit diff: choose **Apply Reviewed Update** to
|
|
replace the edited Material, or **Create New Copy** to preserve it as a separate revision. Apply is
|
|
guarded by the reviewed file fingerprint, participates in transaction rollback, and is blocked if
|
|
the file changes before publication; mismatched provenance can never overwrite an unrelated
|
|
Material. Each extracted Material records read-only source path, stable source-slot identity, and
|
|
source-content fingerprint provenance in Details.
|
|
|
|
For loose texture sets, right-click their folder or unused space inside it and choose **Create
|
|
Materials From Folder**. The review window detects common PBR suffixes such as `albedo`,
|
|
`basecolor`, `diff`, `normal`, `nor_gl`, `roughness`, `metallic`, `ao`, `orm`/`arm`, `height`, and
|
|
`displacement`. Every row shows its confidence and stays editable. Change a row's **Target** to
|
|
merge it into another material or split it into a new material; change **Role** to correct a guess.
|
|
Unresolved images remain visible and start unchecked. Creation publishes every selected group as
|
|
one atomic content transaction and chooses a suffixed filename instead of overwriting an existing
|
|
Material. Packed ORM/ARM maps use R=occlusion, G=roughness, and B=metallic.
|
|
|
|
Project Settings > Content selects an optional project fallback Material/Instance. Clear restores
|
|
the immutable **Default Grid — Engine Built-in**. DefaultGrid is world-space and UV-independent, so
|
|
broken or unassigned meshes stay visible.
|
|
|
|
Material, Material Instance, model-import, Texture-import, and project-default edits live in an
|
|
editor-owned authored-document store until explicitly saved. **UNSAVED** is editor state and is
|
|
shown separately from Git state. `Ctrl+S` saves the last edited context; `Ctrl+Shift+S` and
|
|
**File > Save All** save every dirty scene, asset document, and project setting. Selection changes
|
|
never discard a dirty asset. Source publication is guarded and atomic, then affected-only derived
|
|
processing runs in the background while the previous valid artifact remains active.
|
|
Material scalar/color/render-state saves do not show `PROCESSING`; only packed-map binding changes
|
|
do. Content Browser and Material Library cards share clipped two-line labels, a top-right grid-card
|
|
status rail, and fixed-width list markers, so state never paints over or shifts an asset name.
|
|
|
|
## Headless processing
|
|
|
|
Run `cargo process-assets --project <root>` to deterministically refresh registry v3, normalized
|
|
static/animation model manifests, processed Texture artifacts, canonical Material ARM maps, and
|
|
`assets/content.catalog.ron` without a GPU. All planned artifacts and both catalogs publish under
|
|
one rollback boundary. Use `--check` in CI for a strictly read-only comparison that fails on any
|
|
stale artifact, and `--json` for machine-readable diagnostics. Texture and packed-Material keys are
|
|
resolved before image decode; an existing content-addressed artifact is reused, so an unchanged
|
|
check validates source hashes and catalog references without recompressing images.
|
|
Editor startup, the toolbar/empty-space **Refresh** action, and the shared watcher all use the same
|
|
processor. Saved authored documents enqueue revisioned, coalesced jobs rather than running the
|
|
whole processor on the UI frame. The watcher debounces and deduplicates user-content paths, ignores
|
|
managed/generated publication noise, incrementally rescans the same registry/catalog model,
|
|
refreshes selection-safe browser state, and invalidates thumbnail caches. Selection-only UI changes
|
|
never fingerprint or republish content. Editor transactions suppress their filesystem events until commit. Failures remain visible
|
|
in the editor status line. When an external deletion leaves authored references unresolved, the
|
|
status includes their count and affected document paths. Thumbnail rendering remains editor-owned.
|
|
Ambiguous move repair is rediscovered on startup and manual refresh, so choosing **Review Later**
|
|
cannot lose the repair entry across an editor restart.
|
|
|
|
The permanent storage, material precedence, and fallback decisions are in
|
|
[ADR 0045](../adr/0045-content-workspace-and-material-fallback-contract.md).
|