pixelkiln 0.27.0 → 0.28.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/dist/index.d.cts CHANGED
@@ -7543,8 +7543,11 @@ type GalleryState = PlanState | "undeclared";
7543
7543
  interface GalleryMedia {
7544
7544
  /** Absolute path of the file the server may read for this id. */
7545
7545
  path: string;
7546
- contentType: MediaType;
7546
+ /** Images, or the editor's own project file kept beside a browser edit. */
7547
+ contentType: MediaType | typeof PROJECT_FILE_TYPE;
7547
7548
  }
7549
+ /** Served type of a Pixelorama `.pxo`: a zip the browser never renders. */
7550
+ declare const PROJECT_FILE_TYPE = "application/octet-stream";
7548
7551
  interface GalleryOutput {
7549
7552
  /** Manifest-relative path as recorded (or as the manifest would record it). */
7550
7553
  path: string;
@@ -7802,6 +7805,8 @@ interface GalleryEditMeta {
7802
7805
  changedSince: boolean;
7803
7806
  /** Manifest-relative path of the layered project file kept beside the edit, if present. */
7804
7807
  project: string | null;
7808
+ /** Where the page fetches that file to hand it back to the editor; null without one. */
7809
+ projectUrl: string | null;
7805
7810
  }
7806
7811
  declare function buildGallerySnapshot(opts: BuildGalleryOptions): Promise<GalleryBuild>;
7807
7812
  interface BuildWorkspaceGalleryOptions {
package/dist/index.d.ts CHANGED
@@ -7543,8 +7543,11 @@ type GalleryState = PlanState | "undeclared";
7543
7543
  interface GalleryMedia {
7544
7544
  /** Absolute path of the file the server may read for this id. */
7545
7545
  path: string;
7546
- contentType: MediaType;
7546
+ /** Images, or the editor's own project file kept beside a browser edit. */
7547
+ contentType: MediaType | typeof PROJECT_FILE_TYPE;
7547
7548
  }
7549
+ /** Served type of a Pixelorama `.pxo`: a zip the browser never renders. */
7550
+ declare const PROJECT_FILE_TYPE = "application/octet-stream";
7548
7551
  interface GalleryOutput {
7549
7552
  /** Manifest-relative path as recorded (or as the manifest would record it). */
7550
7553
  path: string;
@@ -7802,6 +7805,8 @@ interface GalleryEditMeta {
7802
7805
  changedSince: boolean;
7803
7806
  /** Manifest-relative path of the layered project file kept beside the edit, if present. */
7804
7807
  project: string | null;
7808
+ /** Where the page fetches that file to hand it back to the editor; null without one. */
7809
+ projectUrl: string | null;
7805
7810
  }
7806
7811
  declare function buildGallerySnapshot(opts: BuildGalleryOptions): Promise<GalleryBuild>;
7807
7812
  interface BuildWorkspaceGalleryOptions {
package/dist/index.js CHANGED
@@ -9678,6 +9678,7 @@ function createGalleryEditHandler(opts) {
9678
9678
  }
9679
9679
 
9680
9680
  // src/gallery/snapshot.ts
9681
+ var PROJECT_FILE_TYPE = "application/octet-stream";
9681
9682
  function galleryMediaId(absolutePath) {
9682
9683
  return createHash4("sha256").update(path24.resolve(absolutePath)).digest("hex").slice(0, 24);
9683
9684
  }
@@ -9827,12 +9828,20 @@ async function buildGallerySnapshot(opts) {
9827
9828
  const companion = await readHandEditCompanion(editPath);
9828
9829
  if (companion) {
9829
9830
  const projectPath = handEditProjectPath(editPath);
9831
+ const project = companion.project && existsSync19(projectPath) ? await fileInfo(projectPath) : null;
9832
+ let projectUrl = null;
9833
+ if (project) {
9834
+ const id = galleryMediaId(projectPath);
9835
+ media.set(id, { path: projectPath, contentType: PROJECT_FILE_TYPE });
9836
+ projectUrl = `${galleryMediaRoute(id)}?v=${project.bytes}-${Date.parse(project.modifiedAt).toString(36)}`;
9837
+ }
9830
9838
  editMeta = {
9831
9839
  editor: companion.editor,
9832
9840
  savedAt: companion.savedAt,
9833
9841
  basedOn: companion.basedOn,
9834
9842
  changedSince: editSha !== null && editSha !== companion.sha256,
9835
- project: companion.project && existsSync19(projectPath) ? portableOutputPath(projectPath, root) : null
9843
+ project: project ? portableOutputPath(projectPath, root) : null,
9844
+ projectUrl
9836
9845
  };
9837
9846
  }
9838
9847
  const regenerated = companion ? generated?.sha256 !== void 0 && generated.sha256 !== null && companion.basedOn !== generated.sha256 : Boolean(generated?.modifiedAt && edit.modifiedAt && generated.modifiedAt > edit.modifiedAt && generated.exists);
@@ -10923,30 +10932,42 @@ function openEditorSheet(item) {
10923
10932
  stage.append(frame, loading);
10924
10933
  panel.append(bar, stage);
10925
10934
  host.append(scrim, panel);
10926
- SHEET = { item, frame, loading, status, msg, save, saveClose, dirty: false, opened: false, ready: null, pending: null, requests: 0, source: src };
10935
+ SHEET = { item, frame, loading, status, msg, save, saveClose, dirty: false, opened: false, ready: null, pending: null, requests: 0, source: src, sentProject: false };
10927
10936
  sheetStatus('cool', 'loading');
10928
10937
  sheetButtons();
10929
10938
  close.focus({ preventScroll: true });
10930
10939
  }
10940
+ // The edit file is what the editor gets; when a browser save kept the layered
10941
+ // project beside it, that goes along too and the editor restores the layers,
10942
+ // falling back to the flattened PNG if the file cannot be read.
10931
10943
  async function sendOpen() {
10932
10944
  const { item, source } = SHEET;
10933
10945
  const style = snap.styles.find((s) => s.id === item.styleId && s.project === item.project);
10934
- let png;
10946
+ const projectUrl = source === item.edit && item.editMeta && item.editMeta.projectUrl && SHEET.ready.version >= 2 ? item.editMeta.projectUrl : null;
10947
+ let png, pxo = null;
10935
10948
  try {
10936
10949
  const res = await fetch(source.url, { cache: 'no-store' });
10937
10950
  if (!res.ok) throw new Error(await res.text());
10938
10951
  png = await res.arrayBuffer();
10952
+ if (projectUrl) {
10953
+ const p = await fetch(projectUrl, { cache: 'no-store' });
10954
+ if (p.ok) pxo = await p.arrayBuffer();
10955
+ }
10939
10956
  } catch (err) {
10940
10957
  if (SHEET) { SHEET.msg.textContent = 'Could not load the image: ' + err.message; sheetStatus('bad', 'failed'); }
10941
10958
  return;
10942
10959
  }
10943
10960
  if (!SHEET) return;
10944
10961
  const request = 'open-' + (++SHEET.requests);
10945
- SHEET.frame.contentWindow.postMessage({
10962
+ SHEET.sentProject = !!pxo;
10963
+ const message = {
10946
10964
  type: 'pixelkiln:open', request,
10947
10965
  asset: { key: item.styleId + '/' + item.assetId, id: item.id, name: item.assetId, width: item.width, height: item.height },
10948
10966
  png, palette: style ? style.palette : [],
10949
- }, location.origin, [png]);
10967
+ };
10968
+ const transfer = [png];
10969
+ if (pxo) { message.pxo = pxo; transfer.push(pxo); }
10970
+ SHEET.frame.contentWindow.postMessage(message, location.origin, transfer);
10950
10971
  sheetStatus('cool', 'opening');
10951
10972
  }
10952
10973
  function requestEditorSave(close) {
@@ -10972,7 +10993,9 @@ async function onEditorMessage(m) {
10972
10993
  case 'pixelkiln:opened':
10973
10994
  SHEET.opened = true;
10974
10995
  SHEET.loading.remove();
10975
- sheetStatus('ok', SHEET.item.edit && SHEET.item.edit.exists ? 'editing the edit file' : 'editing a copy of the generated art');
10996
+ if (m.source === 'pxo') sheetStatus('ok', 'editing the edit file with its ' + (m.layers === 1 ? 'layer' : m.layers + ' layers') + ' restored');
10997
+ else if (SHEET.sentProject) sheetStatus('warn', 'editing the flattened edit file; its layer file could not be opened');
10998
+ else sheetStatus('ok', SHEET.item.edit && SHEET.item.edit.exists ? 'editing the edit file' : 'editing a copy of the generated art');
10976
10999
  sheetButtons();
10977
11000
  break;
10978
11001
  case 'pixelkiln:dirty':