human2ai 0.1.6 → 0.1.8

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 (160) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/cli/main.js +74 -3
  4. package/dist/cli/main.js.map +1 -1
  5. package/dist/database/draft-version-errors.d.ts +6 -0
  6. package/dist/database/draft-version-errors.js +11 -0
  7. package/dist/database/draft-version-errors.js.map +1 -1
  8. package/dist/database/draft-version-store.js +17 -2
  9. package/dist/database/draft-version-store.js.map +1 -1
  10. package/dist/database/image-asset-repository.d.ts +5 -0
  11. package/dist/database/image-asset-repository.js +22 -4
  12. package/dist/database/image-asset-repository.js.map +1 -1
  13. package/dist/database/migrate.js +2 -0
  14. package/dist/database/migrate.js.map +1 -1
  15. package/dist/database/project-session-repository.d.ts +12 -1
  16. package/dist/database/project-session-repository.js +48 -11
  17. package/dist/database/project-session-repository.js.map +1 -1
  18. package/dist/database/session-preview-validation.js +37 -4
  19. package/dist/database/session-preview-validation.js.map +1 -1
  20. package/dist/database/session-storage-repository.d.ts +35 -0
  21. package/dist/database/session-storage-repository.js +281 -0
  22. package/dist/database/session-storage-repository.js.map +1 -0
  23. package/dist/database/ui-sketch-png-split-repository.d.ts +13 -0
  24. package/dist/database/ui-sketch-png-split-repository.js +123 -0
  25. package/dist/database/ui-sketch-png-split-repository.js.map +1 -0
  26. package/dist/database/workspace-settings-repository.d.ts +10 -0
  27. package/dist/database/workspace-settings-repository.js +30 -0
  28. package/dist/database/workspace-settings-repository.js.map +1 -0
  29. package/dist/domain/composition/clipboard.d.ts +3 -0
  30. package/dist/domain/composition/clipboard.js +30 -0
  31. package/dist/domain/composition/clipboard.js.map +1 -0
  32. package/dist/domain/composition/index.d.ts +1 -0
  33. package/dist/domain/composition/index.js +1 -0
  34. package/dist/domain/composition/index.js.map +1 -1
  35. package/dist/domain/session/canvas-edit-history.d.ts +2 -0
  36. package/dist/domain/session/canvas-edit-history.js +4 -0
  37. package/dist/domain/session/canvas-edit-history.js.map +1 -1
  38. package/dist/domain/session/capabilities.js +14 -0
  39. package/dist/domain/session/capabilities.js.map +1 -1
  40. package/dist/domain/session/storage.d.ts +22 -0
  41. package/dist/domain/session/storage.js +10 -0
  42. package/dist/domain/session/storage.js.map +1 -0
  43. package/dist/domain/session/types.d.ts +4 -0
  44. package/dist/domain/ui-sketch/clipboard.d.ts +2 -0
  45. package/dist/domain/ui-sketch/clipboard.js +21 -1
  46. package/dist/domain/ui-sketch/clipboard.js.map +1 -1
  47. package/dist/domain/ui-sketch/draft.js +22 -0
  48. package/dist/domain/ui-sketch/draft.js.map +1 -1
  49. package/dist/domain/ui-sketch/index.d.ts +1 -1
  50. package/dist/domain/ui-sketch/index.js +1 -1
  51. package/dist/domain/ui-sketch/index.js.map +1 -1
  52. package/dist/domain/ui-sketch/png-canvas.d.ts +25 -0
  53. package/dist/domain/ui-sketch/png-canvas.js +78 -0
  54. package/dist/domain/ui-sketch/png-canvas.js.map +1 -0
  55. package/dist/domain/ui-sketch/png-split-regions.d.ts +7 -0
  56. package/dist/domain/ui-sketch/png-split-regions.js +40 -0
  57. package/dist/domain/ui-sketch/png-split-regions.js.map +1 -0
  58. package/dist/domain/ui-sketch/png-split.d.ts +49 -0
  59. package/dist/domain/ui-sketch/png-split.js +39 -0
  60. package/dist/domain/ui-sketch/png-split.js.map +1 -0
  61. package/dist/domain/ui-sketch/transparent-png.d.ts +50 -0
  62. package/dist/domain/ui-sketch/transparent-png.js +178 -0
  63. package/dist/domain/ui-sketch/transparent-png.js.map +1 -0
  64. package/dist/domain/ui-sketch/types.d.ts +2 -0
  65. package/dist/server/app.d.ts +7 -1
  66. package/dist/server/app.js +21 -0
  67. package/dist/server/app.js.map +1 -1
  68. package/dist/server/routes/draft-version-routes.js +12 -3
  69. package/dist/server/routes/draft-version-routes.js.map +1 -1
  70. package/dist/server/routes/project-sessions.js +14 -0
  71. package/dist/server/routes/project-sessions.js.map +1 -1
  72. package/dist/server/routes/storage-protection.d.ts +3 -0
  73. package/dist/server/routes/storage-protection.js +29 -0
  74. package/dist/server/routes/storage-protection.js.map +1 -0
  75. package/dist/server/routes/ui-sketch-png-splits.d.ts +4 -0
  76. package/dist/server/routes/ui-sketch-png-splits.js +42 -0
  77. package/dist/server/routes/ui-sketch-png-splits.js.map +1 -0
  78. package/dist/server/routes/workspace-settings.d.ts +3 -0
  79. package/dist/server/routes/workspace-settings.js +25 -0
  80. package/dist/server/routes/workspace-settings.js.map +1 -0
  81. package/dist/server/runtime.js +8 -1
  82. package/dist/server/runtime.js.map +1 -1
  83. package/dist/server/session-preview-service.js +7 -4
  84. package/dist/server/session-preview-service.js.map +1 -1
  85. package/dist/server/storage-maintenance-service.d.ts +6 -0
  86. package/dist/server/storage-maintenance-service.js +65 -0
  87. package/dist/server/storage-maintenance-service.js.map +1 -0
  88. package/locales/en/common.json +68 -4
  89. package/migrations/0012_ui_sketch_png_splits.sql +6 -0
  90. package/migrations/0013_storage_retention.sql +43 -0
  91. package/migrations/0014_workspace_settings_and_trash.sql +11 -0
  92. package/package.json +1 -1
  93. package/schemas/ui-sketch-draft.schema.json +173 -0
  94. package/skills/human2ai/SKILL.md +8 -2
  95. package/skills/human2ai/references/ui-extraction-prompt.txt +41 -0
  96. package/skills/human2ai/references/ui-extraction.md +38 -0
  97. package/skills/human2ai/references/ui-layout.md +22 -0
  98. package/web/out/404/index.html +1 -1
  99. package/web/out/404.html +1 -1
  100. package/web/out/__next.__PAGE__.txt +8 -8
  101. package/web/out/__next._full.txt +8 -8
  102. package/web/out/__next._tree.txt +4 -4
  103. package/web/out/_next/static/chunks/327-3dba8bbcb4bcb1fc.js +1 -0
  104. package/web/out/_next/static/chunks/868-6a413fc8c52cf6df.js +1 -0
  105. package/web/out/_next/static/chunks/{164-d8dd2b053f27bf62.js → 989-6d7aa6a22a75f7de.js} +61 -61
  106. package/web/out/_next/static/chunks/app/_not-found/{page-742cf440bebb3b9b.js → page-fddf8e0980e10cc5.js} +1 -1
  107. package/web/out/_next/static/chunks/app/composition/page-f810843a0f114d13.js +1 -0
  108. package/web/out/_next/static/chunks/app/layout-9c5f0b5667b84400.js +1 -0
  109. package/web/out/_next/static/chunks/app/{page-9b8bd42a93c9242c.js → page-29cacabbd1c90b6f.js} +1 -1
  110. package/web/out/_next/static/chunks/app/spatial/page-014875456f84d8fa.js +1 -0
  111. package/web/out/_next/static/chunks/app/styles/page-1077a7decb9be158.js +1 -0
  112. package/web/out/_next/static/chunks/app/ui-sketch/page-ea382541f80cad0d.js +1 -0
  113. package/web/out/_next/static/chunks/{main-app-5419a575f3ff4496.js → main-app-9985b291e50ea69b.js} +1 -1
  114. package/web/out/_next/static/chunks/next/dist/client/components/builtin/global-error-1193cefa6c9c6179.js +1 -0
  115. package/web/out/_next/static/chunks/{webpack-e781b4e158568ba0.js → webpack-cfe3662bbaa8d6ce.js} +1 -1
  116. package/web/out/_next/static/css/04c7fcbfa9004e04.css +1 -0
  117. package/web/out/_next/static/css/15abfc5e59099f48.css +1 -0
  118. package/web/out/_next/static/css/{210db1811016f805.css → 5062b0bcc3118ed8.css} +1 -1
  119. package/web/out/_next/static/css/57ecc433cb654a57.css +1 -0
  120. package/web/out/_next/static/css/7a62016f8918890b.css +5 -0
  121. package/web/out/_not-found/__next._full.txt +7 -7
  122. package/web/out/_not-found/__next._not-found.__PAGE__.txt +7 -7
  123. package/web/out/_not-found/__next._tree.txt +4 -4
  124. package/web/out/_not-found/index.html +1 -1
  125. package/web/out/_not-found/index.txt +7 -7
  126. package/web/out/composition/__next._full.txt +9 -9
  127. package/web/out/composition/__next._tree.txt +5 -5
  128. package/web/out/composition/__next.composition.__PAGE__.txt +9 -9
  129. package/web/out/composition/index.html +1 -1
  130. package/web/out/composition/index.txt +9 -9
  131. package/web/out/index.html +1 -1
  132. package/web/out/index.txt +8 -8
  133. package/web/out/spatial/__next._full.txt +8 -8
  134. package/web/out/spatial/__next._tree.txt +4 -4
  135. package/web/out/spatial/__next.spatial.__PAGE__.txt +8 -8
  136. package/web/out/spatial/index.html +1 -1
  137. package/web/out/spatial/index.txt +8 -8
  138. package/web/out/styles/__next._full.txt +8 -8
  139. package/web/out/styles/__next._tree.txt +4 -4
  140. package/web/out/styles/__next.styles.__PAGE__.txt +8 -8
  141. package/web/out/styles/index.html +1 -1
  142. package/web/out/styles/index.txt +8 -8
  143. package/web/out/ui-sketch/__next._full.txt +9 -9
  144. package/web/out/ui-sketch/__next._tree.txt +5 -5
  145. package/web/out/ui-sketch/__next.ui-sketch.__PAGE__.txt +9 -9
  146. package/web/out/ui-sketch/index.html +1 -1
  147. package/web/out/ui-sketch/index.txt +9 -9
  148. package/web/out/_next/static/chunks/348-6baeec6b3dbf02ab.js +0 -1
  149. package/web/out/_next/static/chunks/app/composition/page-8d037a4b99bb5546.js +0 -1
  150. package/web/out/_next/static/chunks/app/layout-ca2f61a5a618caea.js +0 -1
  151. package/web/out/_next/static/chunks/app/spatial/page-1d16089ca24b2eb8.js +0 -1
  152. package/web/out/_next/static/chunks/app/styles/page-e4d3c95957349351.js +0 -1
  153. package/web/out/_next/static/chunks/app/ui-sketch/page-23e5c3b12d99082c.js +0 -1
  154. package/web/out/_next/static/chunks/next/dist/client/components/builtin/global-error-15f5118ed5e001a8.js +0 -1
  155. package/web/out/_next/static/css/2160a6bbf2cb551c.css +0 -1
  156. package/web/out/_next/static/css/28a4978acacc40b0.css +0 -1
  157. package/web/out/_next/static/css/2a0db06638359895.css +0 -1
  158. package/web/out/_next/static/css/4b1f9941c214d619.css +0 -5
  159. /package/web/out/_next/static/{MtSBeMOcAQHxKAV1fmedM → SiEWNIZfGL4sShRQt9h5W}/_buildManifest.js +0 -0
  160. /package/web/out/_next/static/{MtSBeMOcAQHxKAV1fmedM → SiEWNIZfGL4sShRQt9h5W}/_ssgManifest.js +0 -0
@@ -0,0 +1,281 @@
1
+ import { readdir, lstat, unlink } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { setImmediate } from "node:timers/promises";
4
+ import { ImageAssetNotFoundError } from "./image-asset-repository.js";
5
+ import { InvalidRecordError, SessionNotFoundError, ProjectSessionRepository } from "./project-session-repository.js";
6
+ import { WorkspaceSettingsRepository } from "./workspace-settings-repository.js";
7
+ import { DraftVersionExpiredError, DraftVersionNotFoundError } from "./draft-version-errors.js";
8
+ import { HISTORY_VERSION_LIMIT, STORAGE_PROTECTION_MS } from "../domain/session/storage.js";
9
+ const tables = {
10
+ "image-composition": "composition_draft_versions",
11
+ "ui-layout": "ui_sketch_draft_versions",
12
+ spatial: "spatial_draft_versions",
13
+ };
14
+ /** Called inside the save transaction so the latest revision is never removed. */
15
+ export function pruneDraftVersions(database, table, sessionId, now = Date.now()) {
16
+ const { historyRetentionDays } = new WorkspaceSettingsRepository(database).get();
17
+ return database.prepare(`DELETE FROM ${table} WHERE session_id = ?
18
+ AND revision != (SELECT max(revision) FROM ${table} WHERE session_id = ?)
19
+ AND (created_at <= ? OR revision NOT IN (SELECT revision FROM ${table} WHERE session_id = ? ORDER BY revision DESC LIMIT ?))
20
+ AND revision NOT IN (
21
+ SELECT held.value FROM session_storage_protections protection, json_each(protection.revisions_json) held
22
+ WHERE protection.session_id = ? AND protection.expires_at > ?
23
+ )`).run(sessionId, sessionId, new Date(now - historyRetentionDays * 86_400_000).toISOString(), sessionId, HISTORY_VERSION_LIMIT, sessionId, new Date(now).toISOString()).changes;
24
+ }
25
+ export class SessionStorageRepository {
26
+ database;
27
+ assets;
28
+ now;
29
+ root;
30
+ constructor(database, assets, artifactsDirectory, now = Date.now) {
31
+ this.database = database;
32
+ this.assets = assets;
33
+ this.now = now;
34
+ this.root = resolve(artifactsDirectory);
35
+ }
36
+ protect(sessionId, clientId, protection) {
37
+ const table = this.table(sessionId);
38
+ this.database.transaction(() => {
39
+ const missing = this.database.prepare(`SELECT held.value
40
+ FROM json_each(?) held LEFT JOIN image_assets asset ON asset.id = held.value AND asset.session_id = ? AND asset.deleting = 0
41
+ WHERE asset.id IS NULL LIMIT 1`).get(JSON.stringify(protection.assetIds), sessionId);
42
+ if (missing)
43
+ throw new ImageAssetNotFoundError(sessionId, missing.value);
44
+ const expired = this.database.prepare(`SELECT held.value
45
+ FROM json_each(?) held LEFT JOIN ${table} version ON version.revision = held.value AND version.session_id = ?
46
+ WHERE version.id IS NULL LIMIT 1`).get(JSON.stringify(protection.revisions), sessionId);
47
+ if (expired) {
48
+ const latest = this.database.prepare(`SELECT max(revision) AS revision FROM ${table} WHERE session_id = ?`).get(sessionId);
49
+ if (expired.value <= (latest?.revision ?? 0))
50
+ throw new DraftVersionExpiredError(sessionId, expired.value);
51
+ throw new DraftVersionNotFoundError(sessionId, expired.value, "session");
52
+ }
53
+ this.database.prepare(`INSERT INTO session_storage_protections VALUES (?, ?, ?, ?, ?)
54
+ ON CONFLICT(session_id, client_id) DO UPDATE SET revisions_json = excluded.revisions_json,
55
+ asset_ids_json = excluded.asset_ids_json, expires_at = excluded.expires_at`).run(sessionId, clientId, JSON.stringify([...new Set(protection.revisions)]), JSON.stringify([...new Set(protection.assetIds)]), new Date(this.now() + STORAGE_PROTECTION_MS).toISOString());
56
+ // Reintroducing a reference restarts the continuous no-reference grace period.
57
+ this.database.prepare("UPDATE image_assets SET unreferenced_since = NULL WHERE session_id = ? AND id IN (SELECT value FROM json_each(?))")
58
+ .run(sessionId, JSON.stringify(protection.assetIds));
59
+ })();
60
+ }
61
+ release(sessionId, clientId) {
62
+ this.database.prepare("DELETE FROM session_storage_protections WHERE session_id = ? AND client_id = ?").run(sessionId, clientId);
63
+ }
64
+ table(sessionId) {
65
+ const session = this.database.prepare("SELECT session_type FROM sessions WHERE id = ? AND deleted_at IS NULL").get(sessionId);
66
+ if (!session)
67
+ throw new SessionNotFoundError(sessionId);
68
+ return tables[session.session_type];
69
+ }
70
+ usedAssets(sessionId) {
71
+ const table = this.table(sessionId);
72
+ const rows = this.database.prepare(`SELECT DISTINCT json_extract(image.value, '$.assetId') AS id
73
+ FROM ${table} version, json_each(version.draft_json, '$.images') image
74
+ WHERE version.session_id = ? AND json_extract(image.value, '$.assetId') IS NOT NULL
75
+ UNION SELECT held.value AS id FROM session_storage_protections protection, json_each(protection.asset_ids_json) held
76
+ WHERE protection.session_id = ? AND protection.expires_at > ?`).all(sessionId, sessionId, new Date(this.now()).toISOString());
77
+ return new Set(rows.map(row => row.id));
78
+ }
79
+ async collect(sessionId) {
80
+ const result = { versionsDeleted: 0, imagesDeleted: 0, bytesFreed: 0, failedFiles: [] };
81
+ const now = new Date(this.now()).toISOString();
82
+ const settings = new WorkspaceSettingsRepository(this.database).get();
83
+ if (!sessionId)
84
+ new ProjectSessionRepository(this.database).purgeExpiredTrash(new Date(this.now() - settings.trashRetentionDays * 86_400_000).toISOString());
85
+ this.database.prepare("DELETE FROM session_storage_protections WHERE expires_at <= ?").run(now);
86
+ const sessions = this.database.prepare(`SELECT id FROM sessions WHERE deleted_at IS NULL${sessionId ? " AND id = ?" : ""}`)
87
+ .all(...(sessionId ? [sessionId] : []));
88
+ for (const session of sessions) {
89
+ const table = this.table(session.id);
90
+ result.versionsDeleted += this.database.transaction(() => pruneDraftVersions(this.database, table, session.id, this.now()))();
91
+ const used = this.usedAssets(session.id);
92
+ const rows = this.database.prepare("SELECT id, relative_path, unreferenced_since, deleting FROM image_assets WHERE session_id = ?").all(session.id);
93
+ for (const row of rows) {
94
+ if (used.has(row.id) || this.assets.isProtected(row.id)) {
95
+ this.database.prepare("UPDATE image_assets SET unreferenced_since = NULL WHERE id = ?").run(row.id);
96
+ continue;
97
+ }
98
+ if (!row.unreferenced_since) {
99
+ this.database.prepare("UPDATE image_assets SET unreferenced_since = ? WHERE id = ?").run(now, row.id);
100
+ continue;
101
+ }
102
+ if (!row.deleting && Date.parse(row.unreferenced_since) > this.now() - settings.imageRetentionDays * 86_400_000)
103
+ continue;
104
+ const file = join(this.root, row.relative_path);
105
+ const size = await fileSize(file);
106
+ const claimed = this.database.transaction(() => {
107
+ const active = this.database.prepare("SELECT id FROM sessions WHERE id = ? AND deleted_at IS NULL").get(session.id);
108
+ if (!active)
109
+ return false;
110
+ const { imageRetentionDays } = new WorkspaceSettingsRepository(this.database).get();
111
+ // Filesystem awaits allow a save, lease renewal or split task to introduce references.
112
+ const current = this.database.prepare("SELECT unreferenced_since, deleting FROM image_assets WHERE id = ?").get(row.id);
113
+ if (!current || (!current.deleting && (!current.unreferenced_since
114
+ || Date.parse(current.unreferenced_since) > this.now() - imageRetentionDays * 86_400_000)))
115
+ return false;
116
+ if (this.assets.isProtected(row.id) || this.usedAssets(session.id).has(row.id)) {
117
+ this.database.prepare("UPDATE image_assets SET unreferenced_since = NULL WHERE id = ?").run(row.id);
118
+ return false;
119
+ }
120
+ return this.database.prepare("UPDATE image_assets SET deleting = 1 WHERE id = ?").run(row.id).changes > 0;
121
+ })();
122
+ if (!claimed)
123
+ continue;
124
+ try {
125
+ await removeFile(file);
126
+ this.database.transaction(() => {
127
+ // Cache records are retry checkpoints, not permanent ownership of deleted slices.
128
+ this.database.prepare(`DELETE FROM ui_sketch_png_splits WHERE session_id = ? AND
129
+ EXISTS (SELECT 1 FROM json_each(record_json, '$.sources') source WHERE json_extract(source.value, '$.assetId') = ?)`)
130
+ .run(session.id, row.id);
131
+ const caches = this.database.prepare(`SELECT id, record_json FROM ui_sketch_png_splits
132
+ WHERE session_id = ? AND EXISTS (SELECT 1 FROM json_each(record_json, '$.pieces') piece WHERE json_extract(piece.value, '$.assetId') = ?)`)
133
+ .all(session.id, row.id);
134
+ for (const cache of caches) {
135
+ const record = JSON.parse(cache.record_json);
136
+ for (const piece of record.pieces)
137
+ if (piece.assetId === row.id)
138
+ piece.assetId = null;
139
+ this.database.prepare("UPDATE ui_sketch_png_splits SET record_json = ? WHERE id = ?").run(JSON.stringify(record), cache.id);
140
+ }
141
+ this.database.prepare("DELETE FROM image_assets WHERE id = ? AND deleting = 1").run(row.id);
142
+ this.database.prepare("DELETE FROM storage_orphan_files WHERE relative_path = ?").run(row.relative_path);
143
+ })();
144
+ result.imagesDeleted++;
145
+ result.bytesFreed += size;
146
+ }
147
+ catch {
148
+ this.database.prepare("UPDATE image_assets SET deleting = 0 WHERE id = ?").run(row.id);
149
+ result.failedFiles.push(row.relative_path);
150
+ }
151
+ }
152
+ this.database.prepare("DELETE FROM ui_sketch_png_splits WHERE session_id = ? AND updated_at <= ?")
153
+ .run(session.id, new Date(this.now() - settings.historyRetentionDays * 86_400_000).toISOString());
154
+ await setImmediate();
155
+ }
156
+ // Includes legacy/crash leftovers and files whose session was removed by a cascade.
157
+ if (!sessionId)
158
+ await this.collectOrphanFiles(result);
159
+ return result;
160
+ }
161
+ async collectOrphanFiles(result) {
162
+ const { imageRetentionDays } = new WorkspaceSettingsRepository(this.database).get();
163
+ const referenced = new Set(this.database.prepare(`SELECT relative_path FROM image_assets
164
+ UNION SELECT relative_path FROM style_reference_images UNION SELECT relative_path FROM style_preview_models`).all().map(row => row.relative_path));
165
+ const known = this.database.prepare(`SELECT relative_path FROM image_assets WHERE relative_path = ?
166
+ UNION SELECT relative_path FROM style_reference_images WHERE relative_path = ? UNION SELECT relative_path FROM style_preview_models WHERE relative_path = ?`);
167
+ const now = new Date(this.now()).toISOString();
168
+ for (const path of await this.managedFiles()) {
169
+ if (referenced.has(path)) {
170
+ this.database.prepare("DELETE FROM storage_orphan_files WHERE relative_path = ?").run(path);
171
+ }
172
+ else {
173
+ this.database.prepare("INSERT OR IGNORE INTO storage_orphan_files VALUES (?, ?)").run(path, now);
174
+ }
175
+ }
176
+ const stale = this.database.prepare("SELECT relative_path FROM storage_orphan_files WHERE unreferenced_since <= ?").all(new Date(this.now() - imageRetentionDays * 86_400_000).toISOString());
177
+ for (const row of stale) {
178
+ if (!managedPath(row.relative_path))
179
+ continue;
180
+ const file = join(this.root, row.relative_path), size = await fileSize(file);
181
+ const id = row.relative_path.split(/[\\/]/).at(-1).split(".")[0];
182
+ if (this.assets.isProtected(id)) {
183
+ this.database.prepare("UPDATE storage_orphan_files SET unreferenced_since = ? WHERE relative_path = ?").run(now, row.relative_path);
184
+ continue;
185
+ }
186
+ if (known.get(row.relative_path, row.relative_path, row.relative_path)) {
187
+ this.database.prepare("DELETE FROM storage_orphan_files WHERE relative_path = ?").run(row.relative_path);
188
+ continue;
189
+ }
190
+ try {
191
+ await removeFile(file);
192
+ }
193
+ catch {
194
+ result.failedFiles.push(row.relative_path);
195
+ continue;
196
+ }
197
+ this.database.prepare("DELETE FROM storage_orphan_files WHERE relative_path = ?").run(row.relative_path);
198
+ if (size > 0) {
199
+ result.bytesFreed += size;
200
+ if (!file.endsWith(".glb"))
201
+ result.imagesDeleted++;
202
+ }
203
+ }
204
+ }
205
+ async managedFiles() {
206
+ const files = [];
207
+ for (const owner of await entries(this.root)) {
208
+ if (!owner.isDirectory())
209
+ continue;
210
+ if (owner.name === "styles") {
211
+ for (const style of await entries(join(this.root, "styles"))) {
212
+ if (!style.isDirectory())
213
+ continue;
214
+ for (const file of await entries(join(this.root, "styles", style.name))) {
215
+ const relative = join("styles", style.name, file.name);
216
+ if (file.isFile() && managedPath(relative))
217
+ files.push(relative);
218
+ }
219
+ }
220
+ }
221
+ else {
222
+ for (const file of await entries(join(this.root, owner.name, "source"))) {
223
+ const relative = join(owner.name, "source", file.name);
224
+ if (file.isFile() && managedPath(relative))
225
+ files.push(relative);
226
+ }
227
+ }
228
+ }
229
+ return files;
230
+ }
231
+ /** Only called while editing is idle. Convert older databases once, then reclaim small batches. */
232
+ compact() {
233
+ const pages = this.database.pragma("page_count", { simple: true });
234
+ const free = this.database.pragma("freelist_count", { simple: true });
235
+ const size = this.database.pragma("page_size", { simple: true });
236
+ if (free * size < 8 * 1024 * 1024 || free < pages * 0.2)
237
+ return;
238
+ if (this.database.pragma("auto_vacuum", { simple: true }) !== 2) {
239
+ this.database.pragma("auto_vacuum = INCREMENTAL");
240
+ this.database.exec("VACUUM");
241
+ }
242
+ else
243
+ this.database.pragma("incremental_vacuum(256)");
244
+ }
245
+ }
246
+ function managedPath(path) {
247
+ return /^(?:[0-9a-f-]{36}\/source|styles\/[0-9a-f-]{36})\/[0-9a-f-]{36}\.(?:png|jpg|webp|svg|glb)$/.test(path.replaceAll("\\", "/"));
248
+ }
249
+ async function entries(path) {
250
+ try {
251
+ return await readdir(path, { withFileTypes: true });
252
+ }
253
+ catch (error) {
254
+ if (error.code === "ENOENT")
255
+ return [];
256
+ throw error;
257
+ }
258
+ }
259
+ async function fileSize(path) {
260
+ try {
261
+ const stat = await lstat(path);
262
+ if (!stat.isFile())
263
+ throw new InvalidRecordError("Managed image path is not a file.");
264
+ return stat.size;
265
+ }
266
+ catch (error) {
267
+ if (error.code === "ENOENT")
268
+ return 0;
269
+ throw error;
270
+ }
271
+ }
272
+ async function removeFile(path) {
273
+ try {
274
+ await unlink(path);
275
+ }
276
+ catch (error) {
277
+ if (error.code !== "ENOENT")
278
+ throw error;
279
+ }
280
+ }
281
+ //# sourceMappingURL=session-storage-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-storage-repository.js","sourceRoot":"","sources":["../../src/database/session-storage-repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { DatabaseConnection } from "./migrate.ts";
2
+ import { ImageAssetRepository } from "./image-asset-repository.ts";
3
+ import type { PngSplitBatch, PngSplitOptions, PngSplitSource, PngSplitPreview } from "../domain/ui-sketch/png-split.ts";
4
+ export declare class UiSketchPngSplitRepository {
5
+ private readonly database;
6
+ private readonly assets;
7
+ private readonly pending;
8
+ constructor(database: DatabaseConnection, assets: ImageAssetRepository);
9
+ preview(sessionId: string, sources: PngSplitSource[], options: PngSplitOptions): Promise<PngSplitPreview>;
10
+ prepare(sessionId: string, sources: PngSplitSource[], options: PngSplitOptions): Promise<PngSplitBatch>;
11
+ private prepareProtected;
12
+ private prepareSource;
13
+ }
@@ -0,0 +1,123 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ import { ImageAssetNotFoundError, InvalidImageAssetError } from "./image-asset-repository.js";
4
+ import { splitTransparentPng, previewTransparentPng, partitionPngPieces, SPLIT_ALGORITHM, MANUAL_SPLIT_ALGORITHM } from "../domain/ui-sketch/transparent-png.js";
5
+ import { PACKING_ALGORITHM } from "../domain/ui-sketch/png-canvas.js";
6
+ const digest = (bytes) => createHash("sha256").update(bytes).digest("hex");
7
+ export class UiSketchPngSplitRepository {
8
+ database;
9
+ assets;
10
+ pending = new Map();
11
+ constructor(database, assets) {
12
+ this.database = database;
13
+ this.assets = assets;
14
+ }
15
+ async preview(sessionId, sources, options) {
16
+ return this.assets.withProtection(sources.map(source => source.assetId), async () => {
17
+ const previews = [];
18
+ for (const source of [...sources].sort((a, b) => a.nodeId.localeCompare(b.nodeId))) {
19
+ const { asset, filePath } = this.assets.get(sessionId, source.assetId);
20
+ if (asset.mimeType !== "image/png")
21
+ throw new InvalidImageAssetError("PNG splitting requires transparent PNG images.");
22
+ const bytes = await readFile(filePath);
23
+ if (digest(bytes) !== asset.sha256)
24
+ throw new InvalidImageAssetError("Source PNG content changed.");
25
+ try {
26
+ const preview = await previewTransparentPng(bytes, { ...options, regions: source.regions });
27
+ const { kept, discarded } = partitionPngPieces(preview.pieces, options.minSize);
28
+ previews.push({ nodeId: source.nodeId, width: preview.width, height: preview.height,
29
+ discarded: discarded.length, rects: kept.map(piece => piece.sourceRect) });
30
+ }
31
+ catch (error) {
32
+ throw new InvalidImageAssetError(error instanceof Error ? error.message : "Invalid transparent PNG.");
33
+ }
34
+ }
35
+ return { sources: previews };
36
+ });
37
+ }
38
+ async prepare(sessionId, sources, options) {
39
+ return this.assets.withProtection(sources.map(source => source.assetId), () => this.prepareProtected(sessionId, sources, options));
40
+ }
41
+ async prepareProtected(sessionId, sources, options) {
42
+ const batches = [];
43
+ // Selection order does not affect identity, retries or packing.
44
+ for (const source of [...sources].sort((a, b) => a.nodeId.localeCompare(b.nodeId))) {
45
+ const { asset, filePath } = this.assets.get(sessionId, source.assetId);
46
+ if (asset.mimeType !== "image/png")
47
+ throw new InvalidImageAssetError("PNG splitting requires transparent PNG images.");
48
+ const id = digest(JSON.stringify([sessionId, source.nodeId, source.assetId, asset.sha256, SPLIT_ALGORITHM,
49
+ options.alphaThreshold, options.minSize, ...(source.regions?.length ? [MANUAL_SPLIT_ALGORITHM, source.regions] : [])]));
50
+ let pending = this.pending.get(id);
51
+ if (!pending) {
52
+ pending = this.prepareSource(id, sessionId, source, asset.sha256, filePath, options);
53
+ this.pending.set(id, pending);
54
+ }
55
+ try {
56
+ batches.push(await pending);
57
+ }
58
+ finally {
59
+ if (this.pending.get(id) === pending)
60
+ this.pending.delete(id);
61
+ }
62
+ }
63
+ const id = digest(JSON.stringify(batches.map(batch => batch.id)));
64
+ const pieces = batches.flatMap(batch => batch.pieces);
65
+ return { id, algorithm: sources.some(source => source.regions?.length) ? MANUAL_SPLIT_ALGORITHM : SPLIT_ALGORITHM, packingAlgorithm: PACKING_ALGORITHM, options,
66
+ sources: batches.flatMap(batch => batch.sources), pieces, groupId: pieces.length >= 2 ? `png-group-${id}` : null };
67
+ }
68
+ async prepareSource(id, sessionId, source, sha256, filePath, options) {
69
+ const row = this.database.prepare("SELECT record_json FROM ui_sketch_png_splits WHERE id = ?").get(id);
70
+ let record = row ? JSON.parse(row.record_json) : undefined;
71
+ if (record) {
72
+ for (const piece of record.pieces)
73
+ if (piece.assetId) {
74
+ try {
75
+ this.assets.get(sessionId, piece.assetId);
76
+ }
77
+ catch (error) {
78
+ if (error instanceof ImageAssetNotFoundError)
79
+ piece.assetId = null;
80
+ else
81
+ throw error;
82
+ }
83
+ }
84
+ // A reused slice is being handed to a new placement; restart its upload grace.
85
+ this.database.prepare("UPDATE image_assets SET unreferenced_since = NULL WHERE session_id = ? AND id IN (SELECT value FROM json_each(?))")
86
+ .run(sessionId, JSON.stringify(record.pieces.flatMap(piece => piece.assetId ? [piece.assetId] : [])));
87
+ }
88
+ if (record && record.pieces.every(piece => piece.assetId)) {
89
+ this.database.prepare("UPDATE ui_sketch_png_splits SET updated_at = ? WHERE id = ?").run(new Date().toISOString(), id);
90
+ return record;
91
+ }
92
+ const bytes = await readFile(filePath);
93
+ if (digest(bytes) !== sha256)
94
+ throw new InvalidImageAssetError("Source PNG content changed.");
95
+ let split;
96
+ try {
97
+ split = await splitTransparentPng(bytes, { ...options, regions: source.regions });
98
+ }
99
+ catch (error) {
100
+ throw new InvalidImageAssetError(error instanceof Error ? error.message : "Invalid transparent PNG.");
101
+ }
102
+ const { kept, discarded } = partitionPngPieces(split.pieces, options.minSize);
103
+ const persist = () => this.database.prepare("INSERT INTO ui_sketch_png_splits (id, session_id, record_json, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET record_json = excluded.record_json, updated_at = excluded.updated_at").run(id, sessionId, JSON.stringify(record), new Date().toISOString());
104
+ if (!record) {
105
+ record = { id, algorithm: split.algorithm, packingAlgorithm: PACKING_ALGORITHM, options,
106
+ sources: [{ ...source, sha256, width: split.width, height: split.height, discarded: discarded.length }],
107
+ pieces: kept.map((piece, index) => ({ nodeId: `png-${id}-${index}`, assetId: null, sourceNodeId: source.nodeId,
108
+ sourceRect: piece.sourceRect, sha256: digest(piece.png), width: piece.width, height: piece.height })), groupId: null };
109
+ persist();
110
+ }
111
+ for (const [index, piece] of record.pieces.entries()) {
112
+ if (piece.assetId)
113
+ continue;
114
+ const png = kept[index]?.png;
115
+ if (!png || digest(png) !== piece.sha256)
116
+ throw new InvalidImageAssetError("PNG retry content changed.");
117
+ piece.assetId = (await this.assets.create(sessionId, { filename: `${piece.nodeId}.png`, data: png })).id;
118
+ persist();
119
+ }
120
+ return record;
121
+ }
122
+ }
123
+ //# sourceMappingURL=ui-sketch-png-split-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-sketch-png-split-repository.js","sourceRoot":"","sources":["../../src/database/ui-sketch-png-split-repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { DatabaseConnection } from "./migrate.ts";
2
+ import { type WorkspaceSettings, type RetentionSettings } from "../domain/session/storage.ts";
3
+ export declare class WorkspaceSettingsRepository {
4
+ private readonly database;
5
+ constructor(database: DatabaseConnection);
6
+ get(): WorkspaceSettings;
7
+ update(input: RetentionSettings & {
8
+ expectedRevision: number;
9
+ }): WorkspaceSettings;
10
+ }
@@ -0,0 +1,30 @@
1
+ import { InvalidRecordError, RevisionConflictError } from "./project-session-repository.js";
2
+ import { MAX_RETENTION_DAYS } from "../domain/session/storage.js";
3
+ export class WorkspaceSettingsRepository {
4
+ database;
5
+ constructor(database) {
6
+ this.database = database;
7
+ }
8
+ get() {
9
+ return this.database.prepare(`SELECT revision,
10
+ image_retention_days AS imageRetentionDays, history_retention_days AS historyRetentionDays,
11
+ trash_retention_days AS trashRetentionDays FROM workspace_settings WHERE id = 1`).get();
12
+ }
13
+ update(input) {
14
+ for (const value of [input.imageRetentionDays, input.historyRetentionDays, input.trashRetentionDays]) {
15
+ if (!Number.isInteger(value) || value < 1 || value > MAX_RETENTION_DAYS) {
16
+ throw new InvalidRecordError(`Retention must be an integer between 1 and ${MAX_RETENTION_DAYS} days.`);
17
+ }
18
+ }
19
+ return this.database.transaction(() => {
20
+ const current = this.get();
21
+ if (current.revision !== input.expectedRevision)
22
+ throw new RevisionConflictError(input.expectedRevision, current.revision);
23
+ this.database.prepare(`UPDATE workspace_settings SET revision = revision + 1,
24
+ image_retention_days = ?, history_retention_days = ?, trash_retention_days = ? WHERE id = 1`)
25
+ .run(input.imageRetentionDays, input.historyRetentionDays, input.trashRetentionDays);
26
+ return this.get();
27
+ })();
28
+ }
29
+ }
30
+ //# sourceMappingURL=workspace-settings-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-settings-repository.js","sourceRoot":"","sources":["../../src/database/workspace-settings-repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC5F,OAAO,EAAkD,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElH,MAAM,OAAO,2BAA2B;IACT;IAA7B,YAA6B,QAA4B;QAA5B,aAAQ,GAAR,QAAQ,CAAoB;IAAG,CAAC;IAE7D,GAAG;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAwB;;sFAE8B,CAAC,CAAC,GAAG,EAAG,CAAC;IAC7F,CAAC;IAED,MAAM,CAAC,KAAuD;QAC5D,KAAK,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACrG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,kBAAkB,EAAE,CAAC;gBACxE,MAAM,IAAI,kBAAkB,CAAC,8CAA8C,kBAAkB,QAAQ,CAAC,CAAC;YACzG,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,EAAE;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC3B,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK,CAAC,gBAAgB;gBAAE,MAAM,IAAI,qBAAqB,CAAC,KAAK,CAAC,gBAAgB,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC3H,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;oGACwE,CAAC;iBAC5F,GAAG,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACvF,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;QACpB,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import type { CompositionClipboardItem } from "./types.ts";
2
+ /** Validate clipboard nodes with the existing composition schema and invariants. */
3
+ export declare function parseCompositionClipboard(input: unknown): CompositionClipboardItem[] | null;
@@ -0,0 +1,30 @@
1
+ import { copyCompositionItems, createDraft, validateDraft } from "./draft.js";
2
+ /** Validate clipboard nodes with the existing composition schema and invariants. */
3
+ export function parseCompositionClipboard(input) {
4
+ if (!Array.isArray(input) || input.length === 0)
5
+ return null;
6
+ try {
7
+ const draft = createDraft();
8
+ const ids = [];
9
+ for (const entry of input) {
10
+ if (!entry?.item || typeof entry.item.id !== "string")
11
+ return null;
12
+ if (entry.kind === "focus")
13
+ draft.focusPoints.push(entry.item);
14
+ else if (entry.kind === "area")
15
+ draft.areas.push(entry.item);
16
+ else if (entry.kind === "image")
17
+ draft.images.push(entry.item);
18
+ else if (entry.kind === "direction" && !draft.directionLine)
19
+ draft.directionLine = entry.item;
20
+ else
21
+ return null;
22
+ ids.push(entry.item.id);
23
+ }
24
+ return copyCompositionItems(validateDraft(draft), ids);
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../../src/domain/composition/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG9E,oFAAoF;AACpF,MAAM,UAAU,yBAAyB,CAAC,KAAc;IACtD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,WAAW,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACnE,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;gBAAE,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;iBAC1D,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;gBAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;iBACxD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;gBAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;iBAC1D,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,KAAK,CAAC,aAAa;gBAAE,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC;;gBACzF,OAAO,IAAI,CAAC;YACjB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1B,CAAC;QACD,OAAO,oBAAoB,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -14,3 +14,4 @@ export { compositionLayerOrder, reorderCompositionLayers } from "./layers.ts";
14
14
  export { COMPOSITION_PLAN_TYPES, addCompositionPlan, replaceCompositionPlan, removeCompositionPlan, moveCompositionPlans, compositionPlanGeometry, transformCompositionPlan } from "./planning.ts";
15
15
  export { compositionRadialAngles, setCompositionRadialMode, setCompositionRadialRayCount } from "./planning.ts";
16
16
  export { compositionStates, createCompositionState, selectCompositionState, renameCompositionState, reorderCompositionStates, deleteCompositionState, } from "./states.ts";
17
+ export { parseCompositionClipboard } from "./clipboard.ts";
@@ -10,4 +10,5 @@ export { compositionLayerOrder, reorderCompositionLayers } from "./layers.js";
10
10
  export { COMPOSITION_PLAN_TYPES, addCompositionPlan, replaceCompositionPlan, removeCompositionPlan, moveCompositionPlans, compositionPlanGeometry, transformCompositionPlan } from "./planning.js";
11
11
  export { compositionRadialAngles, setCompositionRadialMode, setCompositionRadialRayCount } from "./planning.js";
12
12
  export { compositionStates, createCompositionState, selectCompositionState, renameCompositionState, reorderCompositionStates, deleteCompositionState, } from "./states.js";
13
+ export { parseCompositionClipboard } from "./clipboard.js";
13
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/domain/composition/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,gCAAgC,EAChC,qCAAqC,EACrC,0BAA0B,EAC1B,gBAAgB,EAChB,UAAU,EACV,mBAAmB,EACnB,OAAO,EACP,gBAAgB,EAChB,QAAQ,EACR,aAAa,EACb,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,uBAAuB,EACvB,SAAS,EACT,QAAQ,EACR,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,EACrB,UAAU,EACV,UAAU,EACV,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,sBAAsB,EACtB,UAAU,EACV,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,yBAAyB,EACzB,aAAa,EACb,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,4BAA4B,EAC5B,4BAA4B,EAC5B,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,6BAA6B,EAC7B,2BAA2B,EAC3B,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gCAAgC,EAA4E,MAAM,6BAA6B,CAAC;AACzJ,OAAO,EAAE,yBAAyB,EAAE,+BAA+B,EAAE,4BAA4B,EAAE,+BAA+B,EAAE,6BAA6B,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC7M,OAAO,EACL,0BAA0B,EAC1B,8BAA8B,EAC9B,yBAAyB,EACzB,4BAA4B,EAC5B,8BAA8B,GAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAMhC,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACnM,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAC;AAChH,OAAO,EACL,iBAAiB,EAAE,sBAAsB,EAAE,sBAAsB,EACjE,sBAAsB,EAAE,wBAAwB,EAAE,sBAAsB,GACzE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/domain/composition/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,gCAAgC,EAChC,qCAAqC,EACrC,0BAA0B,EAC1B,gBAAgB,EAChB,UAAU,EACV,mBAAmB,EACnB,OAAO,EACP,gBAAgB,EAChB,QAAQ,EACR,aAAa,EACb,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,uBAAuB,EACvB,SAAS,EACT,QAAQ,EACR,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,EACrB,UAAU,EACV,UAAU,EACV,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,sBAAsB,EACtB,UAAU,EACV,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,yBAAyB,EACzB,aAAa,EACb,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,4BAA4B,EAC5B,4BAA4B,EAC5B,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,6BAA6B,EAC7B,2BAA2B,EAC3B,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gCAAgC,EAA4E,MAAM,6BAA6B,CAAC;AACzJ,OAAO,EAAE,yBAAyB,EAAE,+BAA+B,EAAE,4BAA4B,EAAE,+BAA+B,EAAE,6BAA6B,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC7M,OAAO,EACL,0BAA0B,EAC1B,8BAA8B,EAC9B,yBAAyB,EACzB,4BAA4B,EAC5B,8BAA8B,GAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAMhC,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACnM,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAC;AAChH,OAAO,EACL,iBAAiB,EAAE,sBAAsB,EAAE,sBAAsB,EACjE,sBAAsB,EAAE,wBAAwB,EAAE,sBAAsB,GACzE,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -10,6 +10,8 @@ export declare class CanvasEditHistory<T, Context = undefined> {
10
10
  constructor(initial: T);
11
11
  get canUndo(): boolean;
12
12
  get canRedo(): boolean;
13
+ /** Used by storage protection without publishing editor state or cloning snapshots. */
14
+ retainedDrafts(): T[];
13
15
  record(next: T, beforeContext?: Context, afterContext?: Context | undefined, group?: symbol): boolean;
14
16
  undo(): CanvasHistorySnapshot<T, Context> | null;
15
17
  redo(): CanvasHistorySnapshot<T, Context> | null;
@@ -8,6 +8,10 @@ export class CanvasEditHistory {
8
8
  }
9
9
  get canUndo() { return this.past.length > 0; }
10
10
  get canRedo() { return this.future.length > 0; }
11
+ /** Used by storage protection without publishing editor state or cloning snapshots. */
12
+ retainedDrafts() {
13
+ return [...new Set([this.current, ...[...this.past, ...this.future].flatMap(entry => [entry.before, entry.after])])];
14
+ }
11
15
  record(next, beforeContext, afterContext = beforeContext, group) {
12
16
  if (JSON.stringify(this.current) === JSON.stringify(next))
13
17
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"canvas-edit-history.js","sourceRoot":"","sources":["../../../src/domain/session/canvas-edit-history.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"canvas-edit-history.js","sourceRoot":"","sources":["../../../src/domain/session/canvas-edit-history.ts"],"names":[],"mappings":"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"}