scenri 0.12.0 → 0.13.1
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 +34 -0
- package/dist/serve.js +131 -16
- package/package.json +1 -1
- package/studio-dist/assets/first-shot-square-hTczF9bl.webp +0 -0
- package/studio-dist/assets/first-shot-wide-BSpJwjNx.webp +0 -0
- package/studio-dist/assets/{index-BIFWvZVh.css → index-BVDd0Egc.css} +1 -1
- package/studio-dist/assets/index-CVD83sBv.js +107 -0
- package/studio-dist/assets/presenter-square-BpqHv8Aq.webp +0 -0
- package/studio-dist/assets/presenter-wide-ecc4y7eJ.webp +0 -0
- package/studio-dist/assets/product-square-DPR77tr0.webp +0 -0
- package/studio-dist/assets/product-wide-DLSsO8IK.webp +0 -0
- package/studio-dist/assets/refine-square-CietHTPe.webp +0 -0
- package/studio-dist/assets/refine-wide-BM5sVq6w.webp +0 -0
- package/studio-dist/assets/reuse-square-CMLhjEwt.webp +0 -0
- package/studio-dist/assets/reuse-wide-DbbTeKsL.webp +0 -0
- package/studio-dist/assets/scene-square-CQBwEfXy.webp +0 -0
- package/studio-dist/assets/scene-wide-CJ9ENIrh.webp +0 -0
- package/studio-dist/index.html +2 -2
- package/studio-dist/assets/index-D-7D0wTu.js +0 -107
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.13.1](https://github.com/tonygorb/Scenri/compare/v0.13.0...v0.13.1) (2026-09-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **create:** the feed keeps the tile you were looking at when its columns change ([8908270](https://github.com/tonygorb/Scenri/commit/8908270c37d479659ba7793c0c4fc08a8dbfb430))
|
|
9
|
+
* **studio:** a lesson step pushed out of view by a resize comes back into view ([5f5fefb](https://github.com/tonygorb/Scenri/commit/5f5fefbbe9d98f7241f9d34b057d96fe8d65da92))
|
|
10
|
+
* **studio:** a lesson step written as the page reloads is not lost ([22d04ca](https://github.com/tonygorb/Scenri/commit/22d04ca8006295ab9421470394c0a4e1c64d42d4))
|
|
11
|
+
* **studio:** alerts above the tutor and dialogs, a feed that keeps its place, and three small repairs ([ca63798](https://github.com/tonygorb/Scenri/commit/ca63798df7d6d3db85cef641b977de05e4d8c4be))
|
|
12
|
+
* **studio:** alerts are drawn above the tutor and dialogs, and can be pressed there ([3c5e75c](https://github.com/tonygorb/Scenri/commit/3c5e75c8bf041575c06f80ac78b45f68e9679191))
|
|
13
|
+
* **studio:** the brief has a name a screen reader can say ([22c6b6a](https://github.com/tonygorb/Scenri/commit/22c6b6a66a696cb558d4989dd81d93d1838e86ea))
|
|
14
|
+
|
|
15
|
+
## [0.13.0](https://github.com/tonygorb/Scenri/compare/v0.12.0...v0.13.0) (2026-09-21)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* **studio:** rebuild transient alerts as a quiet toast stack ([7b44141](https://github.com/tonygorb/Scenri/commit/7b44141b57cc6748942d97bc6acc27a022260207))
|
|
21
|
+
* **studio:** turn first-use on, with a tutor that can go back ([df6b799](https://github.com/tonygorb/Scenri/commit/df6b799708d65cf31130233dabe2fd0ee1095a28))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* **cli:** an install that has made a shot is not offered the first-use welcome ([4dae292](https://github.com/tonygorb/Scenri/commit/4dae292f3e833180448b2310a81573b641408bcc))
|
|
27
|
+
* **create:** keep the feed still when a shot archives or the rail toggles ([b3276cd](https://github.com/tonygorb/Scenri/commit/b3276cd1f68315455e8b29d7dfdf13c768a0438a))
|
|
28
|
+
* **studio:** a lesson on its first step has not begun, and Learn cannot take the app down ([cf0540b](https://github.com/tonygorb/Scenri/commit/cf0540b1ecd1e8fbdde2e3d203538f9e13f46d8c))
|
|
29
|
+
* **studio:** a lesson owns its progress, and only the screen is shared ([8c58bdb](https://github.com/tonygorb/Scenri/commit/8c58bdbc06124d2f9fda0775a41f84fa156e10ea))
|
|
30
|
+
* **studio:** a library lesson keeps its button in sight on narrow screens ([93869f4](https://github.com/tonygorb/Scenri/commit/93869f4f1f1fa8791c15ff893ef8ac536deda721))
|
|
31
|
+
* **studio:** a step scrolled out of sight is not gone ([49ceccb](https://github.com/tonygorb/Scenri/commit/49ceccb8d7dc41daf8327ecefb8353f98ed94e70))
|
|
32
|
+
* **studio:** alerts stand above a docked composer instead of over its buttons ([b6756c7](https://github.com/tonygorb/Scenri/commit/b6756c79c63419ca2ed146fc2e3c6d868eeb6f79))
|
|
33
|
+
* **studio:** Learn gives the keyboard back to whichever control opened it ([e9c5945](https://github.com/tonygorb/Scenri/commit/e9c5945d60e6ceb55704da1130fe23a988c6044f))
|
|
34
|
+
* **studio:** no empty alert on every page, and a held toast stays held ([3305f75](https://github.com/tonygorb/Scenri/commit/3305f7542fb38f604c9b4087c59aa7cfe0d4cf6b))
|
|
35
|
+
* **studio:** refining teaches how to find a shot, instead of picking one for you ([228938f](https://github.com/tonygorb/Scenri/commit/228938f2440e17b48d9bab836e7c4aeeb0a74cef))
|
|
36
|
+
|
|
3
37
|
## [0.12.0](https://github.com/tonygorb/Scenri/compare/v0.11.1...v0.12.0) (2026-09-20)
|
|
4
38
|
|
|
5
39
|
|
package/dist/serve.js
CHANGED
|
@@ -11716,6 +11716,43 @@ function registerImageRoutes(app, deps) {
|
|
|
11716
11716
|
|
|
11717
11717
|
// src/release/notes.data.ts
|
|
11718
11718
|
var RELEASES = [
|
|
11719
|
+
{
|
|
11720
|
+
version: "0.13.1",
|
|
11721
|
+
date: "2026-09-22",
|
|
11722
|
+
sections: [
|
|
11723
|
+
{
|
|
11724
|
+
heading: "Alerts",
|
|
11725
|
+
body: "An alert that arrives during a lesson now shows above it, and an alert over an open dialog can be closed or used without closing the dialog, popover or picker beneath it."
|
|
11726
|
+
},
|
|
11727
|
+
{
|
|
11728
|
+
heading: "Create",
|
|
11729
|
+
body: "Closing the side panel or resizing the window while scrolled keeps the shot you were looking at in place. The brief field now has a name that screen readers announce."
|
|
11730
|
+
},
|
|
11731
|
+
{
|
|
11732
|
+
heading: "Learn",
|
|
11733
|
+
body: "A lesson step pushed out of view by resizing the window comes back into view, and a step reached just as the page reloads is no longer lost."
|
|
11734
|
+
}
|
|
11735
|
+
]
|
|
11736
|
+
},
|
|
11737
|
+
{
|
|
11738
|
+
version: "0.13.0",
|
|
11739
|
+
date: "2026-09-21",
|
|
11740
|
+
title: "Learn teaches Scenri one lesson at a time, alerts are quieter, and the Create feed holds still.",
|
|
11741
|
+
sections: [
|
|
11742
|
+
{
|
|
11743
|
+
heading: "Learn",
|
|
11744
|
+
body: "Learn sits in the top bar, or under Help on a smaller screen, and teaches Scenri in six short lessons: your first shot, adding a product, a presenter, a scene, using a product again, and refining a shot. Each lesson keeps its own place, so you can set one down, take up another and come back to it. A new install is offered a guided first shot, and the guide can step back when you change your mind."
|
|
11745
|
+
},
|
|
11746
|
+
{
|
|
11747
|
+
heading: "Alerts",
|
|
11748
|
+
body: "Alerts are smaller and stack at the bottom of the screen, above the composer rather than over it. The same event twice shares one card, an error stays until you close it, and an alert holds still while you point at it or tab into it."
|
|
11749
|
+
},
|
|
11750
|
+
{
|
|
11751
|
+
heading: "Create",
|
|
11752
|
+
body: "Archiving a shot no longer makes the pictures around it blink, and opening or closing the side panel rearranges the feed in one step."
|
|
11753
|
+
}
|
|
11754
|
+
]
|
|
11755
|
+
},
|
|
11719
11756
|
{
|
|
11720
11757
|
version: "0.12.0",
|
|
11721
11758
|
date: "2026-09-20",
|
|
@@ -12835,14 +12872,24 @@ function registerSystemRoutes(app, deps) {
|
|
|
12835
12872
|
}
|
|
12836
12873
|
|
|
12837
12874
|
// src/routes/guide.ts
|
|
12838
|
-
var TASKS = ["first-shot", "refine", "product", "presenter", "scene"];
|
|
12875
|
+
var TASKS = ["first-shot", "refine", "product", "presenter", "scene", "reuse"];
|
|
12839
12876
|
var MILESTONES = ["shot", "refine", "product", "presenter", "scene"];
|
|
12840
12877
|
var KEY = "guide";
|
|
12841
12878
|
var isTask = (t) => TASKS.includes(t);
|
|
12842
12879
|
var isMilestone = (m) => MILESTONES.includes(m);
|
|
12843
12880
|
var num2 = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? n : 0;
|
|
12844
12881
|
function blank(eligible) {
|
|
12845
|
-
return {
|
|
12882
|
+
return {
|
|
12883
|
+
v: 2,
|
|
12884
|
+
eligible,
|
|
12885
|
+
welcome: null,
|
|
12886
|
+
hidden: null,
|
|
12887
|
+
done: {},
|
|
12888
|
+
lessons: {},
|
|
12889
|
+
dismissed: [],
|
|
12890
|
+
active: null,
|
|
12891
|
+
progress: {}
|
|
12892
|
+
};
|
|
12846
12893
|
}
|
|
12847
12894
|
function parse2(raw) {
|
|
12848
12895
|
if (!raw) return null;
|
|
@@ -12854,9 +12901,47 @@ function parse2(raw) {
|
|
|
12854
12901
|
if (j.done && typeof j.done === "object") {
|
|
12855
12902
|
for (const [k, at] of Object.entries(j.done)) if (isMilestone(k) && typeof at === "string") done[k] = at;
|
|
12856
12903
|
}
|
|
12904
|
+
const lessons = {};
|
|
12905
|
+
if (j.lessons && typeof j.lessons === "object") {
|
|
12906
|
+
for (const [k, at] of Object.entries(j.lessons)) if (isTask(k) && typeof at === "string") lessons[k] = at;
|
|
12907
|
+
}
|
|
12908
|
+
const asTask = (a2) => {
|
|
12909
|
+
const b2 = a2?.baseline;
|
|
12910
|
+
return a2 && isTask(a2.task) && typeof a2.brandId === "string" && typeof a2.since === "string" ? {
|
|
12911
|
+
task: a2.task,
|
|
12912
|
+
brandId: a2.brandId,
|
|
12913
|
+
since: a2.since,
|
|
12914
|
+
baseline: { products: num2(b2?.products), presenters: num2(b2?.presenters), scenes: num2(b2?.scenes) },
|
|
12915
|
+
...a2.paused === true ? { paused: true } : {}
|
|
12916
|
+
} : null;
|
|
12917
|
+
};
|
|
12918
|
+
const asProgress = (v) => {
|
|
12919
|
+
const p = v;
|
|
12920
|
+
const b2 = p?.baseline;
|
|
12921
|
+
return p && typeof p.brandId === "string" && typeof p.since === "string" ? {
|
|
12922
|
+
brandId: p.brandId,
|
|
12923
|
+
since: p.since,
|
|
12924
|
+
baseline: { products: num2(b2?.products), presenters: num2(b2?.presenters), scenes: num2(b2?.scenes) },
|
|
12925
|
+
reached: Array.isArray(p.reached) ? p.reached.filter((m) => typeof m === "string") : [],
|
|
12926
|
+
...p.paused === true ? { paused: true } : {}
|
|
12927
|
+
} : null;
|
|
12928
|
+
};
|
|
12929
|
+
const progress = {};
|
|
12930
|
+
if (j.progress && typeof j.progress === "object") {
|
|
12931
|
+
for (const [k, v] of Object.entries(j.progress)) {
|
|
12932
|
+
const p = asProgress(v);
|
|
12933
|
+
if (isTask(k) && p) progress[k] = p;
|
|
12934
|
+
}
|
|
12935
|
+
}
|
|
12936
|
+
if (j.parked && typeof j.parked === "object") {
|
|
12937
|
+
for (const [k, v] of Object.entries(j.parked)) {
|
|
12938
|
+
const t = asTask(v);
|
|
12939
|
+
if (isTask(k) && t && !progress[k]) progress[k] = { ...t, reached: [], paused: true };
|
|
12940
|
+
}
|
|
12941
|
+
}
|
|
12857
12942
|
const a = j.active;
|
|
12858
12943
|
const b = a?.baseline;
|
|
12859
|
-
const
|
|
12944
|
+
const held = a && isTask(a.task) && typeof a.brandId === "string" && typeof a.since === "string" ? {
|
|
12860
12945
|
task: a.task,
|
|
12861
12946
|
brandId: a.brandId,
|
|
12862
12947
|
since: a.since,
|
|
@@ -12869,8 +12954,11 @@ function parse2(raw) {
|
|
|
12869
12954
|
welcome: j.welcome === "taken" || j.welcome === "declined" ? j.welcome : null,
|
|
12870
12955
|
hidden: typeof j.hidden === "boolean" ? j.hidden : null,
|
|
12871
12956
|
done,
|
|
12957
|
+
lessons,
|
|
12872
12958
|
dismissed: Array.isArray(j.dismissed) ? j.dismissed.filter(isTask) : [],
|
|
12873
|
-
|
|
12959
|
+
// an old record's one task in hand is that lesson's progress now
|
|
12960
|
+
active: typeof j.active === "string" && isTask(j.active) ? j.active : held?.task ?? null,
|
|
12961
|
+
progress: held && !progress[held.task] ? { ...progress, [held.task]: { ...held, reached: [] } } : progress
|
|
12874
12962
|
};
|
|
12875
12963
|
} catch {
|
|
12876
12964
|
return null;
|
|
@@ -12929,21 +13017,33 @@ function readGuide(core, env) {
|
|
|
12929
13017
|
changed = true;
|
|
12930
13018
|
}
|
|
12931
13019
|
}
|
|
12932
|
-
|
|
12933
|
-
|
|
13020
|
+
for (const [t, p] of Object.entries(r.progress)) {
|
|
13021
|
+
if (core.store.getBrand(p.brandId)) continue;
|
|
13022
|
+
delete r.progress[t];
|
|
13023
|
+
if (r.active === t) r.active = null;
|
|
12934
13024
|
changed = true;
|
|
12935
13025
|
}
|
|
12936
13026
|
if (changed) write(core.store, r);
|
|
12937
|
-
const
|
|
12938
|
-
const
|
|
12939
|
-
|
|
13027
|
+
const on = r.active ? r.progress[r.active] : null;
|
|
13028
|
+
const a = r.active && on ? {
|
|
13029
|
+
task: r.active,
|
|
13030
|
+
brandId: on.brandId,
|
|
13031
|
+
since: on.since,
|
|
13032
|
+
baseline: on.baseline,
|
|
13033
|
+
...on.paused ? { paused: true } : {}
|
|
13034
|
+
} : null;
|
|
13035
|
+
const nodeKind = a?.task === "first-shot" || a?.task === "reuse" ? "generation" : a?.task === "refine" ? "edit" : null;
|
|
13036
|
+
const settled = r.welcome === null && !!r.done.shot;
|
|
13037
|
+
const eligible = r.eligible && !settled && env.SCENRI_NO_GUIDE !== "1";
|
|
12940
13038
|
return {
|
|
12941
13039
|
eligible,
|
|
12942
13040
|
welcome: r.welcome,
|
|
12943
13041
|
hidden: r.hidden ?? !eligible,
|
|
12944
13042
|
done: r.done,
|
|
13043
|
+
lessons: r.lessons,
|
|
12945
13044
|
dismissed: r.dismissed,
|
|
12946
13045
|
active: a,
|
|
13046
|
+
progress: r.progress,
|
|
12947
13047
|
activeNodes: a && nodeKind ? core.store.nodesSince(a.brandId, nodeKind, a.since) : [],
|
|
12948
13048
|
activeDraftId: a?.task === "presenter" ? core.store.presenterDraftSince(a.brandId, a.since) : null,
|
|
12949
13049
|
counts: a ? countsOf(core, a.brandId) : null
|
|
@@ -12961,19 +13061,34 @@ function applyIntent(core, body) {
|
|
|
12961
13061
|
if (typeof s.brandId !== "string") return "brandId required";
|
|
12962
13062
|
const baseline = countsOf(core, s.brandId);
|
|
12963
13063
|
if (!baseline) return "brand not found";
|
|
12964
|
-
const
|
|
12965
|
-
if (
|
|
12966
|
-
|
|
12967
|
-
|
|
12968
|
-
|
|
13064
|
+
const guiding = r.active ? r.progress[r.active] : null;
|
|
13065
|
+
if (r.active && r.active !== s.task && guiding) r.progress[r.active] = { ...guiding, paused: true };
|
|
13066
|
+
const mine = r.progress[s.task];
|
|
13067
|
+
if (mine && mine.brandId === s.brandId) {
|
|
13068
|
+
const { paused: _, ...going } = mine;
|
|
13069
|
+
r.progress[s.task] = going;
|
|
13070
|
+
} else r.progress[s.task] = { brandId: s.brandId, since: core.store.now(), baseline, reached: [] };
|
|
13071
|
+
r.active = s.task;
|
|
12969
13072
|
r.dismissed = r.dismissed.filter((t) => t !== s.task);
|
|
12970
13073
|
} else if ("finish" in b) {
|
|
12971
13074
|
if (!isTask(b.finish)) return "unknown task";
|
|
12972
|
-
if (r.
|
|
13075
|
+
if (!r.lessons[b.finish]) r.lessons[b.finish] = (/* @__PURE__ */ new Date()).toISOString();
|
|
13076
|
+
if (r.active === b.finish) r.active = null;
|
|
13077
|
+
delete r.progress[b.finish];
|
|
12973
13078
|
} else if ("dismiss" in b) {
|
|
12974
13079
|
if (!isTask(b.dismiss)) return "unknown task";
|
|
12975
13080
|
if (!r.dismissed.includes(b.dismiss)) r.dismissed.push(b.dismiss);
|
|
12976
|
-
|
|
13081
|
+
const shut = r.progress[b.dismiss];
|
|
13082
|
+
if (shut) r.progress[b.dismiss] = { ...shut, paused: true };
|
|
13083
|
+
if (r.active === b.dismiss) r.active = null;
|
|
13084
|
+
} else if ("reached" in b) {
|
|
13085
|
+
const w = b.reached ?? {};
|
|
13086
|
+
if (!isTask(w.task)) return "unknown task";
|
|
13087
|
+
if (typeof w.moment !== "string" || !w.moment) return "moment required";
|
|
13088
|
+
const p = r.progress[w.task];
|
|
13089
|
+
if (!p) return null;
|
|
13090
|
+
if (p.reached.includes(w.moment)) return null;
|
|
13091
|
+
r.progress[w.task] = { ...p, reached: [...p.reached, w.moment].slice(-24) };
|
|
12977
13092
|
} else if ("hidden" in b) {
|
|
12978
13093
|
if (typeof b.hidden !== "boolean") return "hidden is true or false";
|
|
12979
13094
|
r.hidden = b.hidden;
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|