scenri 0.14.0 → 0.16.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.
Files changed (111) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +3 -3
  3. package/dist/{chunk-MHTA6O3A.js → chunk-LMXHCXUC.js} +39 -9
  4. package/dist/{cli-4A3QWRSC.js → cli-NXOTZTBT.js} +3 -3
  5. package/dist/index.js +2 -2
  6. package/dist/serve.js +5509 -3042
  7. package/dist/{src-INKSTJNC.js → src-XRZP3ZB5.js} +3 -3
  8. package/package.json +1 -1
  9. package/studio-dist/assets/index-Cd95h4R3.js +106 -0
  10. package/studio-dist/assets/{index-DWV0lLzi.css → index-hgq6oQCZ.css} +1 -1
  11. package/studio-dist/assets/scene-citrus-light-1-Wme0KapL.webp +0 -0
  12. package/studio-dist/assets/scene-citrus-light-2-yTOgxwBC.webp +0 -0
  13. package/studio-dist/assets/scene-citrus-light-3-Cdm9IoIB.webp +0 -0
  14. package/studio-dist/assets/scene-citrus-light-4-DCtAaq1r.webp +0 -0
  15. package/studio-dist/assets/scene-citrus-signature-1-Cml3IQXs.webp +0 -0
  16. package/studio-dist/assets/scene-citrus-signature-2-KzaKaiGG.webp +0 -0
  17. package/studio-dist/assets/scene-citrus-signature-3-CXTnZSxD.webp +0 -0
  18. package/studio-dist/assets/scene-citrus-signature-4-B096kyLn.webp +0 -0
  19. package/studio-dist/assets/scene-citrus-surface-1-_EhsQ6GX.webp +0 -0
  20. package/studio-dist/assets/scene-citrus-surface-2-jj2bR67l.webp +0 -0
  21. package/studio-dist/assets/scene-citrus-surface-3-CqmDcvcl.webp +0 -0
  22. package/studio-dist/assets/scene-citrus-surface-4-CVx7xz1x.webp +0 -0
  23. package/studio-dist/assets/scene-colour-light-2-CEzTzOPa.webp +0 -0
  24. package/studio-dist/assets/scene-colour-light-4-BWyfdaod.webp +0 -0
  25. package/studio-dist/assets/scene-colour-signature-1-S-vfEXj-.webp +0 -0
  26. package/studio-dist/assets/scene-colour-signature-2-DzMl3jIo.webp +0 -0
  27. package/studio-dist/assets/scene-colour-signature-3-BRXPef3C.webp +0 -0
  28. package/studio-dist/assets/scene-colour-signature-4-D8Mwy86u.webp +0 -0
  29. package/studio-dist/assets/scene-colour-surface-2-8xE9rtHz.webp +0 -0
  30. package/studio-dist/assets/scene-colour-surface-3-o3tD2BcW.webp +0 -0
  31. package/studio-dist/assets/scene-colour-surface-4-Be-8wcD7.webp +0 -0
  32. package/studio-dist/assets/scene-dark-light-1-DNyik_mP.webp +0 -0
  33. package/studio-dist/assets/scene-dark-light-2-D3lbZbkz.webp +0 -0
  34. package/studio-dist/assets/scene-dark-light-3-D8bct498.webp +0 -0
  35. package/studio-dist/assets/scene-dark-signature-1-Uyxq7eTB.webp +0 -0
  36. package/studio-dist/assets/scene-dark-signature-2-CgU_91ec.webp +0 -0
  37. package/studio-dist/assets/scene-dark-signature-3-BFmyRVQE.webp +0 -0
  38. package/studio-dist/assets/scene-dark-signature-4-B-pqY09v.webp +0 -0
  39. package/studio-dist/assets/scene-dark-surface-1-BOty0Ksz.webp +0 -0
  40. package/studio-dist/assets/scene-dark-surface-2-Csn7cpFS.webp +0 -0
  41. package/studio-dist/assets/scene-dark-surface-3-B3AUiSC2.webp +0 -0
  42. package/studio-dist/assets/scene-dark-surface-4-REXRBZcg.webp +0 -0
  43. package/studio-dist/assets/scene-light-2-B84yG8HI.webp +0 -0
  44. package/studio-dist/assets/scene-light-6-dWmIaxxA.webp +0 -0
  45. package/studio-dist/assets/scene-light-7-Cf_DQSe0.webp +0 -0
  46. package/studio-dist/assets/scene-linen-light-1-BGO7NJ2i.webp +0 -0
  47. package/studio-dist/assets/scene-linen-light-2-Nj4uZEQb.webp +0 -0
  48. package/studio-dist/assets/scene-linen-light-3-CQLLstOF.webp +0 -0
  49. package/studio-dist/assets/scene-linen-light-4-Ca0fvqvc.webp +0 -0
  50. package/studio-dist/assets/scene-linen-signature-1-BZSGTVTo.webp +0 -0
  51. package/studio-dist/assets/scene-linen-signature-2-CHn_V3kh.webp +0 -0
  52. package/studio-dist/assets/scene-linen-signature-3-CgDnLeo2.webp +0 -0
  53. package/studio-dist/assets/scene-linen-signature-4-D-J72W_0.webp +0 -0
  54. package/studio-dist/assets/scene-linen-surface-1-D2wLSLtm.webp +0 -0
  55. package/studio-dist/assets/scene-linen-surface-2-BtytXyl5.webp +0 -0
  56. package/studio-dist/assets/scene-linen-surface-3-wZabdFH0.webp +0 -0
  57. package/studio-dist/assets/scene-linen-surface-4-BNOUx8lY.webp +0 -0
  58. package/studio-dist/assets/scene-plaster-light-1-DuUR0x-f.webp +0 -0
  59. package/studio-dist/assets/scene-plaster-light-2-CTV4xOer.webp +0 -0
  60. package/studio-dist/assets/scene-plaster-light-3-DcHxylwu.webp +0 -0
  61. package/studio-dist/assets/scene-plaster-signature-1-GtNrVfYr.webp +0 -0
  62. package/studio-dist/assets/scene-plaster-signature-2-D9iLXrEC.webp +0 -0
  63. package/studio-dist/assets/scene-plaster-signature-3-8VdPMd3L.webp +0 -0
  64. package/studio-dist/assets/scene-plaster-signature-4-e0mDabT1.webp +0 -0
  65. package/studio-dist/assets/scene-plaster-surface-2-X5fj16GI.webp +0 -0
  66. package/studio-dist/assets/scene-plaster-surface-3-Bxtg39zJ.webp +0 -0
  67. package/studio-dist/assets/scene-plaster-surface-4-fJXikVlC.webp +0 -0
  68. package/studio-dist/assets/scene-stone-light-1-Ck-_NhFQ.webp +0 -0
  69. package/studio-dist/assets/scene-stone-light-2-CjBNlt4T.webp +0 -0
  70. package/studio-dist/assets/scene-stone-light-3-BP2OkaJp.webp +0 -0
  71. package/studio-dist/assets/scene-stone-light-4-CHqBjior.webp +0 -0
  72. package/studio-dist/assets/scene-stone-signature-1-DX1azXUT.webp +0 -0
  73. package/studio-dist/assets/scene-stone-signature-2-D88S1ZTv.webp +0 -0
  74. package/studio-dist/assets/scene-stone-signature-3-lrOljf4B.webp +0 -0
  75. package/studio-dist/assets/scene-stone-signature-4-l_vDAftm.webp +0 -0
  76. package/studio-dist/assets/scene-stone-surface-1-WXRrETPZ.webp +0 -0
  77. package/studio-dist/assets/scene-stone-surface-2-TXh8wgcH.webp +0 -0
  78. package/studio-dist/assets/scene-stone-surface-3-Cz_wRxxO.webp +0 -0
  79. package/studio-dist/assets/scene-stone-surface-4-I0MbY8K9.webp +0 -0
  80. package/studio-dist/assets/scene-volcanic-light-1-BSuk4LH5.webp +0 -0
  81. package/studio-dist/assets/scene-volcanic-light-2-CodmHsaU.webp +0 -0
  82. package/studio-dist/assets/scene-volcanic-light-3-DYW5TyYn.webp +0 -0
  83. package/studio-dist/assets/scene-volcanic-light-4-B_4FgKXC.webp +0 -0
  84. package/studio-dist/assets/scene-volcanic-signature-1-Qk4sUfIe.webp +0 -0
  85. package/studio-dist/assets/scene-volcanic-signature-2-CD-IFmSZ.webp +0 -0
  86. package/studio-dist/assets/scene-volcanic-signature-3-BHXZ-3OB.webp +0 -0
  87. package/studio-dist/assets/scene-volcanic-signature-4-DHeJHr5p.webp +0 -0
  88. package/studio-dist/assets/scene-volcanic-surface-1-DURXnZ3G.webp +0 -0
  89. package/studio-dist/assets/scene-volcanic-surface-2-aXSRFhs9.webp +0 -0
  90. package/studio-dist/assets/scene-volcanic-surface-3-3xTz8ity.webp +0 -0
  91. package/studio-dist/assets/scene-volcanic-surface-4-D-yZi4rg.webp +0 -0
  92. package/studio-dist/assets/scene-water-light-1-4dxiIQDI.webp +0 -0
  93. package/studio-dist/assets/scene-water-light-2-DBrtCynY.webp +0 -0
  94. package/studio-dist/assets/scene-water-light-3-Bn1XOxoW.webp +0 -0
  95. package/studio-dist/assets/scene-water-light-4-C8RMoKMt.webp +0 -0
  96. package/studio-dist/assets/scene-water-signature-1-DmzDkEsw.webp +0 -0
  97. package/studio-dist/assets/scene-water-signature-2-D3nTe3bP.webp +0 -0
  98. package/studio-dist/assets/scene-water-signature-3-DL6R7xH3.webp +0 -0
  99. package/studio-dist/assets/scene-water-signature-4-BTHnaJIJ.webp +0 -0
  100. package/studio-dist/assets/scene-water-surface-1-BboH0OO0.webp +0 -0
  101. package/studio-dist/assets/scene-water-surface-2-DrnvFxqP.webp +0 -0
  102. package/studio-dist/assets/scene-water-surface-3-B8K_8sJT.webp +0 -0
  103. package/studio-dist/assets/scene-water-surface-4-mS0WpsuW.webp +0 -0
  104. package/studio-dist/assets/scene-world-1-DTbMGqH6.webp +0 -0
  105. package/studio-dist/assets/scene-world-2-DKT-i3Wq.webp +0 -0
  106. package/studio-dist/assets/scene-world-4-BF8szsQ8.webp +0 -0
  107. package/studio-dist/assets/scene-world-5-D-z_9xds.webp +0 -0
  108. package/studio-dist/assets/scene-world-6-Te5EnrDc.webp +0 -0
  109. package/studio-dist/assets/scene-world-8-EqxlFjcK.webp +0 -0
  110. package/studio-dist/index.html +2 -2
  111. package/studio-dist/assets/index-CHLkXhZz.js +0 -107
package/CHANGELOG.md CHANGED
@@ -1,5 +1,75 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.16.0](https://github.com/tonygorb/Scenri/compare/v0.15.1...v0.16.0) (2026-09-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * a real code screen for phones, and a six-digit code ([01f2fed](https://github.com/tonygorb/Scenri/commit/01f2fedc14f7cfb4cba205d1ae1ea51e8c213f78))
9
+ * allow Scenri through the firewall with one press ([8ca9b5f](https://github.com/tonygorb/Scenri/commit/8ca9b5f64ce49894c00490439f7d00b26a903e0f))
10
+ * **cli:** open Scenri on a phone on the same Wi-Fi ([5d25779](https://github.com/tonygorb/Scenri/commit/5d25779d60b14751f85df8d88cf1ea993c6b79bd))
11
+ * open Scenri on your phone, and Settings one idea per page ([4a6601d](https://github.com/tonygorb/Scenri/commit/4a6601d0608d97137e34f2973d6c1eb2b6e5e409))
12
+ * **studio:** open on your phone, in Settings, General ([9701956](https://github.com/tonygorb/Scenri/commit/9701956625023141d2d9b7257ddd978b018f0f8e))
13
+ * **studio:** Phone and tablet gets its own page in Settings ([6f0ff64](https://github.com/tonygorb/Scenri/commit/6f0ff64353109e6dc9adf76b6638564cc3e5f459))
14
+ * **studio:** Settings, one idea per page ([34e6937](https://github.com/tonygorb/Scenri/commit/34e69374ef3f7db82cf4466299de471f3bf84c77))
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * **cli:** the code page's privacy note is one centred line ([0f33bbe](https://github.com/tonygorb/Scenri/commit/0f33bbe79d31be5cc8e3978ade9de3eb5bbd3eb7))
20
+ * harden the phone code, and settle the review's findings ([ee6dd1d](https://github.com/tonygorb/Scenri/commit/ee6dd1d3226f890abc1d776de092d9d22952893d))
21
+ * plain English on the code page and the phone settings ([f7355d5](https://github.com/tonygorb/Scenri/commit/f7355d5f37b1e239d64d9ccc6cc71b0eb43a68a3))
22
+
23
+ ## [0.15.1](https://github.com/tonygorb/Scenri/compare/v0.15.0...v0.15.1) (2026-09-23)
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * **publish:** make room for the scene cards under the package size line ([171ce2a](https://github.com/tonygorb/Scenri/commit/171ce2aa4ae40573f8b754193b2fec74338c5808))
29
+ * **publish:** make room for the scene cards under the package size line ([baea05f](https://github.com/tonygorb/Scenri/commit/baea05f0b09d820f105d9d47e16722deb8a089e5))
30
+
31
+ ## [0.15.0](https://github.com/tonygorb/Scenri/compare/v0.14.0...v0.15.0) (2026-09-23)
32
+
33
+
34
+ ### Features
35
+
36
+ * **scene:** a shot is read for its place alone ([26ba5ef](https://github.com/tonygorb/Scenri/commit/26ba5ef93615d7c3d23b94d189e60cc1e308cc4a))
37
+ * **scene:** Add pictures means add pictures; a scene can start from one of your shots ([b026ab6](https://github.com/tonygorb/Scenri/commit/b026ab66fce5a1a876f082efb4f2a1150a8254c5))
38
+ * **scenes:** a scene studio conversation ([e1fa96b](https://github.com/tonygorb/Scenri/commit/e1fa96bb1b2d2ba7a79018cce3e3c4da7504d82e))
39
+ * **scenes:** every world's surface, light and idea rows are pictures ([373650c](https://github.com/tonygorb/Scenri/commit/373650cb37da6045ade68b0a014e2f7109848eaa))
40
+ * **scenes:** Sunlit stone's surface, light and idea rows are pictures (pilot) ([ea73eee](https://github.com/tonygorb/Scenri/commit/ea73eee1b98f57cad60082aecfc973f5e729156e))
41
+ * **scenes:** the questions after the world are the world's own ([d59e317](https://github.com/tonygorb/Scenri/commit/d59e3177bb853554c22d1aca4040f2cdc3630950))
42
+ * **studio:** creation conversations show four cards in a row instead of six ([2cca0bb](https://github.com/tonygorb/Scenri/commit/2cca0bbb61b0b79808fcc36b29fb026d8a94413c))
43
+
44
+
45
+ ### Bug Fixes
46
+
47
+ * **codex:** a prompt that ends in a full stop is not given a second one ([816047d](https://github.com/tonygorb/Scenri/commit/816047dadaa8c9a54f2a9d771d77821b3d7c2be1))
48
+ * **conversation:** a question held while an earlier answer changes is live again after ([df7f4c4](https://github.com/tonygorb/Scenri/commit/df7f4c4c7f79efdcf0831572184563e4b21c47ae))
49
+ * **create:** a scene's own words never silence its camera ([f60a5b3](https://github.com/tonygorb/Scenri/commit/f60a5b30d5fc256b5ec562dec19819730f049725))
50
+ * **create:** only a figure's treatment sends a scene's plate with a shot ([4201d06](https://github.com/tonygorb/Scenri/commit/4201d065e2f728d1a238b473c5db6d298bbd6832))
51
+ * **feed:** a search narrows from its first letter ([03cf16c](https://github.com/tonygorb/Scenri/commit/03cf16c0030b95b16e5018f3dfc5a71ea49fb9e0))
52
+ * **feed:** a search reads what people wrote, and its first letter narrows ([059f8f3](https://github.com/tonygorb/Scenri/commit/059f8f3c7bd21a7fc968fe033b2bd9a45ae60cb6))
53
+ * **feed:** a word starts after a space or punctuation, in any script ([257c6b7](https://github.com/tonygorb/Scenri/commit/257c6b76a207658bc7703d3b12cd5ec49767c780))
54
+ * **scene:** a saved scene keeps the figure line it was read with ([9ecf373](https://github.com/tonygorb/Scenri/commit/9ecf3735d0debf0a801f66ed2635ca4bceebfdd2))
55
+ * **scene:** a scene's first picture draws only its own words and nothing lifted from the references ([54184e4](https://github.com/tonygorb/Scenri/commit/54184e4e981dc4ad515e05568145ce250e389446))
56
+ * **scene:** a scene's picture is drawn from its words, never from its references ([91a6dd2](https://github.com/tonygorb/Scenri/commit/91a6dd2d5addb0f35bcd5846f5e5baebe2365e37))
57
+ * **scene:** a shot card is named by the start of its prompt, not all of it ([201882c](https://github.com/tonygorb/Scenri/commit/201882c15aab8753eb7d6607dbe0af440138300e))
58
+ * **scenes:** every scene card redrawn as one physically real photograph ([a785b48](https://github.com/tonygorb/Scenri/commit/a785b489f449f68627e386965e50834eb28e1806))
59
+ * **scenes:** saving a scene draws nothing, and the set is a counted press ([5251ca2](https://github.com/tonygorb/Scenri/commit/5251ca217d66c8342331ff62c8c63ce05c88838a))
60
+ * **scenes:** the draw-in-use button wears the page's ghost pill, not a fill that reads as disabled ([959b72c](https://github.com/tonygorb/Scenri/commit/959b72c5873e551cab3322c937c2dcb16cd09e59))
61
+ * **scenes:** the server no longer cuts a guided direction, and every one asks for an original arrangement ([e85b63c](https://github.com/tonygorb/Scenri/commit/e85b63ca8a65aa76702f07d5144aceb42957d1ff))
62
+ * **scene:** the reader gives a colour its strength, not just its name ([e1103f6](https://github.com/tonygorb/Scenri/commit/e1103f61acbce5078a208e9b0687409c213edfbf))
63
+ * **scene:** the reader keeps lettering and synthesis, and drops the advertisement ([571f494](https://github.com/tonygorb/Scenri/commit/571f4949f2f71c36f12e55280b208e10f0c08ab4))
64
+ * **scene:** the shot picker holds still, speaks, and lets Escape clear it ([16fb509](https://github.com/tonygorb/Scenri/commit/16fb5094f41bbcb5a9d89f94ad7c917232851ecb))
65
+ * **shots:** a glossy product is lit and framed as a campaign hero ([69ef00b](https://github.com/tonygorb/Scenri/commit/69ef00b912651ee367d9a42cada5ff170ca0dd0a))
66
+ * **shots:** a glossy product takes the set's light, its screen off, shown once ([ac19aec](https://github.com/tonygorb/Scenri/commit/ac19aec96f1d534a0db04290fa5dc3c6aa4f130a))
67
+ * **shots:** a phone design on a wide screen becomes the desktop app, and a refine can swap the screen ([d20b5e9](https://github.com/tonygorb/Scenri/commit/d20b5e96f21ee19980bcd65449fac70af83c93f6))
68
+ * **shots:** a picture means what the words beside it say, and can be a device's screen ([ff03a32](https://github.com/tonygorb/Scenri/commit/ff03a320ad82d4d0891fe0bc82d46c0264bd1aac))
69
+ * **shots:** a product alone is staged as a campaign image, held the way its world holds things ([7e12b56](https://github.com/tonygorb/Scenri/commit/7e12b56b52c5aaf5c3264890a37722d707bf4981))
70
+ * **studio:** a picked card says it is picked, and a row respects reduced motion ([9663c4e](https://github.com/tonygorb/Scenri/commit/9663c4e3843ec5d617a52ad13d4d2bf67ae10921))
71
+ * **studio:** a shot search not read yet never says it found nothing ([bddc624](https://github.com/tonygorb/Scenri/commit/bddc6241facf1b54a02be498ea8bd46d5482624a))
72
+
3
73
  ## [0.14.0](https://github.com/tonygorb/Scenri/compare/v0.13.2...v0.14.0) (2026-09-22)
4
74
 
5
75
 
package/README.md CHANGED
@@ -35,7 +35,7 @@ Most AI image tools give you a prompt box and a slot machine. Scenri gives you t
35
35
 
36
36
  Every shot keeps its lineage. Branch an edit from any shot, put the two side by side, and the drift heatmap shows you exactly where the model changed your product when it should not have.
37
37
 
38
- It runs as a local server on `127.0.0.1`. Your brands, your images, and your keys stay on your disk.
38
+ It runs as a local server on your own computer. Your brands, your images, and your keys stay on your disk. To use it from a phone or tablet on the same Wi-Fi, open Settings, Local access, and scan the QR code.
39
39
 
40
40
  ## What a brief produces
41
41
 
@@ -126,7 +126,7 @@ Your Codex session is yours: Scenri runs the official `codex` commands on your m
126
126
  |---|---|---|
127
127
  | `SCENRI_HOME` | `~/.scenri` | where brands, images, and keys live |
128
128
  | `SCENRI_PORT` | `4747` | port |
129
- | `SCENRI_HOST` | `127.0.0.1` | this machine only, see the note below |
129
+ | `SCENRI_HOST` | `127.0.0.1` | the studio's own listener; `127.0.0.1` given explicitly also turns off phone access, see the note below |
130
130
  | `SCENRI_NO_OPEN` | unset | set to `1` to skip opening a browser |
131
131
  | `SCENRI_NO_UPDATE_CHECK` | unset | set to `1` to never ask npm for the latest version |
132
132
  | `SCENRI_REGISTRY` | npmjs | registry for the update check and downloads (mirrors, forks, airgaps) |
@@ -135,7 +135,7 @@ Your Codex session is yours: Scenri runs the official `codex` commands on your m
135
135
 
136
136
  `OPENROUTER_API_KEY`, `REPLICATE_API_TOKEN` and `FAL_KEY` are read from the environment as an alternative to entering them in Settings.
137
137
 
138
- **Before you change `SCENRI_HOST`:** Scenri has no accounts, so anyone who can reach the port can spend your API keys and delete your library. Setting `SCENRI_HOST=0.0.0.0` opens it to your local network so you can use it from a phone. That path prints a URL carrying a one-time access token, refuses every request without it, and rejects unfamiliar `Host` headers. Treat it as convenience on a network you trust, not as a security boundary.
138
+ **Phones and tablets.** Scenri also answers on this computer's Wi-Fi or Ethernet address, so a phone on the same network can open it: Settings, Local access shows a QR code. Scenri has no accounts, so every device other than this computer has to bring a six-digit code, which rides inside the QR code and the link; typing the bare address asks for it, wrong guesses are slowed to a crawl, and **New code** signs every device out. The first time, macOS or Windows may ask whether Node may accept incoming connections: allow it, or phones cannot reach Scenri. Setting `SCENRI_HOST=127.0.0.1` keeps Scenri to this computer alone. Over plain http the code travels unencrypted, like everything else, so treat phone access as convenience on a network you trust.
139
139
 
140
140
  ## Layout
141
141
 
@@ -408,7 +408,11 @@ function ensureIndexes(db) {
408
408
  }
409
409
  function searchTextSql(alias) {
410
410
  const brief = `CASE WHEN json_valid(${alias}.brief) THEN ${alias}.brief ELSE '{}' END`;
411
- return `trim(coalesce(${alias}.prompt, '') || ' ' ||
411
+ const said = `CASE WHEN json_array_length(${brief}, '$.tokens') > 0
412
+ THEN (SELECT group_concat(json_extract(je.value, '$.v'), ' ') FROM json_each(${brief}, '$.tokens') AS je
413
+ WHERE json_extract(je.value, '$.t') = 'text')
414
+ ELSE ${alias}.prompt END`;
415
+ return `trim(coalesce(${said}, '') || ' ' ||
412
416
  coalesce((SELECT group_concat(je.value, ' ') FROM json_each(${brief}, '$.templateFields') AS je), '') || ' ' ||
413
417
  coalesce((SELECT group_concat(coalesce(json_extract(je.value, '$.name'), '') || ' ' || coalesce(json_extract(je.value, '$.hex'), ''), ' ')
414
418
  FROM json_each(${brief}, '$.tokens') AS je WHERE json_extract(je.value, '$.t') = 'color'), ''))`;
@@ -434,8 +438,11 @@ function tokenRowsFromNodesSql() {
434
438
  FROM nodes n
435
439
  WHERE json_extract(${brief}, '$.templateId') IS NOT NULL`;
436
440
  }
437
- var SEARCH_INDEX_VERSION = "v1";
441
+ var SEARCH_INDEX_VERSION = "v2";
438
442
  function ensureSearch(db) {
443
+ const marker = db.prepare("SELECT value FROM settings WHERE key='search_index'").get()?.value;
444
+ if (marker !== SEARCH_INDEX_VERSION)
445
+ db.exec("DROP TRIGGER IF EXISTS nodes_search_ai; DROP TRIGGER IF EXISTS nodes_search_au;");
439
446
  db.exec(`
440
447
  CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(text, tokenize='trigram case_sensitive 0 remove_diacritics 1');
441
448
  CREATE TABLE IF NOT EXISTS node_tokens (
@@ -460,7 +467,6 @@ function ensureSearch(db) {
460
467
  DELETE FROM node_tokens WHERE node_id = old.id;
461
468
  END;
462
469
  `);
463
- const marker = db.prepare("SELECT value FROM settings WHERE key='search_index'").get()?.value;
464
470
  const bounds = db.prepare("SELECT min(rowid) AS lo, max(rowid) AS hi, count(*) AS c FROM nodes").get();
465
471
  const indexed = (rowid) => rowid !== null && !!db.prepare("SELECT 1 FROM nodes_fts WHERE rowid = ?").get(rowid);
466
472
  const whole = bounds.c === 0 || indexed(bounds.lo) && indexed(bounds.hi);
@@ -656,6 +662,8 @@ function fold(s) {
656
662
  }
657
663
  var STEM_MIN = 4;
658
664
  var TRIGRAM_MIN = 3;
665
+ var WORDY = /^[\p{L}\p{N}]+$/u;
666
+ var byWordStart = (term) => term.text.length < TRIGRAM_MIN && WORDY.test(term.text);
659
667
  function searchTerms(q) {
660
668
  return fold(q).trim().split(/\s+/).filter(Boolean).map((text) => ({
661
669
  text,
@@ -666,12 +674,29 @@ function termMatches(haystack, term) {
666
674
  const h = fold(haystack);
667
675
  return h.includes(term.text) || term.stem !== null && h.includes(term.stem);
668
676
  }
677
+ function nameMatches(name, term) {
678
+ if (!byWordStart(term)) return termMatches(name, term);
679
+ return fold(name).split(/[^\p{L}\p{N}]+/u).some((w) => w.startsWith(term.text));
680
+ }
669
681
  function matchesQuery(haystack, q) {
670
682
  const terms = searchTerms(q);
671
683
  if (!terms.length) return true;
672
684
  return terms.every((t) => termMatches(haystack, t));
673
685
  }
674
686
  var quote = (s) => `"${s.replace(/"/g, '""')}"`;
687
+ var WORD_BREAK = `]${" \n\r.,;:!?(){}\"'`/\\|_+*&#@<>=~^%$["}\u05BE\u05F3\u05F4\u201C\u201D\u2018\u2019\u2026\u2013\u2014-`;
688
+ function shortTermSql(term, column, param) {
689
+ if (term.text.length >= TRIGRAM_MIN) return null;
690
+ if (byWordStart(term))
691
+ return {
692
+ sql: `(lower(${column}) GLOB @${param}a OR lower(${column}) GLOB @${param}b)`,
693
+ params: { [`${param}a`]: `${term.text}*`, [`${param}b`]: `*[${WORD_BREAK}]${term.text}*` }
694
+ };
695
+ return {
696
+ sql: `${column} LIKE @${param} ESCAPE '\\'`,
697
+ params: { [param]: `%${term.text.replace(/[\\%_]/g, (c) => `\\${c}`)}%` }
698
+ };
699
+ }
675
700
  function ftsMatch(term) {
676
701
  if (term.text.length < TRIGRAM_MIN) return null;
677
702
  return term.stem ? `(${quote(term.text)} OR ${quote(term.stem)})` : quote(term.text);
@@ -794,6 +819,11 @@ function filterSql(f, params, withLens) {
794
819
  params[`m${i}`] = match;
795
820
  any.push(`n.rowid IN (SELECT rowid FROM nodes_fts WHERE nodes_fts MATCH @m${i})`);
796
821
  }
822
+ const short = shortTermSql(term, "f.text", `l${i}`);
823
+ if (short) {
824
+ Object.assign(params, short.params);
825
+ any.push(`EXISTS (SELECT 1 FROM nodes_fts f WHERE f.rowid = n.rowid AND ${short.sql})`);
826
+ }
797
827
  if (term.tokenIds.length) {
798
828
  const names = term.tokenIds.map((t, j) => {
799
829
  params[`t${i}_${j}`] = t;
@@ -2159,10 +2189,10 @@ var REFERENCE_ROLE_DIRECTIVE = {
2159
2189
  // portrait as the scene image beside a selected presenter) showed which
2160
2190
  // half the model heard. Every treatment clause is retained word for word;
2161
2191
  // only the order and the register of the identity refusal changed.
2162
- scene: "a reference for this world, never for a person: take no identity from the person in it \u2014 not their face, not their likeness \u2014 they are an anonymous stand-in whose place the attached subject takes. Match the environment, the light, and the material, density, scale, finish and spread of the treatment applied to the figure, including which parts of the form it covers and how far it reaches; treat any product, garment or prop staged in it as the same kind of stand-in, demonstrating placement and scale \u2014 never an object to reproduce",
2192
+ scene: "a reference for this world, never for a person: take no identity from the person in it \u2014 not their face, not their likeness \u2014 they are an anonymous stand-in whose place the attached presenter takes. Match the environment, the light, and the material, density, scale, finish and spread of the treatment applied to the figure, including which parts of the form it covers and how far it reaches; its furniture, props and objects are part of the set at their real size, never a stand-in for an attached product and never a measure of its size",
2163
2193
  composition: "a reference for framing, camera angle and pose only \u2014 take no subject, color, material or branding from it",
2164
2194
  style: "a reference for overall treatment and mood only \u2014 take no composition, subject or product detail from it",
2165
- reference: "a reference to match in composition, lighting and treatment"
2195
+ reference: "an image the direction refers to, used the way the direction says; where the direction says nothing about it, a reference to match in composition, lighting and treatment"
2166
2196
  };
2167
2197
  var EDIT_REFERENCE_ROLE_DIRECTIVE = {
2168
2198
  product: "the exact product: keep or restore its label, shape and design faithfully",
@@ -2172,7 +2202,7 @@ var EDIT_REFERENCE_ROLE_DIRECTIVE = {
2172
2202
  scene: "a reference for environment, light and treatment only \u2014 take no identity from any person in it",
2173
2203
  composition: "a reference for framing and pose only",
2174
2204
  style: "a reference for treatment and mood only",
2175
- reference: "a reference for composition, lighting and treatment only"
2205
+ reference: "an image the instruction refers to, used the way the instruction says; where the instruction says nothing about it, a reference for composition, lighting and treatment only"
2176
2206
  };
2177
2207
  var BUDGET_EXHAUSTED = "scenri:budget-exhausted";
2178
2208
  var ASPECT_TOLERANCE = 0.15;
@@ -2223,6 +2253,6 @@ function createCore(homeDir = defaultHome()) {
2223
2253
  };
2224
2254
  }
2225
2255
 
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-MHTA6O3A.js.map
2228
- //# sourceMappingURL=chunk-MHTA6O3A.js.map
2256
+ 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, nameMatches, ratioLabel, searchTerms, shortTermSql, termMatches, uniqueProjectSlug, uniqueSetSlug, uniqueSlug };
2257
+ //# sourceMappingURL=chunk-LMXHCXUC.js.map
2258
+ //# sourceMappingURL=chunk-LMXHCXUC.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-INKSTJNC.js');
466
+ const { createCore } = await import('./src-XRZP3ZB5.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-4A3QWRSC.js.map
534
- //# sourceMappingURL=cli-4A3QWRSC.js.map
533
+ //# sourceMappingURL=cli-NXOTZTBT.js.map
534
+ //# sourceMappingURL=cli-NXOTZTBT.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-4A3QWRSC.js');
89
+ const { runDesktopCommand } = await import('./cli-NXOTZTBT.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-4A3QWRSC.js');
94
+ const { runOpenCommand } = await import('./cli-NXOTZTBT.js');
95
95
  process.exit(await runOpenCommand(fileURLToPath(import.meta.url)));
96
96
  break;
97
97
  }