chore(release): package verified 1.8.0 local synchronization candidate

This commit is contained in:
Tera-Dark
2026-10-23 17:05:50 +08:00
parent 784308b67c
commit 7cd2dbbe0d
43 changed files with 881 additions and 273 deletions
+6 -1
View File
@@ -79,12 +79,17 @@ Before manually publishing to the Comfy registry, make sure the version in `pypr
- `gallery_index.sqlite3` uses `PRAGMA user_version`, a `gallery_schema_migrations` audit table, WAL with a busy timeout, and derived tables including `gallery_images`, `gallery_index_meta`, `gallery_images_fts`, `gallery_image_color_family`, and `gallery_image_fingerprints`. The index is rebuildable; if the SQLite file looks corrupt, move it aside as `.corrupt-*` and rebuild instead of touching `gallery_state.json`.
- `gallery_images` caches stable `asset_id` and `content_revision`; identity survives index rebuilds and plugin moves/trash/restore. JSON annotations remain authoritative. Never use duplicate hashes to merge identity or state.
- `gallery_fts_rows` maps paths to FTS rowids independently of image rowids. Update/delete FTS through this map; clear both on rebuild. Never delete FTS by its UNINDEXED path in a per-image loop.
- `gallery_images` includes cached real image dimensions (`image_width`, `image_height`) exposed as optional frontend `width`/`height`. Missing dimensions are filled gradually and must not block listing.
- `gallery_state.json` writes are locked and atomic. Legacy paths never recorded in the catalog retain conservative suffix recovery with a backup and target metadata priority. Paths already known to the catalog (including old namespaces/tombstones) never use suffix recovery: external moves leave old identity/state missing and create a new asset at the new location.
- Pillow decode work for metadata, dimensions, thumbnails, and color analysis goes through `py/gallery/image_safety.py`. `UNIVERSAL_EXTRACTOR_MAX_IMAGE_PIXELS` defaults to `160000000`; `0` or empty leaves Pillow's default untouched. Oversized images should stay listable while derived metadata fails softly with diagnostics.
- Variant fingerprints are rebuildable derived cache only: SHA-256 file hashes back exact duplicate groups, Pillow dHash backs near-duplicate groups, metadata hashes back same prompt/workflow groups, and filename sequence keys back series groups. They must not replace `gallery_state.json` user state.
- `list_images_page()` should query SQLite directly and avoid loading every image into Python.
- HTTP context/folder queries read committed `gallery_folders` snapshots, including empty folders; only the scan lane traverses directory trees. Keep covering color-status and stable pagination indexes when evolving derived schema 10.
- HTTP context/folder queries read committed `gallery_folders` snapshots, including empty folders; only the scan lane traverses directory trees. Derived schema 11 caches image nanosecond mtimes. Preserve covering color-status and stable pagination indexes. Incremental scans collect folder metadata during the same traversal and synchronize identities only for changed observations, without a second physical stat pass. Scoped folder commits must preserve sibling scopes and inaccessible-source snapshots.
- `galleryJobMonitor.ts` is the sole application task observer, shared by status feedback and refresh waiters. `/jobs` returns an optional `revision`; `after` plus `wait_ms` asynchronously waits for changes without a read worker, with two-second fallback for older servers. Do not reinstate per-hook/per-job polling. Link feedback by explicit job ID, never guessed task kind, and reset quiet timing for a new job in a reused scope record.
- Backend completion discovery uses the injected ComfyUI `prompt_queue.get_history` reader, maps only bounded terminal output hints and primes historical IDs at startup. Do not wrap ComfyUI queue or socket methods. API clients' private completion events may never reach ordinary ComfyUI tabs. `/sync-status` is a visible-only one-second hint check; it never scans files or runs tasks. The optional same-origin `/sync-notifications` bridge and BroadcastChannel deduplicate with history discovery by prompt ID. Only validated registered-source scopes can trigger existing refresh jobs. Keep 15-second physical freshness fallback and the auto-sync preference.
- Refresh coalescing must respect output boot instance/sequence: a running scan cannot absorb a newer completion, but queued covering work can be upgraded. New output invalidates earlier shared frontend promises. Keep unchanged context collection identities and reuse the folder `Intl.Collator`; repeated folder sorting during a count update caused measurable main-thread stalls.
- Internal color/dimension backfills allow one running and one coalesced follow-up per kind/signature; never run duplicate same-kind whole-library backfills in parallel. Do not enqueue dimensions for a complete index. Pillow RGBA `get_flattened_data` can return pixel tuples, not scalar channels.
- Reverse image search is `POST /images/search-by-image` (one multipart `image`, <=20 MB, minimum score 55–95). References stay in memory and never enter import/file transactions. Search all enabled sources independently of UI filters, using fingerprint v3 (SHA-256 + EXIF-normalized pHash/dHash/color/aspect). Full-library preparation reuses persistent `fingerprint_assets` with `whole_library: true`; never cap this path to recent items. Reference decode, ranking and backfill stay in analysis; ordinary read workers remain available. Results include asset ID and validated full path. Refuse stale physical nanosecond mtimes. Clipboard images may be re-encoded and should match visually, not necessarily by file hash. Keep the modal lazy, abort obsolete requests, and preserve it across resize.
- `get_gallery_context()` should use SQLite aggregates for source counts, Pin count, subfolders, `subfolder_details`, and move targets; do not reintroduce full image-index reads for context. A forced context refresh must not rescan in `list_boards()`; board queries read only actual members, not every persisted state path.
- `_sync_image_state_to_index_db()` compares saved state to indexed fields in <=900-bind batches and only rewrites changed rows and FTS helpers, including removed-state clearing. Fingerprint/prewarm path queries share `sqlite_batch.py` bounds. The live freshness scan still stats files for in-place edits, hashes in deterministic traversal order, and limits its scope cache to 32; keep the Settings switch to disable live polling on very large/slow sources.
+34
View File
@@ -1,5 +1,39 @@
# Changelog
## 1.8.0 - 2026-10-23
### 同步与效率
- 单次遍历同时更新图片和目录,只登记变化的资源身份,缓存纳秒时间并加速全文索引的局部更新。
- 后端发现 API 客户端的工作流完成记录,不再依赖普通 ComfyUI 网页收到事件;明确输出目录时优先同步相关范围。
- 任务中心与刷新等待共用变化通知,避免重复轮询、重复提示和旧任务隐藏状态影响新任务。
- 复用未变化的卡片、目录与移动目标,复用自然排序器消除目录刷新热点;元数据回填保持布局,保护请求期间新建的选择。
### 可靠性修复
- 修复新版 Pillow 的颜色像素解析错误,合并后台分析排队,不再创建空尺寸分析任务。
- 新完成事件不会被更早的扫描吞掉;重复输出只保留必要的后续同步。
- 保留单图、批量文件事务、资源 ID、图源权限和 LoRA Manager 行为。
### 候选版与升级
- 此版本为本地验证候选,尚未自动推送 GitHub 或发布 Registry。
- 部署前停止 ComfyUI,备份用户数据和未解决日志,完整替换代码及 dist,重启并刷新页面。
- 目录库和用户 JSON 不迁移;派生索引升级到 schema 11,不要删除 catalog.sqlite3 或恢复日志。
### Synchronization and performance
- Collect image and folder snapshots in one traversal, register only changed identities, cache nanosecond mtimes, and update FTS rows through indexed row IDs.
- Discover API workflow completions on the backend without requiring an ordinary ComfyUI page to receive the event; reconcile known output scopes first.
- Share one change-driven task monitor between feedback and refresh waiters, avoiding duplicate polling, duplicate messages and stale hidden state.
- Reuse unchanged cards, folders and move targets plus one natural-sort collator, keep metadata backfill stable, and protect in-flight selections.
### Reliability fixes
- Fix modern Pillow RGBA pixel extraction, coalesce analysis backlogs, and avoid empty dimension jobs.
- Do not acknowledge new workflow output through an older scan; merge only the necessary follow-up reconciliation.
- Preserve file journals, resource IDs, registered-source permissions and LoRA Manager behavior.
### Candidate and upgrade notes
- This is a local validation candidate; no automatic GitHub push or Registry publication.
- Stop ComfyUI, back up user data and unresolved journals, replace code and the complete dist, restart, and refresh the pages.
- Catalog identities and user JSON remain unchanged. The derived index upgrades to schema 11; never delete catalog.sqlite3 or recovery evidence.
## 1.7.0 - 2026-10-16
### 主要变化
+9
View File
@@ -1,5 +1,14 @@
# ComfyUI Universal Extractor
## 1.8.0 · 同步与性能修复(本地候选)
- 图片与目录只遍历一次,只同步变化的资源身份;加速全文索引局部更新。
- 后端发现 API 跑图完成记录,图库不再依赖另一个 ComfyUI 网页收到事件。
- 刷新与任务中心共用变化通知,修复重复提示、陈旧扫描吞掉新图和新版 Pillow 颜色分析失败。
- 保持资源 ID、用户 JSON、文件事务和既有 ComfyUI/LoRA Manager 行为。
[排查、接口与验收说明](docs/synchronization-performance.zh-CN.md)。这是未发布候选,部署前须停止 ComfyUI,完整替换代码及 dist,再重启并刷新页面。
## 1.7.0 · 自动同步与日常体验优化
- ComfyUI 工作流完成后自动同步图库,保留整理位置;可见时每 15 秒检查遗漏。
+8 -8
View File
@@ -62,7 +62,7 @@ Runtime gallery data is stored under `data/`. User state remains JSON-backed, wh
- `gallery_sources.json`: configured gallery sources.
- `catalog.sqlite3`: authoritative identity and jobs (`assets`, `jobs`, `catalog_meta`, `catalog_migrations`), independent schema/backup, never deleted as a cache. Asset IDs survive index rebuilds and plugin moves/trash/restoration; duplicate content and external moves do not automatically merge identities.
- `upload_journal/`: session-owned temporary upload recovery records.
- `gallery_index.sqlite3`: derived image index, schema 10 with cached asset IDs/content revisions, versioned fingerprints, committed `gallery_folders` snapshots and covering/stable-order indexes. Fingerprints include the search descriptor and physical nanosecond mtime. Hot context requests never traverse directories; initial upgrades may discover empty folders asynchronously after serving existing image rows.
- `gallery_index.sqlite3`: derived image index, schema 11 with cached asset IDs/content revisions and physical nanosecond mtimes, versioned fingerprints, committed `gallery_folders` snapshots and covering/stable-order indexes. Fingerprints also include the search descriptor and physical nanosecond mtime. Hot context requests never traverse directories; initial upgrades may discover empty folders asynchronously after serving existing image rows.
- `library_summary_cache.json`: derived prompt-library counts keyed by filename, file size, and `mtime_ns`; it is runtime state, not a user library.
- `thumb_cache/`: generated thumbnails.
- `trash/`: plugin-managed trash storage.
@@ -75,6 +75,7 @@ The important tables are:
- `gallery_index_meta`: source signature, built timestamp, color index version, state digest, auxiliary-table version, and FTS availability.
- `gallery_schema_migrations`: append-only migration audit rows recording schema version bumps.
- `gallery_images_fts`: FTS5 search table for filename/path/title/category/notes text. If FTS5 is unavailable, search falls back to the existing LIKE expression.
- `gallery_fts_rows`: derived B-tree mapping from path to FTS rowid. Incremental updates delete FTS entries by rowid, avoiding a full scan per UNINDEXED path. Initialize existing rowids once; clear the map together with a full FTS rebuild, never reuse the unrelated `gallery_images` rowid.
- `gallery_image_color_family`: normalized color-family rows used by color filtering, while legacy color fields remain in `gallery_images` for response compatibility.
- `gallery_image_fingerprints`: local-only derived fingerprints used by variant organization. It stores file hashes, lightweight visual dHash values, prompt/workflow metadata hashes, filename sequence keys, and soft-failure diagnostics keyed by `relative_path`.
@@ -86,14 +87,13 @@ HTTP image listing prepares its index outside the read pool, then uses `list_ima
The first gallery load should use the existing SQLite index without `force_refresh=true`. Forced image refresh is reserved for user-triggered refreshes and freshness-detected changes, where the backend can prefer incremental sync over a full rebuild.
Automatic gallery sync is a two-step flow:
Automatic gallery sync shares the existing durable refresh coordinator with manual refresh. A visible gallery reads `/api/sync-status` once per second for bounded terminal output hints, discovered through the injected ComfyUI history reader or same-origin bridge. Browser broadcasts are an optional fast path. Prompt IDs deduplicate these transports; one-second bursts merge to a common registered-source scope. API clients with private sockets do not require an ordinary ComfyUI page. Startup history is primed without replaying old runs.
1. The frontend calls `/universal_gallery/api/images/freshness` for the current folder/source view while the page is visible.
2. If the fingerprint changed, the frontend calls `/api/images?force_refresh=true`; the backend performs incremental index sync when the existing DB signature is current.
Known output goes straight to a `refresh_index` job without an extra physical freshness scan. Refresh jobs capture the output boot instance/sequence: a running scan predating new output cannot satisfy it, while a queued covering scan can absorb the latest sequence. Frontend shared promises are invalidated by new output. Visible-only 15-second physical freshness remains the fallback for external file edits/old hosts; acknowledge its fingerprint only after successful synchronization. Initial indexed browsing is followed by an asynchronous reconciliation. The auto-sync preference controls all automatic paths.
Incremental sync scans only the affected scope, upserts new or modified files, deletes missing files, and queues only changed paths for color backfill. Full rebuild remains the cold-start and repair fallback. Scan commits validate the source mutation generation; inaccessible subtrees do not become deletion signals. Identity is registered in bounded batches, independently of decoding; failed analysis remains soft.
`get_gallery_context()` avoids full image reads. It derives source counts, pinned count, subfolders, and move targets from SQLite aggregates plus lightweight directory discovery. Its forced refresh runs the index sync once; board summaries reuse the resulting database. `list_boards()` selects only actual indexed board members, ordered by creation time for automatic covers, rather than building a potentially over-limit `IN` query for every pinned/note state path.
`get_gallery_context()` avoids full image reads and directory discovery. It derives source counts, pinned count, subfolders, and move targets from committed SQLite snapshots. Its forced refresh awaits the shared index task; board summaries reuse the resulting database. `list_boards()` selects only actual indexed board members, ordered by creation time for automatic covers, rather than building a potentially over-limit `IN` query for every pinned/note state path. Frontend context snapshots retain unchanged collection identities so count updates do not invalidate folder/move calculations. Folder sorting reuses an `Intl.Collator` and one per-sort key cache instead of parsing pins/dates and constructing locale options for each comparison. Only the active tree or list presentation is built.
The freshness endpoint still stats in-scope image files (file-in-place edits must be detectable); it now hashes in deterministic directory order without retaining the entire image record list. The in-process cache retains the 32 most recently visited scopes. Disabling live gallery refresh from Settings is appropriate for exceptionally large or slow network-mounted sources.
@@ -195,9 +195,9 @@ Explicit gallery refresh waits for both context and image/trash requests to fini
- `GET /universal_gallery/api/release-notes?locale=zh-CN|en`: installed `current_version`, `source`, and up to five `releases`, current first. Structured bilingual files ship in `docs/releases`; older CHANGELOG sections remain readable offline. `update-status` preserves old fields and adds `check_status` (`checked`, `cached`, `failed`, `preview`), `notes_source`, `last_success_at`. Remote calls share one in-flight check and cache successful results for 30 minutes. Safe Markdown uses React text, never raw HTML or remote images.
- `GET /universal_gallery/api/images/locate?asset_id=...&limit=48&sort_by=created_at&sort_order=desc`: returns `asset_id`, `relative_path`, canonical parent `subfolder`, one-based `page`, zero-based page `index`, and `limit`. A legacy `relative_path` can replace the ID. Source resolution, namespace checks, same-origin guards and the list's stable SQL ordering apply. The folder page includes descendants, matching ordinary list semantics.
- ComfyUI extension listens to `execution_success`, terminal `executing`, `execution_error` and `execution_interrupted`. Channel `universal-extractor-gallery-events` carries `{version:1,type:"outputs-changed",prompt_id:string}` (1–200 chars); both ends deduplicate a bounded 256 prompt IDs. The current terminal event does not provide a trusted complete source list, so it conservatively refreshes all registered sources. No arbitrary path is accepted from an event.
- `useGalleryAutoSync` owns dirty generations, visible-only fallback checks, first-baseline reconciliation and 2s/10s retry for transient failures. `gallerySync` coalesces persistent scan jobs across manual and automatic callers, cancelling polling when all subscribers leave; submitted backend jobs retain their normal durable lifecycle. A new event during a scan requires a trailing sync. Fingerprints are acknowledged only after success.
- The global task observer polls every 2 seconds while visible and active jobs exist; startup/visibility/activity events discover jobs without permanent idle polling. UI feedback retains errors/active operations and at most 50 completed session records. Only registered cancellable scan/fingerprint jobs expose cancellation. Ordinary toast history is not persisted.
- ComfyUI extension listens to `execution_success`, terminal `executing`, `execution_error` and `execution_interrupted`. Channel `universal-extractor-gallery-events` carries `{version:1,type:"outputs-changed",prompt_id:string}` (1–200 chars); both ends deduplicate a bounded 256 prompt IDs. The bridge now relays bounded output folder hints through `/sync-notifications`; server validation maps them to registered-source scopes. Unknown scope falls back to all registered sources. No arbitrary path is accepted from an event.
- `useGalleryAutoSync` owns dirty generations, visible-only fallback checks, one initial all-source reconciliation even when an old folder is open, and 2s/10s retry for transient failures. `gallerySync` coalesces persistent scan jobs across manual and automatic callers, cancelling polling when all subscribers leave; submitted backend jobs retain their normal durable lifecycle. A new event during a scan requires a trailing sync. Fingerprints are acknowledged only after success.
- The global task observer and refresh waiters share one `galleryJobMonitor` subscription. Modern servers return a revision and wake one asynchronous long poll on task changes; older servers retain the visible-only two-second fallback. Waiting holds no query worker/SQLite transaction; no active jobs means no continuing task polling. UI feedback retains errors/active operations and at most 50 completed session records. Feedback links only explicit job IDs. Each new job resets its scope record's quiet timer and hidden state. Ordinary toast history is not persisted.
- `UiPreferences.interfaceDensity` is optional for older callers and normalizes to `standard` or `compact`. It never changes saved column count. Shared dimensions set card gaps to 14/10px and estimated chrome to 50/44px, respectively. Image search retains its in-memory session through detail/locate; explicit close releases URLs and requests.
- Virtual masonry measures its scroll margin below the toolbar, uses fixed estimated card heights, and shares actual cached lane positions with offscreen box selection. Media ratios remain stable for existing content versions during the loaded page; a new content version or page entry can use newly available dimensions. Width/density changes restore the visible asset and pixel offset. Browser anchoring is disabled for the virtual surface to avoid double correction. Thumbnail load/retry state is keyed by source URL/content version; cached complete images are recognized at attachment, prefetch waits for decode and does not clear successful sources. Placeholders are static and photos do not fade in on every mount. Legacy trash previews reserve a fixed 1/1.36 frame with `object-fit:contain` rather than growing CSS columns after decode.
- Trash batches execute sequentially, stop at the first failed commit, retain unfinished selections and perform a single snapshot reload without forcing a source scan. Each file retains the existing recovery journal semantics. Refresh deduplication ignores the enqueue-time catalog generation: each attempt captures a fresh generation after acquiring the scan lock and validates it before commit. I/O/conflict retries remain bounded to three attempts in the same task. A terminal auto-sync failure blocks timer/focus retries for that scope and dirty generation; a user retry or new output permits another attempt. Historical refresh jobs coalesce into one UI record per scope rather than restoring a stack of identical failures. On scheduler startup, equivalent queued legacy refresh jobs retain only the newest executable request; older records become cancelled with a `superseded_by` result. No file-operation journal, asset identity or versioned analysis work is consolidated.
+42
View File
@@ -0,0 +1,42 @@
{
"version": "1.8.0",
"date": "2026-10-23",
"locales": {
"zh-CN": [
{"title": "同步与效率", "items": [
"单次遍历同时更新图片和目录,只登记变化的资源身份,缓存纳秒时间并加速全文索引的局部更新。",
"后端发现 API 客户端的工作流完成记录,不再依赖普通 ComfyUI 网页收到事件;明确输出目录时优先同步相关范围。",
"任务中心与刷新等待共用变化通知,避免重复轮询、重复提示和旧任务隐藏状态影响新任务。",
"复用未变化的卡片、目录与移动目标,复用自然排序器消除目录刷新热点;元数据回填保持布局,保护请求期间新建的选择。"
]},
{"title": "可靠性修复", "items": [
"修复新版 Pillow 的颜色像素解析错误,合并后台分析排队,不再创建空尺寸分析任务。",
"新完成事件不会被更早的扫描吞掉;重复输出只保留必要的后续同步。",
"保留单图、批量文件事务、资源 ID、图源权限和 LoRA Manager 行为。"
]},
{"title": "候选版与升级", "items": [
"此版本为本地验证候选,尚未自动推送 GitHub 或发布 Registry。",
"部署前停止 ComfyUI,备份用户数据和未解决日志,完整替换代码及 dist,重启并刷新页面。",
"目录库和用户 JSON 不迁移;派生索引升级到 schema 11,不要删除 catalog.sqlite3 或恢复日志。"
]}
],
"en": [
{"title": "Synchronization and performance", "items": [
"Collect image and folder snapshots in one traversal, register only changed identities, cache nanosecond mtimes, and update FTS rows through indexed row IDs.",
"Discover API workflow completions on the backend without requiring an ordinary ComfyUI page to receive the event; reconcile known output scopes first.",
"Share one change-driven task monitor between feedback and refresh waiters, avoiding duplicate polling, duplicate messages and stale hidden state.",
"Reuse unchanged cards, folders and move targets plus one natural-sort collator, keep metadata backfill stable, and protect in-flight selections."
]},
{"title": "Reliability fixes", "items": [
"Fix modern Pillow RGBA pixel extraction, coalesce analysis backlogs, and avoid empty dimension jobs.",
"Do not acknowledge new workflow output through an older scan; merge only the necessary follow-up reconciliation.",
"Preserve file journals, resource IDs, registered-source permissions and LoRA Manager behavior."
]},
{"title": "Candidate and upgrade notes", "items": [
"This is a local validation candidate; no automatic GitHub push or Registry publication.",
"Stop ComfyUI, back up user data and unresolved journals, replace code and the complete dist, restart, and refresh the pages.",
"Catalog identities and user JSON remain unchanged. The derived index upgrades to schema 11; never delete catalog.sqlite3 or recovery evidence."
]}
]
}
}
+73
View File
@@ -0,0 +1,73 @@
# 1.8.0 同步与性能修复:本地候选
基于已发布 1.7.0 的后续修复,仍保留 React/Vite、aiohttp、SQLite、用户 JSON、资源 ID 与文件事务。此文记录 1.8.0 候选源码的行为;不表示正在运行的 ComfyUI 已部署,也不替代正式版本发布说明。
## 排查结论
- 实际安装版本为 1.7.0,检查时已有 14,317 条在线索引。近期刷新任务从入队到完成约 6–20 秒;历史记录中还存在数分钟等待。这包含排队时间,不能作为单独的扫描耗时。
- 增量刷新仍重复遍历目录并两次核对全库身份。当前改为一次目录遍历,同时记录图片和空目录;提交只处理变化的身份,复用已读取的纳秒 mtime。无变化不改写目录库,目录局部更新不删除其他范围快照。
- 颜色分析记录中有 348 次像素类型错误。修正新版 Pillow 返回 RGBA 元组时的解析;尺寸完整时不再建立空任务。同种分析只运行一项,额外变化合并为一项后续任务。
- 刷新等待与全局状态中心原本分别读取任务状态;设置和错误路径还会建立额外提示。现在共用一个监听器,以真实 job ID 关联记录,不再根据相同任务类型猜测归属。
- 真实 ComfyUI 的 API 工作流完成事件可能只发给该 API 客户端;普通网页和 BroadcastChannel 都收不到。新后端从注入的 ComfyUI 完成历史读取紧凑的输出范围,不读取工作流模型参数,也不包裹或修改宿主方法。
## 同步和反馈
工作流完成历史、HTTP 桥接和浏览器广播按 prompt ID 去重。可见且启用自动同步的图库每秒读取轻量通知,合并一秒内的输出范围后提交一个公共父范围刷新。通知检查不遍历磁盘、不创建任务;初次历史只建基线。关闭同步或隐藏页面后暂停检查;外部文件和旧宿主仍保留 15 秒 freshness 兜底。
明确的完成通知不再先排一次 freshness 扫描。索引成功后并行读取页面与目录;相同快照复用原卡片和数组,不重建瀑布流。只有最新页、靠近顶部且没有选择/拖动/详情时自动替换;其他位置保留浏览状态。用户在请求期间新建的选择也受保护。元数据回填本身不显示“有新图片”。
首次校准覆盖所有已注册来源,避免首次打开旧文件夹时遗漏其他目录的旧完成记录。后续已知输出才缩小到对应范围。任务记录内部的输出进程标识和序号;比新输出更早启动的扫描不能替它确认成功,尚未执行的覆盖任务则可吸收最新序号。前端也会使较早的共享等待失效,保留必要的后续同步。
前端采样发现目录排序是插入新图时的主线程热点。当前复用一个自然排序器,每次排序只解析一次各目录的 Pin、日期和修改时间;只计算正在使用的树形或列表视图。未变化的目录、图版和移动目标保留对象引用。派生全文索引另用 `gallery_fts_rows` 保存路径到 FTS rowid 的映射,避免每个变化图片都按未索引的路径字段全表删除;全文重建时同时清空映射。
状态中心与刷新等待共用任务观察器。新服务通过变化版本唤醒异步等待,旧服务降级为每两秒查询;取消订阅不取消服务端工作,隐藏页面中断 HTTP,失败退避。浮层仍最多三条,后台维护延迟两秒显示,失败保留。新的任务不继承上一任务已经隐藏的状态或旧的等待时间。
## 兼容接口
| 接口 | 行为与限制 |
| --- | --- |
| `GET /universal_gallery/api/sync-status?since=N` | 只读通知快照,返回进程 instance、sequence 和事件;非负安全整数,最多保留 256 个 prompt,不写用户状态,不扫描文件 |
| `POST /universal_gallery/api/sync-notifications` | 同源 JSON:`version:1`、1–200 字符 `prompt_id`、最多 128 条 `outputs`;只消费 output/input/temp 的相对 subfolder,验证注册来源和路径边界,最多 32 个范围 |
| `GET /universal_gallery/api/jobs` | 原字段保留,新增 `revision`;可传 `after` 和 `wait_ms`(0–20000),最多 32 个异步等待者,不占查询线程 |
只公开原有 `refresh_index` / `fingerprint_assets` 任务类型。新增通知不接收函数、命令或任意磁盘路径。来源离线、目录库故障和文件事务恢复门禁仍有效。目录库身份与用户 JSON 不迁移;派生索引通过现有迁移机制增加纳秒时间缓存。
## 验证方式和边界
运行 `scripts/verify.ps1`,并单独运行固定版本 LoRA Manager 集成。新增用例覆盖单次扫描/变化身份计数、同秒编辑、父范围合并、源权限、完成历史去重、无网页 API 工作流、共享任务监听、异步等待取消、陈旧 POST 响应和提示生命周期。
扫描优化阶段使用 `scripts/compare_refresh.py --baseline-root <1.7源码> --source <隔离PNG目录> --output artifacts/comparison.json`,在同一个只读物理目录上交替 A/B、B/A 运行,两边使用不同临时目录库。14,317 张生成 PNG,5 次预热、30 次采样,P95 使用 nearest-rank。旧基线为 `d83cba9`。此记录测量单次扫描和身份增量改造,早于之后的 FTS rowid、无用 URL 构造移除和前端排序改造;不是最终源码逐项重新采样的结果。
| 操作 | 1.7 P50 / P95 | 扫描优化阶段 P50 / P95 |
| --- | --- | --- |
| 全库刷新 | 2508 / 4275ms | 932 / 1876ms |
| 50 张图片的局部目录刷新 | 1370 / 3982ms | 70 / 241ms |
| 热列表查询 | 42 / 50ms | 42 / 49ms |
| 热上下文查询 | 122 / 138ms | 122 / 141ms |
全库无变化时,身份登记从每次 28,634 项降为 0,目录访问从 578 降为 289;局部目录从 290 降为 1。两个普通查询的 P95 均在基线 120% 内。独立树的早期比较受 Windows 文件创建及并发负载影响,保留原始日志但不用于上述结论。`benchmark_refresh.py` 仍可单独创建样本;配对比较优先使用同一来源的 `compare_refresh.py`。
这些有效 PNG 均为 64×64 的相同生成小图;扫描比较关闭解码和分析。样本创建、首次建库不计入热刷新。它们不代表真实大原图解码、GPU 跑图耗时或任意磁盘吞吐。原始记录和中间失败日志保存在未提交的 `artifacts/`;可携带的汇总为 [1.8.0-sync.json](validation/1.8.0-sync.json)。
最终真实完成链路在独立 CPU ComfyUI、独立 input/output/data 中使用 LoadImage→SaveImage,不使用模型、不访问用户图源。普通 ComfyUI 网页关闭,通过无 `client_id` 的 API 连续提交 30 轮,开始时索引 14,399 项(输出 14,398、输入 1)。30 张新图全部入库、显示并解码,无手动刷新;服务端这段时间恰有 30 个成功刷新任务、0 个失败刷新。完成时间取宿主 `execution_success` 历史时间戳,显示取 DOM 新图片到达,解码取 `complete && naturalWidth > 0`。
| 最终候选指标 | P50 | P95 | 最大 |
| --- | --- | --- | --- |
| 完成→入库 | 2019ms | 2594ms | 2621ms |
| 完成→卡片显示 | 2232ms | 2811ms | 2895ms |
| 完成→缩略图解码 | 2324ms | 2893ms | 2995ms |
这 30 轮中,PerformanceObserver 记录的 ≥50ms 主线程长任务为 **0**,浮动任务卡最多 **0**(短维护任务保持安静)。早期未完成目录排序优化的两组采样仍有每轮 50–176ms 的长任务,已保留作为定位证据。一次无变化的手动刷新只发出 1 次扫描 POST、1 次 context GET 和 1 次分页 GET;24 个已挂载图片框均无卸载/新增,未重建照片节点。长轮询进度请求、轻量完成通知和缩略图预热是不同用途,不应算作额外扫描。
| 验收范围 | 结果 |
| --- | --- |
| 后端、compileall | 171 项通过,编译通过 |
| 前端、类型、lint、i18n | 255 项通过,961 个翻译键审计通过 |
| ComfyUI 桥接 | LoRA 11 项、完成通知 2 项通过 |
| 固定上游 LoRA Manager | `0a262cbe0c0a773c174e6b4570c44671d6e2b9a3` 的 6 项通过 |
| 依赖、构建、dist、版本 | 0 个审计漏洞,26 个静态引用审计及 1.8.0 元数据检查通过 |
| 自动同步与浏览保护 | API 独立客户端、去重、进行中再次完成、取消/503/失败阻断、旧目录首次校准、共享监听及选择保护通过 |
完整验证日志为 `artifacts/sync-core-1.8-final-verify.log`;最后的目录排序改造重新运行完整前端验证,日志为 `artifacts/sync-core-frontend-final.log`。最终宿主/浏览器逐轮记录为 `artifacts/sync-core-comfy-acceptance.json` 和 `artifacts/sync-core-browser-candidate.json`。无旧测试删除或跳过。真实用户原图库只做只读检查;安装目录仍运行旧版,本记录不宣称已经完成用户安装环境的部署后验收。
部署必须停止 ComfyUI,备份数据和未解决日志,完整替换源码及 dist,重启并刷新页面。不要覆盖用户 `data`,不要删除目录库或恢复日志。未收到明确发布指令时不推送、不改旧标签、不提交 Registry。
+156
View File
@@ -0,0 +1,156 @@
{
"version": "1.8.0",
"date": "2026-10-23",
"status": "local_candidate",
"environment": {
"os": "Windows",
"python": "3.12.8",
"comfy_mode": "isolated CPU/offline, own plugin only",
"real_user_sources_modified": false
},
"scan_comparison": {
"baseline_commit": "d83cba9",
"candidate_stage": "single-walk/changed-identity stage; before later FTS-rowid and unused-URL refinements",
"images": 14317,
"warmups": 5,
"samples": 30,
"method": "A/B and B/A alternating on same read-only generated 64x64 PNG tree; separate temp catalogs; decoding and analysis disabled",
"metrics": {
"all": {
"before": {
"p50_ms": 2508.3912000000055,
"p95_ms": 4274.6564999997645,
"counts": {
"ms": 4274.6564999997645,
"observed_assets": 28634,
"directory_visits": 578
}
},
"after": {
"p50_ms": 931.7863999995097,
"p95_ms": 1875.876299999618,
"counts": {
"ms": 1290.765800000372,
"observed_assets": 0,
"directory_visits": 289
}
}
},
"folder": {
"before": {
"p50_ms": 1369.7821499999918,
"p95_ms": 3981.710899999598,
"counts": {
"ms": 3122.6615999994465,
"observed_assets": 28634,
"directory_visits": 290
}
},
"after": {
"p50_ms": 70.18509999988964,
"p95_ms": 240.87650000001304,
"counts": {
"ms": 182.15049999980693,
"observed_assets": 0,
"directory_visits": 1
}
}
},
"list": {
"before": {
"p50_ms": 42.11390000000392,
"p95_ms": 49.68800000006013,
"counts": {
"ms": 36.489199999778066,
"observed_assets": 0,
"directory_visits": 0
}
},
"after": {
"p50_ms": 42.37534999992931,
"p95_ms": 48.76590000003489,
"counts": {
"ms": 36.44590000021708,
"observed_assets": 0,
"directory_visits": 0
}
}
},
"context": {
"before": {
"p50_ms": 122.03900000031354,
"p95_ms": 138.1562999995367,
"counts": {
"ms": 86.55679999992572,
"observed_assets": 0,
"directory_visits": 0
}
},
"after": {
"p50_ms": 122.00475000008737,
"p95_ms": 140.8638000002611,
"counts": {
"ms": 138.71910000034404,
"observed_assets": 0,
"directory_visits": 0
}
}
}
}
},
"final_comfy_workflow": {
"baseline_all_sources": 14399,
"rounds": 30,
"displayed": 30,
"decoded": 30,
"long_tasks_50ms": 0,
"max_floating_cards": 0,
"index_latency_ms": {
"p50": 2019.0,
"p95": 2594,
"max": 2621
},
"display_latency_ms": {
"p50": 2231.5,
"p95": 2811,
"max": 2895
},
"loaded_latency_ms": {
"p50": 2324.0,
"p95": 2893,
"max": 2995
},
"workflow": "API LoadImage -> SaveImage, no client_id, no ordinary ComfyUI page",
"refresh_jobs_succeeded": 30,
"refresh_jobs_failed": 0,
"source_hint_check_ms": 1000,
"coalescing_ms": 1000
},
"manual_refresh": {
"scan_post_requests": 1,
"context_get_requests": 1,
"page_get_requests": 1,
"mounted_image_shells_removed": 0,
"mounted_image_shells_added": 0,
"max_floating_cards": 0
},
"verification": {
"python_tests": 171,
"frontend_tests": 255,
"lora_bridge_tests": 11,
"completion_bridge_tests": 2,
"pinned_lora_upstream_tests": 6,
"security_vulnerabilities": 0,
"i18n_keys": 961,
"dist_references": 26,
"typecheck": true,
"lint": true,
"compileall": true,
"build": true,
"release_metadata": true
},
"limits": [
"Generated tiny images measure metadata scanning and synchronization, not full-resolution decoding or GPU throughput.",
"Real installed plugin remains 1.7.0 until ComfyUI is stopped and candidate code/dist are deployed."
]
}
+66 -66
View File
@@ -1,71 +1,71 @@
{
"_I18nProvider-cZczvL7B.js": {
"file": "assets/I18nProvider-cZczvL7B.js",
"_I18nProvider-DEfiLn3N.js": {
"file": "assets/I18nProvider-DEfiLn3N.js",
"name": "I18nProvider"
},
"_boxes--0yaGReH.js": {
"file": "assets/boxes--0yaGReH.js",
"_boxes-GTo8RUgH.js": {
"file": "assets/boxes-GTo8RUgH.js",
"name": "boxes",
"imports": [
"_I18nProvider-cZczvL7B.js"
"_I18nProvider-DEfiLn3N.js"
]
},
"_copy-Dp5BPX7F.js": {
"file": "assets/copy-Dp5BPX7F.js",
"_copy-CoBcvjUE.js": {
"file": "assets/copy-CoBcvjUE.js",
"name": "copy",
"imports": [
"_I18nProvider-cZczvL7B.js"
"_I18nProvider-DEfiLn3N.js"
]
},
"_download-Bn9yrFnw.js": {
"file": "assets/download-Bn9yrFnw.js",
"_download-CSrVSIDj.js": {
"file": "assets/download-CSrVSIDj.js",
"name": "download",
"imports": [
"_I18nProvider-cZczvL7B.js"
"_I18nProvider-DEfiLn3N.js"
]
},
"_image-plus-C2AXraiD.js": {
"file": "assets/image-plus-C2AXraiD.js",
"_image-plus-C8PYGAuQ.js": {
"file": "assets/image-plus-C8PYGAuQ.js",
"name": "image-plus",
"imports": [
"_I18nProvider-cZczvL7B.js"
"_I18nProvider-DEfiLn3N.js"
]
},
"_refresh-ccw-CfRNSMuu.js": {
"file": "assets/refresh-ccw-CfRNSMuu.js",
"_refresh-ccw-bV8xzBtF.js": {
"file": "assets/refresh-ccw-bV8xzBtF.js",
"name": "refresh-ccw",
"imports": [
"_I18nProvider-cZczvL7B.js"
"_I18nProvider-DEfiLn3N.js"
]
},
"_save-DFeVqt65.js": {
"file": "assets/save-DFeVqt65.js",
"_save-DQG0Zsqx.js": {
"file": "assets/save-DQG0Zsqx.js",
"name": "save",
"imports": [
"_I18nProvider-cZczvL7B.js"
"_I18nProvider-DEfiLn3N.js"
]
},
"_sparkles-Bu6QFW9h.js": {
"file": "assets/sparkles-Bu6QFW9h.js",
"_sparkles-elJjV352.js": {
"file": "assets/sparkles-elJjV352.js",
"name": "sparkles",
"imports": [
"_I18nProvider-cZczvL7B.js"
"_I18nProvider-DEfiLn3N.js"
]
},
"_wand-sparkles-C_uDcQtj.js": {
"file": "assets/wand-sparkles-C_uDcQtj.js",
"_wand-sparkles-262S5lQG.js": {
"file": "assets/wand-sparkles-262S5lQG.js",
"name": "wand-sparkles",
"imports": [
"_I18nProvider-cZczvL7B.js"
"_I18nProvider-DEfiLn3N.js"
]
},
"index.html": {
"file": "assets/index-DWGqjUOp.js",
"file": "assets/index-CKow1Wte.js",
"name": "index",
"src": "index.html",
"isEntry": true,
"imports": [
"_I18nProvider-cZczvL7B.js"
"_I18nProvider-DEfiLn3N.js"
],
"dynamicImports": [
"src/components/gallery/DualFolderWorkspace.tsx",
@@ -83,40 +83,40 @@
]
},
"src/components/gallery/BoardShareModal.tsx": {
"file": "assets/BoardShareModal-DrZ4X-PT.js",
"file": "assets/BoardShareModal-CNeucGS-.js",
"name": "BoardShareModal",
"src": "src/components/gallery/BoardShareModal.tsx",
"isDynamicEntry": true,
"imports": [
"_I18nProvider-cZczvL7B.js",
"_copy-Dp5BPX7F.js",
"_download-Bn9yrFnw.js",
"_I18nProvider-DEfiLn3N.js",
"_copy-CoBcvjUE.js",
"_download-CSrVSIDj.js",
"index.html"
]
},
"src/components/gallery/DualFolderWorkspace.tsx": {
"file": "assets/DualFolderWorkspace-Dl7afjQz.js",
"file": "assets/DualFolderWorkspace-D8G_E4xN.js",
"name": "DualFolderWorkspace",
"src": "src/components/gallery/DualFolderWorkspace.tsx",
"isDynamicEntry": true,
"imports": [
"_I18nProvider-cZczvL7B.js",
"_refresh-ccw-CfRNSMuu.js",
"_I18nProvider-DEfiLn3N.js",
"_refresh-ccw-bV8xzBtF.js",
"index.html",
"src/components/gallery/MetadataViewerModal.tsx"
]
},
"src/components/gallery/ImageDetailModal.tsx": {
"file": "assets/ImageDetailModal-DaLI34MR.js",
"file": "assets/ImageDetailModal-Bv5UXO0m.js",
"name": "ImageDetailModal",
"src": "src/components/gallery/ImageDetailModal.tsx",
"isDynamicEntry": true,
"imports": [
"_I18nProvider-cZczvL7B.js",
"_sparkles-Bu6QFW9h.js",
"_boxes--0yaGReH.js",
"_copy-Dp5BPX7F.js",
"_save-DFeVqt65.js",
"_I18nProvider-DEfiLn3N.js",
"_boxes-GTo8RUgH.js",
"_copy-CoBcvjUE.js",
"_save-DQG0Zsqx.js",
"_sparkles-elJjV352.js",
"index.html"
],
"css": [
@@ -124,13 +124,13 @@
]
},
"src/components/gallery/ImageSearchModal.tsx": {
"file": "assets/ImageSearchModal-DZp0PXl_.js",
"file": "assets/ImageSearchModal-CH4E-SEH.js",
"name": "ImageSearchModal",
"src": "src/components/gallery/ImageSearchModal.tsx",
"isDynamicEntry": true,
"imports": [
"_I18nProvider-cZczvL7B.js",
"_image-plus-C2AXraiD.js",
"_I18nProvider-DEfiLn3N.js",
"_image-plus-C8PYGAuQ.js",
"index.html"
],
"css": [
@@ -138,51 +138,51 @@
]
},
"src/components/gallery/MetadataViewerModal.tsx": {
"file": "assets/MetadataViewerModal-CEAQlgmV.js",
"file": "assets/MetadataViewerModal-zQM0uYbS.js",
"name": "MetadataViewerModal",
"src": "src/components/gallery/MetadataViewerModal.tsx",
"isDynamicEntry": true,
"imports": [
"_I18nProvider-cZczvL7B.js",
"_I18nProvider-DEfiLn3N.js",
"index.html"
]
},
"src/components/gallery/VariantGroupsView.tsx": {
"file": "assets/VariantGroupsView-BdO8HmPI.js",
"file": "assets/VariantGroupsView-BT_Lf8GR.js",
"name": "VariantGroupsView",
"src": "src/components/gallery/VariantGroupsView.tsx",
"isDynamicEntry": true,
"imports": [
"_I18nProvider-cZczvL7B.js",
"_I18nProvider-DEfiLn3N.js",
"index.html"
]
},
"src/components/library/LibraryWorkspace.tsx": {
"file": "assets/LibraryWorkspace-CT02q7ep.js",
"file": "assets/LibraryWorkspace-CPcrzo6d.js",
"name": "LibraryWorkspace",
"src": "src/components/library/LibraryWorkspace.tsx",
"isDynamicEntry": true,
"imports": [
"_I18nProvider-cZczvL7B.js",
"_sparkles-Bu6QFW9h.js",
"_wand-sparkles-C_uDcQtj.js",
"_boxes--0yaGReH.js",
"_copy-Dp5BPX7F.js",
"_download-Bn9yrFnw.js",
"_refresh-ccw-CfRNSMuu.js",
"_save-DFeVqt65.js",
"_I18nProvider-DEfiLn3N.js",
"_boxes-GTo8RUgH.js",
"_copy-CoBcvjUE.js",
"_download-CSrVSIDj.js",
"_refresh-ccw-bV8xzBtF.js",
"_save-DQG0Zsqx.js",
"_sparkles-elJjV352.js",
"_wand-sparkles-262S5lQG.js",
"index.html"
]
},
"src/components/settings/SettingsWorkspace.tsx": {
"file": "assets/SettingsWorkspace-CZMkuJbF.js",
"file": "assets/SettingsWorkspace-BaJRJ6EV.js",
"name": "SettingsWorkspace",
"src": "src/components/settings/SettingsWorkspace.tsx",
"isDynamicEntry": true,
"imports": [
"_I18nProvider-cZczvL7B.js",
"_image-plus-C2AXraiD.js",
"_save-DFeVqt65.js",
"_I18nProvider-DEfiLn3N.js",
"_image-plus-C8PYGAuQ.js",
"_save-DQG0Zsqx.js",
"index.html"
],
"css": [
@@ -190,15 +190,15 @@
]
},
"src/components/workbench/WorkbenchWorkspace.tsx": {
"file": "assets/WorkbenchWorkspace-KwG36plb.js",
"file": "assets/WorkbenchWorkspace-CpFPCzJz.js",
"name": "WorkbenchWorkspace",
"src": "src/components/workbench/WorkbenchWorkspace.tsx",
"isDynamicEntry": true,
"imports": [
"_I18nProvider-cZczvL7B.js",
"_sparkles-Bu6QFW9h.js",
"_wand-sparkles-C_uDcQtj.js",
"_copy-Dp5BPX7F.js",
"_I18nProvider-DEfiLn3N.js",
"_copy-CoBcvjUE.js",
"_sparkles-elJjV352.js",
"_wand-sparkles-262S5lQG.js",
"index.html"
],
"css": [
+1
View File
@@ -0,0 +1 @@
import{a as e,n as t,r as n,s as r}from"./I18nProvider-DEfiLn3N.js";import{t as i}from"./copy-CoBcvjUE.js";import{t as a}from"./download-CSrVSIDj.js";import{C as o,T as s,W as c,f as l}from"./index-CKow1Wte.js";var u=r(e(),1),d=n(),f=5,p=20,m=220,h=34,g=1224,_=1110,v=e=>new Promise((t,n)=>{let r=new Image;r.crossOrigin=`anonymous`,r.onload=()=>t(r),r.onerror=n,r.src=e}),y=(e,t,n,r,i,a)=>{let o=t.width/t.height,s=i/a,c=o>s?t.height*s:t.width,l=o>s?t.height:t.width/s,u=(t.width-c)/2,d=(t.height-l)/2;e.drawImage(t,u,d,c,l,n,r,i,a)},b=({open:e,board:n,onClose:r})=>{let{t:b}=t(),{pushToast:x}=l(),S=(0,u.useRef)(null),[C,w]=(0,u.useState)([]),[T,E]=(0,u.useState)(!1),[D,O]=(0,u.useState)(!1),k=n?.name??``,A=(0,u.useMemo)(()=>n?b(`boardShareSubtitle`,{count:n.count}):``,[n,b]);(0,u.useEffect)(()=>{if(!e||!n)return;let t=!1;return E(!0),o.listImages(1,p,``,``,``,n.id,``,``,!1,``,`created_at`,`desc`,!1).then(e=>{t||w(e.images??[])}).catch(e=>{t||x(e instanceof Error?e.message:b(`boardShareError`),`error`)}).finally(()=>{t||E(!1)}),()=>{t=!0}},[n,e,x,b]),(0,u.useEffect)(()=>{if(!e||!n||!S.current)return;let t=!1;return(async()=>{O(!0);let e=S.current;if(!e)return;e.width=g,e.height=_;let n=e.getContext(`2d`);if(!n)return;n.fillStyle=`#ffffff`,n.fillRect(0,0,g,_),n.fillStyle=`#18181b`,n.font=`700 46px Inter, Arial, sans-serif`,n.fillText(k,h,76,1156),n.fillStyle=`#71717a`,n.font=`500 22px Inter, Arial, sans-serif`,n.fillText(A,h,114,1156),n.fillStyle=`#f4f4f5`;for(let e=0;e<p;e+=1){let t=e%f,r=Math.floor(e/f),i=h+t*234,a=154+r*234;n.fillRect(i,a,m,m)}let r=await Promise.allSettled(C.slice(0,p).map(e=>v(e.thumb_url||e.url)));t||(r.forEach((e,t)=>{if(e.status!==`fulfilled`)return;let r=t%f,i=Math.floor(t/f),a=h+r*234,o=154+i*234;y(n,e.value,a,o,m,m)}),O(!1))})(),()=>{t=!0}},[n,C,e,A,k]);let j=()=>new Promise(e=>{S.current?.toBlob(t=>e(t),`image/png`,.94)}),M=async()=>{let e=await j();if(!e||!n)return;let t=URL.createObjectURL(e),r=document.createElement(`a`);r.href=t,r.download=`${n.name}-share.png`,document.body.appendChild(r),r.click(),r.remove(),URL.revokeObjectURL(t)},N=async()=>{let e=await j();if(!e||!(`ClipboardItem`in window))x(b(`contextCopyImageError`),`error`);else try{await navigator.clipboard.write([new ClipboardItem({"image/png":e})]),x(b(`contextCopyImageSuccess`),`success`)}catch{x(b(`contextCopyImageError`),`error`)}};return!e||!n?null:(0,d.jsx)(`div`,{className:`ue-modal-backdrop`,onClick:r,children:(0,d.jsxs)(`div`,{className:`ue-library-modal ue-board-share-modal`,onClick:e=>e.stopPropagation(),children:[(0,d.jsx)(`button`,{className:`ue-modal-close ue-modal-close--light`,onClick:r,"aria-label":b(`modalClose`),children:(0,d.jsx)(s,{size:18})}),(0,d.jsxs)(`div`,{className:`ue-pane-copy`,children:[(0,d.jsx)(`p`,{className:`ue-pane-kicker`,children:b(`boardShareTitle`)}),(0,d.jsx)(`h2`,{children:n.name}),(0,d.jsx)(`p`,{children:b(T||D?`commonLoading`:`boardShareText`)})]}),(0,d.jsx)(`div`,{className:`ue-board-share-preview`,children:C.length?(0,d.jsx)(`canvas`,{ref:S,"aria-label":b(`boardShareTitle`)}):(0,d.jsxs)(`div`,{className:`ue-gallery-state ue-gallery-state--empty`,children:[(0,d.jsx)(c,{size:32}),(0,d.jsx)(`p`,{children:b(T?`commonLoading`:`boardEmptyText`)})]})}),(0,d.jsxs)(`div`,{className:`ue-library-modal-actions`,children:[(0,d.jsx)(`button`,{className:`ue-icon-action`,onClick:r,"aria-label":b(`libraryCancel`),title:b(`libraryCancel`),children:(0,d.jsx)(s,{size:14})}),(0,d.jsx)(`button`,{className:`ue-icon-action`,onClick:()=>void N(),disabled:!C.length||D,"aria-label":b(`contextCopyImage`),title:b(`contextCopyImage`),children:(0,d.jsx)(i,{size:14})}),(0,d.jsx)(`button`,{className:`ue-icon-action ue-icon-action--filled`,onClick:()=>void M(),disabled:!C.length||D,"aria-label":b(`boardShareDownload`),title:b(`boardShareDownload`),children:(0,d.jsx)(a,{size:14})})]})]})})};export{b as BoardShareModal};
-1
View File
@@ -1 +0,0 @@
import{a as e,n as t,r as n,s as r}from"./I18nProvider-cZczvL7B.js";import{t as i}from"./copy-Dp5BPX7F.js";import{t as a}from"./download-Bn9yrFnw.js";import{B as o,C as s,T as c,f as l}from"./index-DWGqjUOp.js";var u=r(e(),1),d=n(),f=5,p=f*4,m=220,h=34,g=1224,_=1110,v=e=>new Promise((t,n)=>{let r=new Image;r.crossOrigin=`anonymous`,r.onload=()=>t(r),r.onerror=n,r.src=e}),y=(e,t,n,r,i,a)=>{let o=t.width/t.height,s=i/a,c=o>s?t.height*s:t.width,l=o>s?t.height:t.width/s,u=(t.width-c)/2,d=(t.height-l)/2;e.drawImage(t,u,d,c,l,n,r,i,a)},b=({open:e,board:n,onClose:r})=>{let{t:b}=t(),{pushToast:x}=l(),S=(0,u.useRef)(null),[C,w]=(0,u.useState)([]),[T,E]=(0,u.useState)(!1),[D,O]=(0,u.useState)(!1),k=n?.name??``,A=(0,u.useMemo)(()=>n?b(`boardShareSubtitle`,{count:n.count}):``,[n,b]);(0,u.useEffect)(()=>{if(!e||!n)return;let t=!1;return E(!0),s.listImages(1,p,``,``,``,n.id,``,``,!1,``,`created_at`,`desc`,!1).then(e=>{t||w(e.images??[])}).catch(e=>{t||x(e instanceof Error?e.message:b(`boardShareError`),`error`)}).finally(()=>{t||E(!1)}),()=>{t=!0}},[n,e,x,b]),(0,u.useEffect)(()=>{if(!e||!n||!S.current)return;let t=!1;return(async()=>{O(!0);let e=S.current;if(!e)return;e.width=g,e.height=_;let n=e.getContext(`2d`);if(!n)return;n.fillStyle=`#ffffff`,n.fillRect(0,0,g,_),n.fillStyle=`#18181b`,n.font=`700 46px Inter, Arial, sans-serif`,n.fillText(k,h,76,g-h*2),n.fillStyle=`#71717a`,n.font=`500 22px Inter, Arial, sans-serif`,n.fillText(A,h,114,g-h*2),n.fillStyle=`#f4f4f5`;for(let e=0;e<p;e+=1){let t=e%f,r=Math.floor(e/f),i=h+t*234,a=154+r*234;n.fillRect(i,a,m,m)}let r=await Promise.allSettled(C.slice(0,p).map(e=>v(e.thumb_url||e.url)));t||(r.forEach((e,t)=>{if(e.status!==`fulfilled`)return;let r=t%f,i=Math.floor(t/f),a=h+r*234,o=154+i*234;y(n,e.value,a,o,m,m)}),O(!1))})(),()=>{t=!0}},[n,C,e,A,k]);let j=()=>new Promise(e=>{S.current?.toBlob(t=>e(t),`image/png`,.94)}),M=async()=>{let e=await j();if(!e||!n)return;let t=URL.createObjectURL(e),r=document.createElement(`a`);r.href=t,r.download=`${n.name}-share.png`,document.body.appendChild(r),r.click(),r.remove(),URL.revokeObjectURL(t)},N=async()=>{let e=await j();if(!e||!(`ClipboardItem`in window)){x(b(`contextCopyImageError`),`error`);return}try{await navigator.clipboard.write([new ClipboardItem({"image/png":e})]),x(b(`contextCopyImageSuccess`),`success`)}catch{x(b(`contextCopyImageError`),`error`)}};return!e||!n?null:(0,d.jsx)(`div`,{className:`ue-modal-backdrop`,onClick:r,children:(0,d.jsxs)(`div`,{className:`ue-library-modal ue-board-share-modal`,onClick:e=>e.stopPropagation(),children:[(0,d.jsx)(`button`,{className:`ue-modal-close ue-modal-close--light`,onClick:r,"aria-label":b(`modalClose`),children:(0,d.jsx)(c,{size:18})}),(0,d.jsxs)(`div`,{className:`ue-pane-copy`,children:[(0,d.jsx)(`p`,{className:`ue-pane-kicker`,children:b(`boardShareTitle`)}),(0,d.jsx)(`h2`,{children:n.name}),(0,d.jsx)(`p`,{children:b(T||D?`commonLoading`:`boardShareText`)})]}),(0,d.jsx)(`div`,{className:`ue-board-share-preview`,children:C.length?(0,d.jsx)(`canvas`,{ref:S,"aria-label":b(`boardShareTitle`)}):(0,d.jsxs)(`div`,{className:`ue-gallery-state ue-gallery-state--empty`,children:[(0,d.jsx)(o,{size:32}),(0,d.jsx)(`p`,{children:b(T?`commonLoading`:`boardEmptyText`)})]})}),(0,d.jsxs)(`div`,{className:`ue-library-modal-actions`,children:[(0,d.jsx)(`button`,{className:`ue-icon-action`,onClick:r,"aria-label":b(`libraryCancel`),title:b(`libraryCancel`),children:(0,d.jsx)(c,{size:14})}),(0,d.jsx)(`button`,{className:`ue-icon-action`,onClick:()=>void N(),disabled:!C.length||D,"aria-label":b(`contextCopyImage`),title:b(`contextCopyImage`),children:(0,d.jsx)(i,{size:14})}),(0,d.jsx)(`button`,{className:`ue-icon-action ue-icon-action--filled`,onClick:()=>void M(),disabled:!C.length||D,"aria-label":b(`boardShareDownload`),title:b(`boardShareDownload`),children:(0,d.jsx)(a,{size:14})})]})]})})};export{b as BoardShareModal};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
import{a as e,n as t,r as n,s as r}from"./I18nProvider-cZczvL7B.js";import{C as i,T as a,Y as o,a as s,f as c,i as l,ot as u}from"./index-DWGqjUOp.js";var d=r(e(),1),f=n(),p=({image:e,onClose:n})=>{let{t:r}=t(),{pushToast:p}=c(),[m,h]=(0,d.useState)(null),[g,_]=(0,d.useState)(!0),[v,y]=(0,d.useState)(null);(0,d.useEffect)(()=>{let t=!1;return _(!0),y(null),(async()=>{try{let n=await i.getImageMetadata(e.relative_path);t||h(n)}catch(e){t||y(e instanceof Error?e.message:r(`metadataLoadError`))}finally{t||_(!1)}})(),()=>{t=!0}},[e.relative_path,r]);let b=async(e,t)=>{if(!e.trim()){p(r(`metadataNoPositivePrompt`),`info`);return}try{await navigator.clipboard.writeText(e),p(t,`success`)}catch(e){p(e instanceof Error?e.message:r(`contextCopyError`),`error`)}},x=l(m),S=s(m),C=!!(m?.metadata||m?.workflow||m?.summary?.positive_prompt||m?.summary?.negative_prompt);return(0,f.jsx)(`div`,{className:`ue-modal-backdrop ue-metadata-backdrop`,onClick:n,children:(0,f.jsxs)(`section`,{className:`ue-metadata-modal`,onClick:e=>e.stopPropagation(),"aria-label":r(`metadataView`),children:[(0,f.jsxs)(`header`,{className:`ue-metadata-head`,children:[(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`span`,{children:r(`metadataView`)}),(0,f.jsx)(`h2`,{title:e.filename,children:e.filename})]}),(0,f.jsx)(`button`,{className:`ue-icon-action`,type:`button`,onClick:n,"aria-label":r(`modalClose`),children:(0,f.jsx)(a,{size:15})})]}),g?(0,f.jsxs)(`div`,{className:`ue-detail-state`,children:[(0,f.jsx)(`div`,{className:`ue-loading-orb`}),(0,f.jsx)(`p`,{children:r(`modalLoading`)})]}):v?(0,f.jsx)(`div`,{className:`ue-inline-error`,children:v}):C?(0,f.jsxs)(f.Fragment,{children:[(0,f.jsxs)(`div`,{className:`ue-metadata-actions`,children:[(0,f.jsxs)(`button`,{className:`ue-secondary-btn`,type:`button`,onClick:()=>void b(x,r(`metadataCopyPositiveSuccess`)),children:[(0,f.jsx)(o,{size:14}),(0,f.jsx)(`span`,{children:r(`metadataCopyPositive`)})]}),(0,f.jsxs)(`button`,{className:`ue-secondary-btn`,type:`button`,onClick:()=>void b(S,r(`metadataCopyRawSuccess`)),children:[(0,f.jsx)(u,{size:14}),(0,f.jsx)(`span`,{children:r(`metadataCopyRaw`)})]})]}),m?.recipe?.warnings?.length?(0,f.jsx)(`p`,{className:`ue-metadata-warning`,role:`status`,children:r(`metadataPartialWarning`)}):null,(0,f.jsxs)(`div`,{className:`ue-metadata-summary`,children:[(0,f.jsxs)(`label`,{children:[(0,f.jsx)(`span`,{children:r(`metadataPositivePrompt`)}),(0,f.jsx)(`textarea`,{value:x||r(`metadataNoPositivePrompt`),readOnly:!0})]}),(0,f.jsxs)(`label`,{children:[(0,f.jsx)(`span`,{children:r(`metadataNegativePrompt`)}),(0,f.jsx)(`textarea`,{value:m?.summary?.negative_prompt||r(`metadataNoNegativePrompt`),readOnly:!0})]})]}),(0,f.jsxs)(`details`,{className:`ue-detail-disclosure`,open:!0,children:[(0,f.jsxs)(`summary`,{children:[(0,f.jsx)(u,{size:15}),(0,f.jsx)(`span`,{children:r(`modalRawMetadata`)})]}),(0,f.jsx)(`pre`,{className:`ue-meta-raw`,children:S})]})]}):(0,f.jsx)(`div`,{className:`ue-detail-state ue-detail-state--empty`,children:(0,f.jsx)(`p`,{children:r(`modalNoMetadata`)})})]})})};export{p as MetadataViewerModal};
@@ -0,0 +1 @@
import{a as e,n as t,r as n,s as r}from"./I18nProvider-DEfiLn3N.js";import{C as i,T as a,Z as o,a as s,et as c,f as l,i as u}from"./index-CKow1Wte.js";var d=r(e(),1),f=n(),p=({image:e,onClose:n})=>{let{t:r}=t(),{pushToast:p}=l(),[m,h]=(0,d.useState)(null),[g,_]=(0,d.useState)(!0),[v,y]=(0,d.useState)(null);(0,d.useEffect)(()=>{let t=!1;return _(!0),y(null),(async()=>{try{let n=await i.getImageMetadata(e.relative_path);t||h(n)}catch(e){t||y(e instanceof Error?e.message:r(`metadataLoadError`))}finally{t||_(!1)}})(),()=>{t=!0}},[e.relative_path,r]);let b=async(e,t)=>{if(!e.trim())p(r(`metadataNoPositivePrompt`),`info`);else try{await navigator.clipboard.writeText(e),p(t,`success`)}catch(e){p(e instanceof Error?e.message:r(`contextCopyError`),`error`)}},x=u(m),S=s(m),C=!!(m?.metadata||m?.workflow||m?.summary?.positive_prompt||m?.summary?.negative_prompt);return(0,f.jsx)(`div`,{className:`ue-modal-backdrop ue-metadata-backdrop`,onClick:n,children:(0,f.jsxs)(`section`,{className:`ue-metadata-modal`,onClick:e=>e.stopPropagation(),"aria-label":r(`metadataView`),children:[(0,f.jsxs)(`header`,{className:`ue-metadata-head`,children:[(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`span`,{children:r(`metadataView`)}),(0,f.jsx)(`h2`,{title:e.filename,children:e.filename})]}),(0,f.jsx)(`button`,{className:`ue-icon-action`,type:`button`,onClick:n,"aria-label":r(`modalClose`),children:(0,f.jsx)(a,{size:15})})]}),g?(0,f.jsxs)(`div`,{className:`ue-detail-state`,children:[(0,f.jsx)(`div`,{className:`ue-loading-orb`}),(0,f.jsx)(`p`,{children:r(`modalLoading`)})]}):v?(0,f.jsx)(`div`,{className:`ue-inline-error`,children:v}):C?(0,f.jsxs)(f.Fragment,{children:[(0,f.jsxs)(`div`,{className:`ue-metadata-actions`,children:[(0,f.jsxs)(`button`,{className:`ue-secondary-btn`,type:`button`,onClick:()=>void b(x,r(`metadataCopyPositiveSuccess`)),children:[(0,f.jsx)(c,{size:14}),(0,f.jsx)(`span`,{children:r(`metadataCopyPositive`)})]}),(0,f.jsxs)(`button`,{className:`ue-secondary-btn`,type:`button`,onClick:()=>void b(S,r(`metadataCopyRawSuccess`)),children:[(0,f.jsx)(o,{size:14}),(0,f.jsx)(`span`,{children:r(`metadataCopyRaw`)})]})]}),m?.recipe?.warnings?.length?(0,f.jsx)(`p`,{className:`ue-metadata-warning`,role:`status`,children:r(`metadataPartialWarning`)}):null,(0,f.jsxs)(`div`,{className:`ue-metadata-summary`,children:[(0,f.jsxs)(`label`,{children:[(0,f.jsx)(`span`,{children:r(`metadataPositivePrompt`)}),(0,f.jsx)(`textarea`,{value:x||r(`metadataNoPositivePrompt`),readOnly:!0})]}),(0,f.jsxs)(`label`,{children:[(0,f.jsx)(`span`,{children:r(`metadataNegativePrompt`)}),(0,f.jsx)(`textarea`,{value:m?.summary?.negative_prompt||r(`metadataNoNegativePrompt`),readOnly:!0})]})]}),(0,f.jsxs)(`details`,{className:`ue-detail-disclosure`,open:!0,children:[(0,f.jsxs)(`summary`,{children:[(0,f.jsx)(o,{size:15}),(0,f.jsx)(`span`,{children:r(`modalRawMetadata`)})]}),(0,f.jsx)(`pre`,{className:`ue-meta-raw`,children:S})]})]}):(0,f.jsx)(`div`,{className:`ue-detail-state ue-detail-state--empty`,children:(0,f.jsx)(`p`,{children:r(`modalNoMetadata`)})})]})})};export{p as MetadataViewerModal};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{i as e}from"./I18nProvider-cZczvL7B.js";var t=e(`boxes`,[[`path`,{d:`M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z`,key:`lc1i9w`}],[`path`,{d:`m7 16.5-4.74-2.85`,key:`1o9zyk`}],[`path`,{d:`m7 16.5 5-3`,key:`va8pkn`}],[`path`,{d:`M7 16.5v5.17`,key:`jnp8gn`}],[`path`,{d:`M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z`,key:`8zsnat`}],[`path`,{d:`m17 16.5-5-3`,key:`8arw3v`}],[`path`,{d:`m17 16.5 4.74-2.85`,key:`8rfmw`}],[`path`,{d:`M17 16.5v5.17`,key:`k6z78m`}],[`path`,{d:`M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z`,key:`1xygjf`}],[`path`,{d:`M12 8 7.26 5.15`,key:`1vbdud`}],[`path`,{d:`m12 8 4.74-2.85`,key:`3rx089`}],[`path`,{d:`M12 13.5V8`,key:`1io7kd`}]]);export{t};
import{i as e}from"./I18nProvider-DEfiLn3N.js";var t=e(`boxes`,[[`path`,{d:`M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z`,key:`lc1i9w`}],[`path`,{d:`m7 16.5-4.74-2.85`,key:`1o9zyk`}],[`path`,{d:`m7 16.5 5-3`,key:`va8pkn`}],[`path`,{d:`M7 16.5v5.17`,key:`jnp8gn`}],[`path`,{d:`M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z`,key:`8zsnat`}],[`path`,{d:`m17 16.5-5-3`,key:`8arw3v`}],[`path`,{d:`m17 16.5 4.74-2.85`,key:`8rfmw`}],[`path`,{d:`M17 16.5v5.17`,key:`k6z78m`}],[`path`,{d:`M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z`,key:`1xygjf`}],[`path`,{d:`M12 8 7.26 5.15`,key:`1vbdud`}],[`path`,{d:`m12 8 4.74-2.85`,key:`3rx089`}],[`path`,{d:`M12 13.5V8`,key:`1io7kd`}]]);export{t};
@@ -1 +1 @@
import{i as e}from"./I18nProvider-cZczvL7B.js";var t=e(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]);export{t};
import{i as e}from"./I18nProvider-DEfiLn3N.js";var t=e(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]);export{t};
@@ -1 +1 @@
import{i as e}from"./I18nProvider-cZczvL7B.js";var t=e(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]);export{t};
import{i as e}from"./I18nProvider-DEfiLn3N.js";var t=e(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]);export{t};
@@ -1 +1 @@
import{i as e}from"./I18nProvider-cZczvL7B.js";var t=e(`image-plus`,[[`path`,{d:`M16 5h6`,key:`1vod17`}],[`path`,{d:`M19 2v6`,key:`4bpg5p`}],[`path`,{d:`M21 11.5V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7.5`,key:`1ue2ih`}],[`path`,{d:`m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21`,key:`1xmnt7`}],[`circle`,{cx:`9`,cy:`9`,r:`2`,key:`af1f0g`}]]);export{t};
import{i as e}from"./I18nProvider-DEfiLn3N.js";var t=e(`image-plus`,[[`path`,{d:`M16 5h6`,key:`1vod17`}],[`path`,{d:`M19 2v6`,key:`4bpg5p`}],[`path`,{d:`M21 11.5V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7.5`,key:`1ue2ih`}],[`path`,{d:`m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21`,key:`1xmnt7`}],[`circle`,{cx:`9`,cy:`9`,r:`2`,key:`af1f0g`}]]);export{t};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{i as e}from"./I18nProvider-cZczvL7B.js";var t=e(`refresh-ccw`,[[`path`,{d:`M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`14sxne`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}],[`path`,{d:`M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16`,key:`1hlbsb`}],[`path`,{d:`M16 16h5v5`,key:`ccwih5`}]]);export{t};
import{i as e}from"./I18nProvider-DEfiLn3N.js";var t=e(`refresh-ccw`,[[`path`,{d:`M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`14sxne`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}],[`path`,{d:`M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16`,key:`1hlbsb`}],[`path`,{d:`M16 16h5v5`,key:`ccwih5`}]]);export{t};
@@ -1 +1 @@
import{i as e}from"./I18nProvider-cZczvL7B.js";var t=e(`save`,[[`path`,{d:`M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z`,key:`1c8476`}],[`path`,{d:`M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7`,key:`1ydtos`}],[`path`,{d:`M7 3v4a1 1 0 0 0 1 1h7`,key:`t51u73`}]]);export{t};
import{i as e}from"./I18nProvider-DEfiLn3N.js";var t=e(`save`,[[`path`,{d:`M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z`,key:`1c8476`}],[`path`,{d:`M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7`,key:`1ydtos`}],[`path`,{d:`M7 3v4a1 1 0 0 0 1 1h7`,key:`t51u73`}]]);export{t};
@@ -1 +1 @@
import{i as e}from"./I18nProvider-cZczvL7B.js";var t=e(`sparkles`,[[`path`,{d:`M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z`,key:`1s2grr`}],[`path`,{d:`M20 2v4`,key:`1rf3ol`}],[`path`,{d:`M22 4h-4`,key:`gwowj6`}],[`circle`,{cx:`4`,cy:`20`,r:`2`,key:`6kqj1y`}]]);export{t};
import{i as e}from"./I18nProvider-DEfiLn3N.js";var t=e(`sparkles`,[[`path`,{d:`M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z`,key:`1s2grr`}],[`path`,{d:`M20 2v4`,key:`1rf3ol`}],[`path`,{d:`M22 4h-4`,key:`gwowj6`}],[`circle`,{cx:`4`,cy:`20`,r:`2`,key:`6kqj1y`}]]);export{t};
@@ -1 +1 @@
import{i as e}from"./I18nProvider-cZczvL7B.js";var t=e(`wand-sparkles`,[[`path`,{d:`m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72`,key:`ul74o6`}],[`path`,{d:`m14 7 3 3`,key:`1r5n42`}],[`path`,{d:`M5 6v4`,key:`ilb8ba`}],[`path`,{d:`M19 14v4`,key:`blhpug`}],[`path`,{d:`M10 2v2`,key:`7u0qdc`}],[`path`,{d:`M7 8H3`,key:`zfb6yr`}],[`path`,{d:`M21 16h-4`,key:`1cnmox`}],[`path`,{d:`M11 3H9`,key:`1obp7u`}]]);export{t};
import{i as e}from"./I18nProvider-DEfiLn3N.js";var t=e(`wand-sparkles`,[[`path`,{d:`m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72`,key:`ul74o6`}],[`path`,{d:`m14 7 3 3`,key:`1r5n42`}],[`path`,{d:`M5 6v4`,key:`ilb8ba`}],[`path`,{d:`M19 14v4`,key:`blhpug`}],[`path`,{d:`M10 2v2`,key:`7u0qdc`}],[`path`,{d:`M7 8H3`,key:`zfb6yr`}],[`path`,{d:`M21 16h-4`,key:`1cnmox`}],[`path`,{d:`M11 3H9`,key:`1obp7u`}]]);export{t};
+1 -1
View File
@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Universal Gallery - ComfyUI image gallery and artist library manager" />
<title>ComfyUI Universal Extractor</title>
<script type="module" crossorigin src="/gallery/assets/index-DWGqjUOp.js"></script>
<script type="module" crossorigin src="/gallery/assets/index-CKow1Wte.js"></script>
<link rel="stylesheet" crossorigin href="/gallery/assets/index-2iqQxvh4.css">
</head>
<body>
+1 -1
View File
@@ -1 +1 @@
{"version":"1.7.0"}
{"version":"1.8.0"}
+446 -157
View File
@@ -1,12 +1,12 @@
{
"name": "gallery_ui",
"version": "1.7.0",
"version": "1.8.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "gallery_ui",
"version": "1.7.0",
"version": "1.8.0",
"dependencies": {
"@tanstack/react-virtual": "^3.13.24",
"lucide-react": "^1.8.0",
@@ -31,8 +31,8 @@
"tailwindcss": "^4.2.2",
"typescript": "~6.0.2",
"typescript-eslint": "^8.58.0",
"vite": "^8.0.4",
"vitest": "^4.1.5"
"vite": "^8.3.5",
"vitest": "^4.1.12"
}
},
"node_modules/@adobe/css-tools": {
@@ -841,19 +841,36 @@
}
},
"node_modules/@oxc-project/types": {
"version": "0.137.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz",
"integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==",
"version": "0.155.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz",
"integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
"url": "https://github.com/sponsors/oxc-project"
}
},
"node_modules/@rolldown/binding-android-arm-eabi": {
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz",
"integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz",
"integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz",
"integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==",
"cpu": [
"arm64"
],
@@ -868,9 +885,9 @@
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz",
"integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz",
"integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==",
"cpu": [
"arm64"
],
@@ -885,9 +902,9 @@
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz",
"integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz",
"integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==",
"cpu": [
"x64"
],
@@ -902,9 +919,9 @@
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz",
"integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz",
"integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==",
"cpu": [
"x64"
],
@@ -919,9 +936,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz",
"integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz",
"integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==",
"cpu": [
"arm"
],
@@ -936,13 +953,16 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz",
"integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz",
"integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==",
"cpu": [
"arm64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -953,13 +973,16 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz",
"integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz",
"integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==",
"cpu": [
"arm64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
@@ -970,13 +993,16 @@
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz",
"integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz",
"integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==",
"cpu": [
"ppc64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -987,13 +1013,16 @@
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz",
"integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz",
"integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==",
"cpu": [
"s390x"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1004,13 +1033,16 @@
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz",
"integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz",
"integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==",
"cpu": [
"x64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1021,13 +1053,16 @@
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz",
"integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz",
"integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==",
"cpu": [
"x64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1038,9 +1073,9 @@
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz",
"integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz",
"integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==",
"cpu": [
"arm64"
],
@@ -1054,29 +1089,10 @@
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-wasm32-wasi": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz",
"integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==",
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "1.11.1",
"@emnapi/runtime": "1.11.1",
"@napi-rs/wasm-runtime": "^1.1.6"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz",
"integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz",
"integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==",
"cpu": [
"arm64"
],
@@ -1091,9 +1107,9 @@
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz",
"integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz",
"integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==",
"cpu": [
"x64"
],
@@ -1913,16 +1929,16 @@
}
},
"node_modules/@vitest/expect": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz",
"integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.12.tgz",
"integrity": "sha512-vFlTt+YEu9WBpNjdGayNmlMVzrWPi1+sv853/7t2FU7hpO5gAFBKHzQmkCq5Xjpr+evegF2994LQT3hF3mRlqQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
"@types/chai": "^5.2.2",
"@vitest/spy": "4.1.11",
"@vitest/utils": "4.1.11",
"@vitest/spy": "4.1.12",
"@vitest/utils": "4.1.12",
"chai": "^6.2.2",
"tinyrainbow": "^3.1.0"
},
@@ -1931,13 +1947,13 @@
}
},
"node_modules/@vitest/mocker": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz",
"integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.12.tgz",
"integrity": "sha512-fKShNpKZzupWZuY0z7egydvL0sqW4INEZBEExMBMAa6izlENEVHwoXgdaTCaBZ5fwlr4xf2o1wXC83n9RpO+NA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "4.1.11",
"@vitest/spy": "4.1.12",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21"
},
@@ -1958,9 +1974,9 @@
}
},
"node_modules/@vitest/pretty-format": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz",
"integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.12.tgz",
"integrity": "sha512-XW9O8+Mxx4VpCtkYdMJLZQQwsVryP9wk7cPKDxtXFfwSak6yRh5+2fep/Y0XF/ElBvJHq8bcYdi6aeuai0opZA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1971,13 +1987,13 @@
}
},
"node_modules/@vitest/runner": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz",
"integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.12.tgz",
"integrity": "sha512-NKlVKCr5QnCe8jijdMnmkpHHU+WTqiLFR6G0pHji9uRjXK9xJl1k1nTw6F3pOOj2PDcsDt2GKQMI7wIsaT491w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "4.1.11",
"@vitest/utils": "4.1.12",
"pathe": "^2.0.3"
},
"funding": {
@@ -1985,14 +2001,14 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz",
"integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.12.tgz",
"integrity": "sha512-ko1y1Qgb/slzQ9mr+b89Cuqud8WG1TKtsPD8r7ZUBdvVOGt85Af2HwCqHujrhwJq95hs/9RM7fcAPg57o1vlzw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.1.11",
"@vitest/utils": "4.1.11",
"@vitest/pretty-format": "4.1.12",
"@vitest/utils": "4.1.12",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
@@ -2001,9 +2017,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz",
"integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.12.tgz",
"integrity": "sha512-RGq9x4JNGNTZHEJxvN0oSiJjeSCExm0n2gZKKJZHY9cX5AVA6M7VJILBT3ZJ/tJ/0J9uWT7fETV9/btoi//vSg==",
"dev": true,
"license": "MIT",
"funding": {
@@ -2011,13 +2027,13 @@
}
},
"node_modules/@vitest/utils": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz",
"integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.12.tgz",
"integrity": "sha512-Q3awXrwOfEV+qc3WmO5ONyE0yO7Q1gQ9/oyHxGLZ/pl7N3NO/ZztqQwKLuwMkV+CA1JYSIeO3gkHnRDIo4AVaA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.1.11",
"@vitest/pretty-format": "4.1.12",
"convert-source-map": "^2.0.0",
"tinyrainbow": "^3.1.0"
},
@@ -2226,9 +2242,9 @@
"license": "CC-BY-4.0"
},
"node_modules/chai": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz",
"integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3467,9 +3483,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.18",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
"version": "3.3.20",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz",
"integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==",
"dev": true,
"funding": [
{
@@ -3624,9 +3640,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3637,9 +3653,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.26",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
"integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
"version": "8.5.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz",
"integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==",
"dev": true,
"funding": [
{
@@ -3657,9 +3673,9 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.17",
"nanoid": "^3.3.20",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
"source-map-js": "^1.2.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -3779,13 +3795,13 @@
}
},
"node_modules/rolldown": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz",
"integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==",
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz",
"integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.137.0",
"@oxc-project/types": "=0.155.0",
"@rolldown/pluginutils": "^1.0.0"
},
"bin": {
@@ -3795,21 +3811,21 @@
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm64": "1.1.3",
"@rolldown/binding-darwin-arm64": "1.1.3",
"@rolldown/binding-darwin-x64": "1.1.3",
"@rolldown/binding-freebsd-x64": "1.1.3",
"@rolldown/binding-linux-arm-gnueabihf": "1.1.3",
"@rolldown/binding-linux-arm64-gnu": "1.1.3",
"@rolldown/binding-linux-arm64-musl": "1.1.3",
"@rolldown/binding-linux-ppc64-gnu": "1.1.3",
"@rolldown/binding-linux-s390x-gnu": "1.1.3",
"@rolldown/binding-linux-x64-gnu": "1.1.3",
"@rolldown/binding-linux-x64-musl": "1.1.3",
"@rolldown/binding-openharmony-arm64": "1.1.3",
"@rolldown/binding-wasm32-wasi": "1.1.3",
"@rolldown/binding-win32-arm64-msvc": "1.1.3",
"@rolldown/binding-win32-x64-msvc": "1.1.3"
"@rolldown/binding-android-arm-eabi": "1.2.15",
"@rolldown/binding-android-arm64": "1.2.15",
"@rolldown/binding-darwin-arm64": "1.2.15",
"@rolldown/binding-darwin-x64": "1.2.15",
"@rolldown/binding-freebsd-x64": "1.2.15",
"@rolldown/binding-linux-arm-gnueabihf": "1.2.15",
"@rolldown/binding-linux-arm64-gnu": "1.2.15",
"@rolldown/binding-linux-arm64-musl": "1.2.15",
"@rolldown/binding-linux-ppc64-gnu": "1.2.15",
"@rolldown/binding-linux-s390x-gnu": "1.2.15",
"@rolldown/binding-linux-x64-gnu": "1.2.15",
"@rolldown/binding-linux-x64-musl": "1.2.15",
"@rolldown/binding-openharmony-arm64": "1.2.15",
"@rolldown/binding-win32-arm64-msvc": "1.2.15",
"@rolldown/binding-win32-x64-msvc": "1.2.15"
}
},
"node_modules/rolldown/node_modules/@rolldown/pluginutils": {
@@ -4004,9 +4020,9 @@
}
},
"node_modules/tinyrainbow": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz",
"integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz",
"integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4190,16 +4206,16 @@
}
},
"node_modules/vite": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz",
"integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==",
"version": "8.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz",
"integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"lightningcss": "^1.32.0",
"picomatch": "^4.0.4",
"postcss": "^8.5.15",
"rolldown": "~1.1.2",
"lightningcss": "^1.33.0",
"picomatch": "^4.0.7",
"postcss": "^8.5.30",
"rolldown": "~1.2.14",
"tinyglobby": "^0.2.17"
},
"bin": {
@@ -4216,7 +4232,7 @@
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
"@vitejs/devtools": "^0.3.0",
"@vitejs/devtools": "^0.7.1 || ^0.8.0",
"esbuild": "^0.27.0 || ^0.28.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
@@ -4267,20 +4283,293 @@
}
}
},
"node_modules/vite/node_modules/lightningcss": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
"integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
},
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"lightningcss-android-arm64": "1.33.0",
"lightningcss-darwin-arm64": "1.33.0",
"lightningcss-darwin-x64": "1.33.0",
"lightningcss-freebsd-x64": "1.33.0",
"lightningcss-linux-arm-gnueabihf": "1.33.0",
"lightningcss-linux-arm64-gnu": "1.33.0",
"lightningcss-linux-arm64-musl": "1.33.0",
"lightningcss-linux-x64-gnu": "1.33.0",
"lightningcss-linux-x64-musl": "1.33.0",
"lightningcss-win32-arm64-msvc": "1.33.0",
"lightningcss-win32-x64-msvc": "1.33.0"
}
},
"node_modules/vite/node_modules/lightningcss-android-arm64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
"integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vite/node_modules/lightningcss-darwin-arm64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
"integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vite/node_modules/lightningcss-darwin-x64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
"integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vite/node_modules/lightningcss-freebsd-x64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
"integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
"integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
"integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
"cpu": [
"arm64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vite/node_modules/lightningcss-linux-arm64-musl": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
"integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
"cpu": [
"arm64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vite/node_modules/lightningcss-linux-x64-gnu": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
"integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
"cpu": [
"x64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vite/node_modules/lightningcss-linux-x64-musl": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
"integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
"cpu": [
"x64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
"integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vite/node_modules/lightningcss-win32-x64-msvc": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
"integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/vitest": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz",
"integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.12.tgz",
"integrity": "sha512-5eWdz2yb3ajxWF3C8C4TjkXl7FGm3n2E/VlazALrQHzK7nYMSGFnCZto0bbPNl77AwkOZVLk0YnBAejUu0NiWA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "4.1.11",
"@vitest/mocker": "4.1.11",
"@vitest/pretty-format": "4.1.11",
"@vitest/runner": "4.1.11",
"@vitest/snapshot": "4.1.11",
"@vitest/spy": "4.1.11",
"@vitest/utils": "4.1.11",
"@vitest/expect": "4.1.12",
"@vitest/mocker": "4.1.12",
"@vitest/pretty-format": "4.1.12",
"@vitest/runner": "4.1.12",
"@vitest/snapshot": "4.1.12",
"@vitest/spy": "4.1.12",
"@vitest/utils": "4.1.12",
"es-module-lexer": "^2.0.0",
"expect-type": "^1.3.0",
"magic-string": "^0.30.21",
@@ -4308,12 +4597,12 @@
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "4.1.11",
"@vitest/browser-preview": "4.1.11",
"@vitest/browser-webdriverio": "4.1.11",
"@vitest/coverage-istanbul": "4.1.11",
"@vitest/coverage-v8": "4.1.11",
"@vitest/ui": "4.1.11",
"@vitest/browser-playwright": "4.1.12",
"@vitest/browser-preview": "4.1.12",
"@vitest/browser-webdriverio": "^4.1.11",
"@vitest/coverage-istanbul": "4.1.12",
"@vitest/coverage-v8": "4.1.12",
"@vitest/ui": "4.1.12",
"happy-dom": "*",
"jsdom": "*",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "gallery_ui",
"private": true,
"version": "1.7.0",
"version": "1.8.0",
"type": "module",
"scripts": {
"dev": "vite",
@@ -41,7 +41,7 @@
"tailwindcss": "^4.2.2",
"typescript": "~6.0.2",
"typescript-eslint": "^8.58.0",
"vite": "^8.0.4",
"vitest": "^4.1.5"
"vite": "^8.3.5",
"vitest": "^4.1.12"
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "tera-universal-extractor"
description = "ComfyUI custom node and gallery workspace for prompt libraries, searchable image notes, and reusable extraction workflows."
version = "1.7.0"
version = "1.8.0"
license = { file = "LICENSE.txt" }
readme = "README.md"
requires-python = ">=3.10"