scenri 0.11.0 → 0.12.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
@@ -1,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.12.0](https://github.com/tonygorb/Scenri/compare/v0.11.1...v0.12.0) (2026-09-20)
4
+
5
+
6
+ ### Features
7
+
8
+ * archive a selection, and make the selection dock a toolbar ([a2697a2](https://github.com/tonygorb/Scenri/commit/a2697a26201314185e609125d04a2bf6391c4e6e))
9
+ * **create:** archive a whole selection from the dock ([d5c2181](https://github.com/tonygorb/Scenri/commit/d5c2181210f0f92b1df528b0113a773a049e916d))
10
+ * **studio:** make the selection dock a toolbar ([362112e](https://github.com/tonygorb/Scenri/commit/362112e9d1bc2be6bf171f0b0702e61ca8845720))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * ask the browser whether the page is still here ([2800f8d](https://github.com/tonygorb/Scenri/commit/2800f8d72feb294b7a54da090242ecb2a66e6929))
16
+ * **brand:** a deleted brand does not reopen its dialogs on the next one ([dbe69eb](https://github.com/tonygorb/Scenri/commit/dbe69eb2e6679dc068c426721a0a0ebbfd39c3ca))
17
+ * **brand:** brand saves, renames, deletes and shot wipes reach the screen ([020c905](https://github.com/tonygorb/Scenri/commit/020c905c6700d8e909cef025d437bc7d4566b663))
18
+ * **create:** a shot restored from the archive comes back to the feed ([131d132](https://github.com/tonygorb/Scenri/commit/131d1329dac03a4d43299636ecf94cb90a26c0e1))
19
+ * **create:** keep a phone's toast clear of the tab bar ([c42f947](https://github.com/tonygorb/Scenri/commit/c42f94791f226879d15f460f2e054ddd79529a9b))
20
+ * **create:** the selection dock is a shelf on a phone, not the pill squeezed ([ffac597](https://github.com/tonygorb/Scenri/commit/ffac5970779d28dfabd52d7655d7469a993417e6))
21
+ * make every saved change reach every surface without a reload ([fb1e517](https://github.com/tonygorb/Scenri/commit/fb1e517170d1327eb4b1ce8af5a6e93d42c9ed7e))
22
+ * **presenters:** a saved person is on every surface the moment they land ([dd6ed16](https://github.com/tonygorb/Scenri/commit/dd6ed16ba1e687c1bf8848bb1f070349663ddb81))
23
+ * **products:** a product edit or delete reaches every surface at once ([2e34a76](https://github.com/tonygorb/Scenri/commit/2e34a765fe038e26d440a2ee586ea7844b6c35b9))
24
+ * **scenes:** a deleted scene leaves every surface at once ([1d132cf](https://github.com/tonygorb/Scenri/commit/1d132cff749437f1ed6ec5ea7635eaa92125763e))
25
+ * **studio:** ask the browser, not the last render, whether the page is still here ([0c4d874](https://github.com/tonygorb/Scenri/commit/0c4d8742a7358dad11fc5d6a2d137f8bb9d11448))
26
+ * **studio:** hand focus on after a delete, and write the rule down ([07efb5c](https://github.com/tonygorb/Scenri/commit/07efb5c653af482e4725587a731dceb1701de130))
27
+ * **studio:** let the selection bar undo Select all ([da21207](https://github.com/tonygorb/Scenri/commit/da21207c9d1215e85b10670b7af8c3b5631462b3))
28
+ * **studio:** order brand answers so a late read never undoes a newer write ([abac146](https://github.com/tonygorb/Scenri/commit/abac146d5fa8ff559bb1105589d2c4418c5df94b))
29
+
30
+ ## [0.11.1](https://github.com/tonygorb/Scenri/compare/v0.11.0...v0.11.1) (2026-09-18)
31
+
32
+
33
+ ### Bug Fixes
34
+
35
+ * pause first-use guidance, and a bar panel opened from another stays open ([ad6fb8d](https://github.com/tonygorb/Scenri/commit/ad6fb8d3146f7e51ab257fdaab9c74cbef88dad8))
36
+ * **studio:** a bar panel opened while another is open stays open ([fb43b43](https://github.com/tonygorb/Scenri/commit/fb43b43c5e7cf44ce0bcd7a5dd3daf815b4c6875))
37
+ * **studio:** pause first-use guidance until it is finished ([6fd33f0](https://github.com/tonygorb/Scenri/commit/6fd33f074e2af8373a1ae8cdd7690468cc6e90dd))
38
+
3
39
  ## [0.11.0](https://github.com/tonygorb/Scenri/compare/v0.10.3...v0.11.0) (2026-09-18)
4
40
 
5
41
 
@@ -865,7 +865,7 @@ function createStore(db) {
865
865
  }));
866
866
  },
867
867
  updateBrand(id, json) {
868
- db.prepare("UPDATE brands SET json=?, slug=?, updated_at=datetime('now') WHERE id=?").run(
868
+ db.prepare("UPDATE brands SET json=?, slug=?, updated_at=strftime('%Y-%m-%d %H:%M:%f','now') WHERE id=?").run(
869
869
  JSON.stringify(json),
870
870
  uniqueSlug(db, json.meta.name, id),
871
871
  id
@@ -2224,5 +2224,5 @@ function createCore(homeDir = defaultHome()) {
2224
2224
  }
2225
2225
 
2226
2226
  export { ASPECT_TOLERANCE, BUDGET_EXHAUSTED, EDIT_REFERENCE_ROLE_DIRECTIVE, REFERENCE_ROLE_DIRECTIVE, SCHEMA_VERSION, STEM_MIN, SchemaTooNewError, SpendCapError, TRIGRAM_MIN, budgetSize, createCatalogStore, createCore, createStore, defaultHome, fold, ftsMatch, matchesQuery, ratioLabel, searchTerms, termMatches, uniqueProjectSlug, uniqueSetSlug, uniqueSlug };
2227
- //# sourceMappingURL=chunk-K6IELFL3.js.map
2228
- //# sourceMappingURL=chunk-K6IELFL3.js.map
2227
+ //# sourceMappingURL=chunk-MHTA6O3A.js.map
2228
+ //# sourceMappingURL=chunk-MHTA6O3A.js.map
@@ -463,7 +463,7 @@ async function addOrRepair(ownEntry, say, over) {
463
463
  }
464
464
  async function rememberDecline(home) {
465
465
  try {
466
- const { createCore } = await import('./src-TC4UQ7H6.js');
466
+ const { createCore } = await import('./src-INKSTJNC.js');
467
467
  const core = createCore(home);
468
468
  try {
469
469
  core.store.setSetting("desktop.prompt", "declined");
@@ -530,5 +530,5 @@ function tail(path, bytes = 4096) {
530
530
  }
531
531
 
532
532
  export { addToDesktop, assetsDirFor, installDeps, runDesktopCommand, runExecFile, runOpenCommand };
533
- //# sourceMappingURL=cli-GMRPOISO.js.map
534
- //# sourceMappingURL=cli-GMRPOISO.js.map
533
+ //# sourceMappingURL=cli-4A3QWRSC.js.map
534
+ //# sourceMappingURL=cli-4A3QWRSC.js.map
package/dist/index.js CHANGED
@@ -86,12 +86,12 @@ try {
86
86
  break;
87
87
  }
88
88
  case "desktop": {
89
- const { runDesktopCommand } = await import('./cli-GMRPOISO.js');
89
+ const { runDesktopCommand } = await import('./cli-4A3QWRSC.js');
90
90
  process.exit(await runDesktopCommand(command, fileURLToPath(import.meta.url)));
91
91
  break;
92
92
  }
93
93
  case "open": {
94
- const { runOpenCommand } = await import('./cli-GMRPOISO.js');
94
+ const { runOpenCommand } = await import('./cli-4A3QWRSC.js');
95
95
  process.exit(await runOpenCommand(fileURLToPath(import.meta.url)));
96
96
  break;
97
97
  }
package/dist/serve.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createUpdateChecker, classify, findNpm, isReleaseTriplet, stageVersion } from './chunk-YK37H6DM.js';
2
2
  import { shouldAdoptRunning, anotherScenriLines, portBusyLines } from './chunk-CMLJGPYC.js';
3
- import { SchemaTooNewError, createCore, BUDGET_EXHAUSTED, SpendCapError, EDIT_REFERENCE_ROLE_DIRECTIVE, REFERENCE_ROLE_DIRECTIVE, ratioLabel, searchTerms, termMatches, budgetSize, SCHEMA_VERSION, ASPECT_TOLERANCE } from './chunk-K6IELFL3.js';
3
+ import { SchemaTooNewError, createCore, BUDGET_EXHAUSTED, SpendCapError, EDIT_REFERENCE_ROLE_DIRECTIVE, REFERENCE_ROLE_DIRECTIVE, ratioLabel, searchTerms, termMatches, budgetSize, SCHEMA_VERSION, ASPECT_TOLERANCE } from './chunk-MHTA6O3A.js';
4
4
  import { detectInstallKind } from './chunk-PAIAXRAC.js';
5
5
  export { detectInstallKind } from './chunk-PAIAXRAC.js';
6
6
  import { readMeta, repoSlug } from './chunk-Y3ZPBPLP.js';
@@ -8007,6 +8007,7 @@ async function runSceneBuild(deps, job, hashes, instruction, signal) {
8007
8007
  commit(core, job.brandId, (json) => {
8008
8008
  const rows = brandScenes(json);
8009
8009
  const at = rows.findIndex((s) => s?.id === scene.id);
8010
+ if (at < 0 && job.sceneId) throw new Error("This scene was deleted while it was being read again.");
8010
8011
  json.scenes = at >= 0 ? rows.map((s, i) => i === at ? scene : s) : [...rows, scene];
8011
8012
  });
8012
8013
  patch(job, {
@@ -8052,6 +8053,11 @@ function sceneBuildRunning(brandId, sceneId) {
8052
8053
  }
8053
8054
  return false;
8054
8055
  }
8056
+ function cancelSceneBuilds(brandId, sceneId) {
8057
+ for (const b of builds.values()) {
8058
+ if (b.brandId === brandId && b.sceneId === sceneId && !b.finished) cancelAssetBuild(b.id);
8059
+ }
8060
+ }
8055
8061
  var LOOPBACK = ["localhost", "127.0.0.1", "::1"];
8056
8062
  var ACCESS_COOKIE = "sc_access";
8057
8063
  var LEGACY_ACCESS_COOKIE = "bt_access";
@@ -10947,10 +10953,16 @@ function registerAssetBuildRoutes(app, deps) {
10947
10953
  if (!brand) return;
10948
10954
  const id = String(req.params.sceneId);
10949
10955
  if (!brandScenes(brand.json).some((s) => s.id === id)) return reply.status(404).send({ error: "scene not found" });
10950
- commit(core, brand.id, (json) => {
10951
- json.scenes = brandScenes(json).filter((s) => s.id !== id);
10952
- });
10953
- return { ok: true };
10956
+ cancelSceneBuilds(brand.id, id);
10957
+ let row;
10958
+ try {
10959
+ row = commit(core, brand.id, (json) => {
10960
+ json.scenes = brandScenes(json).filter((s) => s.id !== id);
10961
+ });
10962
+ } catch (err) {
10963
+ return reply.status(err.statusCode ?? 500).send({ error: err.message });
10964
+ }
10965
+ return { ok: true, brand: row };
10954
10966
  });
10955
10967
  app.post("/api/brands/:id/scenes/:sceneId/reread", async (req, reply) => {
10956
10968
  const brand = brandOr404(req, reply);
@@ -11704,6 +11716,39 @@ function registerImageRoutes(app, deps) {
11704
11716
 
11705
11717
  // src/release/notes.data.ts
11706
11718
  var RELEASES = [
11719
+ {
11720
+ version: "0.12.0",
11721
+ date: "2026-09-20",
11722
+ title: "Saved work shows up everywhere at once, and a selection has its own toolbar.",
11723
+ sections: [
11724
+ {
11725
+ heading: "Create",
11726
+ body: "Pick several shots and the dock can archive the lot in one go, with a single Undo that brings them all back. That dock is a toolbar now: how many are picked, Select all, and the verbs as tools that name themselves. It is the same bar on a phone, at a size a thumb can hit."
11727
+ },
11728
+ {
11729
+ heading: "Brand",
11730
+ body: "A brand you save, rename or delete, and a workspace you wipe, show up on every screen straight away. A brand you have just deleted no longer reopens its dialogs on the brand you land in next."
11731
+ },
11732
+ {
11733
+ heading: "Fixes",
11734
+ body: "A product edit, a new presenter and a deleted scene each reach every surface that shows them, without a reload, and a slow answer can no longer undo a newer change. Walking to another page while a delete is still out leaves you where you went, instead of pulling you back when it answers. A shot restored from the archive comes back to the feed where it belongs. After deleting a record, focus lands somewhere sensible instead of at the top of the page. On a phone a toast no longer sits on the tab bar."
11735
+ }
11736
+ ]
11737
+ },
11738
+ {
11739
+ version: "0.11.1",
11740
+ date: "2026-09-18",
11741
+ sections: [
11742
+ {
11743
+ heading: "Learn",
11744
+ body: "The welcome, the guided first shot, First steps and Learn are paused for now while they are finished. Nothing of them opens by itself, and Help no longer lists them."
11745
+ },
11746
+ {
11747
+ heading: "Fixes",
11748
+ body: "With a panel in the top bar open, pressing another control in the bar opens its panel and keeps it open, instead of opening it and closing it again at once."
11749
+ }
11750
+ ]
11751
+ },
11707
11752
  {
11708
11753
  version: "0.11.0",
11709
11754
  date: "2026-09-18",
@@ -12962,7 +13007,7 @@ function registerDesktopRoutes(app, deps) {
12962
13007
  record: null
12963
13008
  };
12964
13009
  }
12965
- const { installDeps } = await import('./cli-GMRPOISO.js');
13010
+ const { installDeps } = await import('./cli-4A3QWRSC.js');
12966
13011
  const { desktopStatus } = await import('./install-VXJPLYWM.js');
12967
13012
  return desktopStatus(installDeps(runtime.entry));
12968
13013
  });
@@ -12970,7 +13015,7 @@ function registerDesktopRoutes(app, deps) {
12970
13015
  if (!runtime.entry) {
12971
13016
  return { ok: false, reason: "unsupported", message: "Desktop shortcuts are not available on this system yet." };
12972
13017
  }
12973
- const { addToDesktop } = await import('./cli-GMRPOISO.js');
13018
+ const { addToDesktop } = await import('./cli-4A3QWRSC.js');
12974
13019
  return addToDesktop(runtime.entry);
12975
13020
  });
12976
13021
  app.get("/api/desktop", async () => {
@@ -13078,7 +13123,12 @@ function buildServer(opts) {
13078
13123
  return { ...row, warnings, report };
13079
13124
  });
13080
13125
  app.put("/api/brands/:id", async (req, reply) => {
13081
- const json = req.body?.brand;
13126
+ const body = req.body ?? {};
13127
+ let json = body.brand;
13128
+ if (body.keepAssets === true && json && typeof json === "object") {
13129
+ const stored = core.store.getBrand(req.params.id);
13130
+ if (stored) json = withStoredAssets(json, stored.json);
13131
+ }
13082
13132
  const v = validateBrand(json);
13083
13133
  if (!v.valid) return reply.status(400).send({ error: "invalid .brand", details: v.errors });
13084
13134
  const row = core.store.updateBrand(req.params.id, json);
@@ -14460,6 +14510,16 @@ function buildServer(opts) {
14460
14510
  }
14461
14511
  return app;
14462
14512
  }
14513
+ var ASSET_COLLECTIONS = ["products", "scenes", "characters"];
14514
+ function withStoredAssets(sent, stored) {
14515
+ const out = { ...sent };
14516
+ const held = stored ?? {};
14517
+ for (const key of ASSET_COLLECTIONS) {
14518
+ if (key in held) out[key] = held[key];
14519
+ else delete out[key];
14520
+ }
14521
+ return out;
14522
+ }
14463
14523
 
14464
14524
  // src/serve.ts
14465
14525
  var PORT = Number(process.env.SCENRI_PORT || 4747);
@@ -14611,7 +14671,7 @@ async function run() {
14611
14671
  }
14612
14672
  }
14613
14673
  const ownEntry = fileURLToPath(import.meta.url);
14614
- const { addToDesktop, installDeps } = await import('./cli-GMRPOISO.js');
14674
+ const { addToDesktop, installDeps } = await import('./cli-4A3QWRSC.js');
14615
14675
  const { refreshLauncher } = await import('./refresh-5HBDLLLB.js');
14616
14676
  const { askOnTerminal, offerDesktop, shouldOfferDesktop } = await import('./offer-W6NJXMWT.js');
14617
14677
  const { launcherInstalled } = await import('./paths-6ZXRZUHY.js');
@@ -1,3 +1,3 @@
1
- export { ASPECT_TOLERANCE, BUDGET_EXHAUSTED, EDIT_REFERENCE_ROLE_DIRECTIVE, REFERENCE_ROLE_DIRECTIVE, SCHEMA_VERSION, STEM_MIN, SchemaTooNewError, SpendCapError, TRIGRAM_MIN, budgetSize, createCatalogStore, createCore, createStore, defaultHome, fold, ftsMatch, matchesQuery, ratioLabel, searchTerms, termMatches, uniqueProjectSlug, uniqueSetSlug, uniqueSlug } from './chunk-K6IELFL3.js';
2
- //# sourceMappingURL=src-TC4UQ7H6.js.map
3
- //# sourceMappingURL=src-TC4UQ7H6.js.map
1
+ export { ASPECT_TOLERANCE, BUDGET_EXHAUSTED, EDIT_REFERENCE_ROLE_DIRECTIVE, REFERENCE_ROLE_DIRECTIVE, SCHEMA_VERSION, STEM_MIN, SchemaTooNewError, SpendCapError, TRIGRAM_MIN, budgetSize, createCatalogStore, createCore, createStore, defaultHome, fold, ftsMatch, matchesQuery, ratioLabel, searchTerms, termMatches, uniqueProjectSlug, uniqueSetSlug, uniqueSlug } from './chunk-MHTA6O3A.js';
2
+ //# sourceMappingURL=src-INKSTJNC.js.map
3
+ //# sourceMappingURL=src-INKSTJNC.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scenri",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "author": "Tony Gorb <hello@scenri.co>",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",