babylon.quarks-editor 0.20.0 → 0.21.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/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@ All notable changes to the `babylon.quarks-editor` package are documented here.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
5
  and the project follows the `quarks.core` 0.x version line.
6
6
 
7
+ ## [0.21.0] — 2026-10-16
8
+
9
+ ### Changed
10
+
11
+ - Asset folder import inlines external textures and geometries through
12
+ `QuarksLoader.resolveLinkedResources` and a File-backed `QuarksResourceResolver`, matching the
13
+ runtime loader path instead of a private `resolveExternalGeometries` helper.
14
+
7
15
  ## [0.20.0] — 2026-10-16
8
16
 
9
17
  ### Added
@@ -87,31 +87,53 @@ class ImportedFileIndex {
87
87
  }
88
88
  }
89
89
  /**
90
- * Inlines what an "assets as files" export put in separate files: image urls become data URIs
91
- * and external geometries are read into the JSON, so the effect loads (and later exports) as a
90
+ * File-backed {@link QuarksResourceResolver}: relative image urls become data URIs and geometry
91
+ * urls load from the imported file set. Collects missing references into `missing`.
92
+ */
93
+ function createFileResolver(effectPath, index, missing) {
94
+ return {
95
+ async resolve(url, kind, baseUrl) {
96
+ if (kind === 'image') {
97
+ if (isExternalUrl(url)) {
98
+ return url;
99
+ }
100
+ const file = index.find(baseUrl || effectPath, url);
101
+ if (!file) {
102
+ missing.push(url);
103
+ return url;
104
+ }
105
+ return index.read(file, readAsDataUrl);
106
+ }
107
+ // Geometry (and other kinds): stable path key for fetchJson.
108
+ const file = index.find(baseUrl || effectPath, url);
109
+ if (!file) {
110
+ missing.push(url);
111
+ throw new Error('not among the imported files');
112
+ }
113
+ return relativePathOf(file);
114
+ },
115
+ fetchJson(resolvedUrl) {
116
+ // resolvedUrl is relativePathOf(file) from resolve(); look up by path (empty base).
117
+ const file = index.find('', resolvedUrl);
118
+ if (!file) {
119
+ throw new Error(`missing file "${resolvedUrl}"`);
120
+ }
121
+ return index.read(file, async (f) => JSON.parse(await f.text()));
122
+ },
123
+ };
124
+ }
125
+ /**
126
+ * Inlines what an "assets as files" export put in separate files via
127
+ * {@link QuarksLoader.resolveLinkedResources}, so the effect loads (and later exports) as a
92
128
  * self-contained effect. Returns the references that could not be found among the files.
93
129
  */
94
130
  async function inlineExternalFiles(json, effectPath, index) {
95
131
  const missing = [];
96
- for (const image of Array.isArray(json.images) ? json.images : []) {
97
- const url = image?.url;
98
- if (typeof url !== 'string' || isExternalUrl(url))
99
- continue;
100
- const file = index.find(effectPath, url);
101
- if (file) {
102
- image.url = await index.read(file, readAsDataUrl);
103
- }
104
- else {
105
- missing.push(url);
106
- }
107
- }
108
- await QuarksLoader.resolveExternalGeometries(json, async (url) => {
109
- const file = index.find(effectPath, url);
110
- if (!file) {
111
- missing.push(url);
112
- throw new Error('not among the imported files');
113
- }
114
- return index.read(file, async (f) => JSON.parse(await f.text()));
132
+ // baseUrl is the effect file path so resolveRelativePath can walk from its folder; the
133
+ // File resolver ignores HTTP join and looks up files by path/name instead.
134
+ await QuarksLoader.resolveLinkedResources(json, {
135
+ baseUrl: effectPath,
136
+ resolver: createFileResolver(effectPath, index, missing),
115
137
  });
116
138
  return missing;
117
139
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "babylon.quarks-editor",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Shuriken-style effect editor for babylon.quarks: headless edit-model core + React module-stack UI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,3 +1,4 @@
1
+ import type {QuarksResourceKind, QuarksResourceResolver} from 'babylon.quarks';
1
2
  import {QuarksLoader} from 'babylon.quarks';
2
3
 
3
4
  /**
@@ -124,33 +125,55 @@ class ImportedFileIndex {
124
125
  }
125
126
 
126
127
  /**
127
- * Inlines what an "assets as files" export put in separate files: image urls become data URIs
128
- * and external geometries are read into the JSON, so the effect loads (and later exports) as a
128
+ * File-backed {@link QuarksResourceResolver}: relative image urls become data URIs and geometry
129
+ * urls load from the imported file set. Collects missing references into `missing`.
130
+ */
131
+ function createFileResolver(effectPath: string, index: ImportedFileIndex, missing: string[]): QuarksResourceResolver {
132
+ return {
133
+ async resolve(url: string, kind: QuarksResourceKind, baseUrl: string): Promise<string> {
134
+ if (kind === 'image') {
135
+ if (isExternalUrl(url)) {
136
+ return url;
137
+ }
138
+ const file = index.find(baseUrl || effectPath, url);
139
+ if (!file) {
140
+ missing.push(url);
141
+ return url;
142
+ }
143
+ return index.read(file, readAsDataUrl);
144
+ }
145
+ // Geometry (and other kinds): stable path key for fetchJson.
146
+ const file = index.find(baseUrl || effectPath, url);
147
+ if (!file) {
148
+ missing.push(url);
149
+ throw new Error('not among the imported files');
150
+ }
151
+ return relativePathOf(file);
152
+ },
153
+ fetchJson(resolvedUrl: string): Promise<unknown> {
154
+ // resolvedUrl is relativePathOf(file) from resolve(); look up by path (empty base).
155
+ const file = index.find('', resolvedUrl);
156
+ if (!file) {
157
+ throw new Error(`missing file "${resolvedUrl}"`);
158
+ }
159
+ return index.read(file, async (f) => JSON.parse(await f.text()));
160
+ },
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Inlines what an "assets as files" export put in separate files via
166
+ * {@link QuarksLoader.resolveLinkedResources}, so the effect loads (and later exports) as a
129
167
  * self-contained effect. Returns the references that could not be found among the files.
130
168
  */
131
169
  async function inlineExternalFiles(json: any, effectPath: string, index: ImportedFileIndex): Promise<string[]> {
132
170
  const missing: string[] = [];
133
-
134
- for (const image of Array.isArray(json.images) ? json.images : []) {
135
- const url = image?.url;
136
- if (typeof url !== 'string' || isExternalUrl(url)) continue;
137
- const file = index.find(effectPath, url);
138
- if (file) {
139
- image.url = await index.read(file, readAsDataUrl);
140
- } else {
141
- missing.push(url);
142
- }
143
- }
144
-
145
- await QuarksLoader.resolveExternalGeometries(json, async (url) => {
146
- const file = index.find(effectPath, url);
147
- if (!file) {
148
- missing.push(url);
149
- throw new Error('not among the imported files');
150
- }
151
- return index.read(file, async (f) => JSON.parse(await f.text()));
171
+ // baseUrl is the effect file path so resolveRelativePath can walk from its folder; the
172
+ // File resolver ignores HTTP join and looks up files by path/name instead.
173
+ await QuarksLoader.resolveLinkedResources(json, {
174
+ baseUrl: effectPath,
175
+ resolver: createFileResolver(effectPath, index, missing),
152
176
  });
153
-
154
177
  return missing;
155
178
  }
156
179