Merge pull request #923 from zanllp/docs/reorganize-skill-references

docs: reorganize skill documentation and extend URL scheme
docs/fix-url-encoding-example
zanllp 2026-02-22 13:26:00 +08:00 committed by GitHub
commit d3892407bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
83 changed files with 913 additions and 211 deletions

View File

@ -2,7 +2,14 @@
"permissions": {
"allow": [
"Bash(git checkout:*)",
"Bash(tree:*)"
"Bash(tree:*)",
"Bash(npm run build:*)",
"Bash(curl:*)",
"Bash(python3:*)",
"Bash(python:*)",
"Bash(npx vue-tsc:*)",
"Bash(find:*)",
"Bash(grep:*)"
]
}
}

1
.gitignore vendored
View File

@ -26,3 +26,4 @@ iib.db-wal
.cursor/
CLAUDE.md
.claude/*

View File

@ -39,14 +39,13 @@
- 支持全屏预览,并且支持在全屏预览下使用自定义快捷键进行操作
- 支持在全屏预览模式下通过按下方向键或点击按钮移动到前一个或后一个图像。
- 支持播放远程服务器上的视频文件
- 支持 WebM 视频与音频文件播放。
- 优化视频流 Range 处理,改善大文件吞吐。
### 💻 多种使用方法
- 您可以将其作为 SD-webui 的扩展安装。
- 您可以使用 Python 独立运行它。
- 还提供桌面应用程序版本。
- 支持多种流行的AI软件
- **NEW**[与 AI 助手一起使用](docs/ai-agents-zh.md)Claude Code、Cursor、OpenClaw 等)
### 🚶‍♀️ Walk模式
- 自动加载下一个文件夹 `(类似于 os.walk)`,可让您无需分页浏览所有图像。
@ -138,6 +137,10 @@ exe版本同样支持ComfyUI/Fooocus/NovelAI
使用iframe接入IIB将IIB作为你应用的文件浏览器使用。 参考 https://github.com/zanllp/sd-webui-infinite-image-browsing/blob/main/vue/usage.md
## 作为 AI 助手技能
IIB 可以与 Claude Code、Cursor 和 OpenClaw 等 AI 助手一起使用。详情请参阅 [AI 助手文档](docs/ai-agents-zh.md)。
# 预览
<img width="1920" alt="image" src="https://user-images.githubusercontent.com/25872019/230064374-47ba209e-562b-47b8-a2ce-d867e3afe204.png">

View File

@ -48,15 +48,13 @@ If you would like to support more software, please refer to: [parsers](https://g
- Support full-screen preview and enable custom shortcut key operations while in full-screen preview mode.
- Support navigating to the previous or next image in full-screen preview mode by pressing arrow keys or clicking buttons.
- Support playing video files from a remote server.
- Support WebM videos and audio playback.
- Improved video streaming Range handling for large files.
### 💻 Multiple Usage Methods
- You can install it as an extension on SD-webui.
- You can run it independently using Python.
- The desktop app version is also available.
- Supports multiple popular AI software.
- **NEW**: Use with AI agents (Claude Code, Cursor, OpenClaw, etc.) via Agent Skills.
- **NEW**: [Use with AI agents](docs/ai-agents.md) (Claude Code, Cursor, OpenClaw, etc.)
### 🎵 TikTok-Style View
@ -152,31 +150,9 @@ If you need to compile it yourself, please refer to https://github.com/zanllp/sd
Use iframe to access IIB and use it as a file browser for your application. Refer to https://github.com/zanllp/sd-webui-infinite-image-browsing/blob/main/vue/usage.md
## With AI Agents (Claude Code, Cursor, OpenClaw, etc.)
IIB can be used as an [Agent Skill](https://agentskills.io), allowing AI agents to search, browse, tag, and organize your images through natural language.
### Installation
```bash
npx skills add https://github.com/zanllp/infinite-image-browsing --skill iib
```
### Usage
1. Start IIB service first:
```bash
python app.py --port 7866
```
2. Then ask your AI agent to help with image tasks:
- "Find all images with 'sunset' in the prompt"
- "Tag these images as favorites"
- "Organize my Downloads folder by theme"
- "Show me the generation parameters of this image"
The agent will use IIB's API to perform these operations. See [skills/iib/SKILL.md](skills/iib/SKILL.md) for the full API documentation.
## As an AI Agent Skill
IIB can be used with AI agents like Claude Code, Cursor, and OpenClaw. See [AI Agents Documentation](docs/ai-agents.md) for details.
# Preview

88
docs/ai-agents-zh.md Normal file
View File

@ -0,0 +1,88 @@
# 与 AI 助手一起使用 IIBClaude Code、Cursor、OpenClaw 等)
IIB 可以作为 [Agent Skill](https://agentskills.io) 使用,让 AI 助手通过自然语言来搜索、浏览、标签化和整理你的图片。
## 安装
为你的 AI 助手安装 IIB 技能:
```bash
npx skills add https://github.com/zanllp/infinite-image-browsing --skill iib
```
## 使用方法
### 第一步:启动 IIB 服务
首先,启动 IIB 服务:
```bash
python app.py --port <port>
```
### 第二步:与 AI 助手交互
IIB 运行后,你可以让 AI 助手帮你完成各种图片任务:
**搜索与筛选:**
- "查找所有提示词中包含 'sunset' 的图片"
- "显示使用模型 X 生成的图片"
- "查找标记为 '收藏' 的图片"
- "查找包含人物但不是草稿的图片"
- "搜索质量高且包含风景的图片"
**标签管理:**
- "将这些图片标记为 '高质量'"
- "从所有图片中移除 'test' 标签"
- "为有人物的图片添加 'portrait' 标签"
**文件整理:**
- "按主题整理我的下载文件夹"
- "将所有风景图片移到单独的文件夹"
- "为不同艺术风格创建文件夹"
**信息获取:**
- "显示这张图片的生成参数"
- "这些图片使用了什么提示词?"
- "比较这两张图片的设置"
### 可用的视图类型
| 视图类型 | 描述 | URL 示例 |
|----------|------|----------|
| **快速查看单张图片** | 全屏预览指定图片 | `?action=view&path=/path/to/img.png` |
| **关键词搜索页** | 搜索页面入口 | `?action=pane&type=fuzzy-search` |
| **关键词搜索(预填充)** | 自动填充关键词并搜索 | `?action=pane&type=fuzzy-search&props={"substr":"sunset"}` |
| **标签搜索页** | 按标签筛选 | `?action=pane&type=tag-search` |
| **标签搜索结果** | 预设标签的搜索结果 | `?action=pane&type=tag-search-matched-image-grid&props=...` |
| **图片对比** | 并排对比两张图 | `?action=pane&type=img-sli&props=...` |
| **文件夹浏览** | 浏览指定文件夹 | `?action=pane&type=local&props=...` |
| **随机图片** | 随机展示图片 | `?action=pane&type=random-image` |
**示例对话:**
- "搜索包含 'sunset' 的图片"
- "显示我标记为收藏的所有图片"
- "在 IIB 中对比这两张图片"
## 支持的 AI 助手
IIB 的技能兼容以下平台:
- **Claude Code** - Anthropic 的官方 Claude CLI
- **Cursor** - AI 代码编辑器
- **OpenClaw** - AI 编程助手
- 任何其他支持 [Agent Skills](https://agentskills.io) 格式的助手
## 系统要求
- Python 3.7+
- IIB 服务运行在可访问的端口上
- 支持 Agent Skills 的 AI 助手
## 故障排查
如果 AI 助手无法连接到 IIB
1. 确保 IIB 服务正在运行(`python app.py --port <port>`
2. 检查端口是否与助手配置的端口匹配
3. 验证网络/防火墙设置允许本地连接

87
docs/ai-agents.md Normal file
View File

@ -0,0 +1,87 @@
# Using IIB with AI Agents (Claude Code, Cursor, OpenClaw, etc.)
IIB can be used as an [Agent Skill](https://agentskills.io), allowing AI agents to search, browse, tag, and organize your images through natural language.
## Installation
Install the IIB skill for your AI agent:
```bash
npx skills add https://github.com/zanllp/infinite-image-browsing --skill iib
```
## Usage
### Step 1: Start IIB Service
First, start the IIB service:
```bash
python app.py --port <port>
```
### Step 2: Interact with Your AI Agent
Once IIB is running, you can ask your AI agent to help with various image tasks:
**Search & Filter:**
- "Find all images with 'sunset' in the prompt"
- "Show me images generated with model X"
- "Find images tagged as 'favorites'"
- "Find images with people but exclude drafts"
- "Search for high quality landscape images"
**Tag Management:**
- "Tag these images as 'best quality'"
- "Remove the 'test' tag from all images"
- "Add the 'portrait' tag to images with people"
**File Organization:**
- "Organize my Downloads folder by theme"
- "Move all landscape images to a separate folder"
- "Create folders for different art styles"
**Information Retrieval:**
- "Show me the generation parameters of this image"
- "What prompts were used for these images?"
- "Compare the settings between these two images"
### Available View Types
| View Type | Description | URL Example |
|-----------|-------------|-------------|
| **Quick View** | Fullscreen preview of single image | `?action=view&path=/path/to/img.png` |
| **Keyword Search** | Search page for keyword filtering | `?action=pane&type=fuzzy-search` |
| **Keyword Search (Pre-filled)** | Search with auto-filled keyword | `?action=pane&type=fuzzy-search&props={"substr":"sunset"}` |
| **Tag Search** | Filter by tags | `?action=pane&type=tag-search` |
| **Tag Results** | Pre-filtered tag search results | `?action=pane&type=tag-search-matched-image-grid&props=...` |
| **Image Compare** | Side-by-side comparison | `?action=pane&type=img-sli&props=...` |
| **Folder Browse** | Browse specific folder | `?action=pane&type=local&props=...` |
| **Random Images** | Display random images | `?action=pane&type=random-image` |
**Example prompts:**
- "Search for images with 'sunset' in the prompt"
- "Show all images tagged as favorites"
- "Compare these two images in IIB"
## Supported AI Agents
IIB's skill is compatible with:
- **Claude Code** - Anthropic's official CLI for Claude
- **Cursor** - AI code editor
- **OpenClaw** - AI coding assistant
- Any other agent that supports the [Agent Skills](https://agentskills.io) format
## Requirements
- Python 3.7+
- IIB service running on an accessible port
- AI agent with Agent Skills support
## Troubleshooting
If the agent cannot connect to IIB:
1. Ensure IIB service is running (`python app.py --port <port>`)
2. Check that the port matches the one configured in your agent
3. Verify network/firewall settings allow local connections

View File

@ -13,8 +13,8 @@ Promise.resolve().then(async () => {
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Infinite Image Browsing</title>
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-0cf80cdf.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-6d9e7c10.css">
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-5573ae38.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-dd273d5b.css">
</head>
<body>

View File

@ -1116,8 +1116,8 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
class SearchBySubstrReq(BaseModel):
surstr: str
cursor: str
regexp: str
cursor: Optional[str] = ""
regexp: Optional[str] = ""
folder_paths: List[str] = None
size: Optional[int] = 200
path_only: Optional[bool] = False
@ -1128,7 +1128,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
if IIB_DEBUG:
logger.info(req)
conn = DataBase.get_conn()
folder_paths=normalize_paths(req.folder_paths, os.getcwd())
folder_paths=normalize_paths(req.folder_paths or [], os.getcwd())
if(not folder_paths and req.folder_paths):
return { "files": [], "cursor": Cursor(has_next=False) }
imgs, next_cursor = DbImg.find_by_substring(
@ -1147,10 +1147,10 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
}
class MatchImagesByTagsReq(BaseModel):
and_tags: List[int]
or_tags: List[int]
not_tags: List[int]
cursor: str
and_tags: Optional[List[int]] = []
or_tags: Optional[List[int]] = []
not_tags: Optional[List[int]] = []
cursor: Optional[str] = ""
folder_paths: List[str] = None
size: Optional[int] = 200
random_sort: Optional[bool] = False
@ -1160,7 +1160,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
if IIB_DEBUG:
logger.info(req)
conn = DataBase.get_conn()
folder_paths=normalize_paths(req.folder_paths, os.getcwd())
folder_paths=normalize_paths(req.folder_paths or [], os.getcwd())
if(not folder_paths and req.folder_paths):
return { "files": [], "cursor": Cursor(has_next=False) }
imgs, next_cursor = ImageTag.get_images_by_tags(

View File

@ -7,11 +7,23 @@ description: Interact with IIB (Infinite Image Browsing) service for searching,
IIB is an image/video browsing and management tool that parses metadata from AI generation tools (Stable Diffusion, ComfyUI, etc.).
---
## Quick Links to Detailed Guides
| Topic | Description |
|-------|-------------|
| **[API Reference](references/api-reference.md)** | Complete API endpoint documentation |
| **[Search Strategies](references/search-strategies.md)** | Multi-word, regex, tag combination searches |
| **[Agent Patterns](references/agent-patterns.md)** | Common workflows and decision trees |
---
## Before You Start
**IMPORTANT:** Always do these two things first:
1. **Ask the user for the port** if they started the service themselves (common ports: `7866` standalone, `7860` SD WebUI extension)
1. **Ask the user for the port** if they started the service themselves (common ports: `<port>` standalone, `7860` SD WebUI extension)
2. **Test connectivity** with a hello request before any other operation
```bash
@ -25,13 +37,13 @@ Note: Use `--noproxy "*"` to bypass proxy for localhost connections.
```bash
cd /path/to/sd-webui-infinite-image-browsing
python app.py --port 7866
python app.py --port <port>
```
To run as a background daemon:
```bash
nohup python app.py --port 7866 > iib.log 2>&1 &
nohup python app.py --port <port> > iib.log 2>&1 &
```
## Quick Reference
@ -49,34 +61,37 @@ nohup python app.py --port 7866 > iib.log 2>&1 &
| Add library path | POST | `/db/extra_paths` |
| Remove library path | DELETE | `/db/extra_paths` |
Base URL: `http://127.0.0.1:7866/infinite_image_browsing`
Base URL: `http://127.0.0.1:<port>/infinite_image_browsing`
## Core Operations
### Search Images
#### Basic Keyword Search
Search by keyword in file path or generation parameters:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/search_by_substr \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-H "Content-Type: application/json" \
-d '{"surstr": "landscape", "cursor": "", "size": 50}'
-d '{"surstr": "landscape", "size": 50}'
```
Search with regex pattern:
#### Advanced Search
For complex searches (multi-word, regex patterns, tag combinations), see **[Search Strategies](references/search-strategies.md)**.
**Quick tips:**
- Multi-word searches? Use regex OR: `(word1|word2)`
- Need tag combinations? Use `/db/match_images_by_tags` with AND/OR/NOT logic
- See **[Search Strategies](references/search-strategies.md)** for detailed strategies
#### Limit to Specific Folders
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/search_by_substr \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-H "Content-Type: application/json" \
-d '{"surstr": "", "regexp": "masterpiece.*1girl", "cursor": "", "size": 50}'
```
Limit to specific folders:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/search_by_substr \
-H "Content-Type: application/json" \
-d '{"surstr": "portrait", "folder_paths": ["/path/to/folder"], "cursor": "", "size": 50}'
-d '{"surstr": "portrait", "folder_paths": ["/path/to/folder"], "size": 50}'
```
Response:
@ -92,14 +107,14 @@ Response:
Get all tags:
```bash
curl http://127.0.0.1:7866/infinite_image_browsing/db/basic_info
curl http://127.0.0.1:<port>/infinite_image_browsing/db/basic_info
# Response includes: {"tags": [{"id": 1, "name": "favorites", "type": "custom"}, ...]}
```
Create a tag:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/add_custom_tag \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/add_custom_tag \
-H "Content-Type: application/json" \
-d '{"tag_name": "favorites"}'
```
@ -107,7 +122,7 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/add_custom_tag \
Tag multiple images:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/batch_update_image_tag \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/batch_update_image_tag \
-H "Content-Type: application/json" \
-d '{"img_paths": ["/path/to/img1.png", "/path/to/img2.png"], "action": "add", "tag_id": 1}'
```
@ -115,7 +130,7 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/batch_update_image
Search by tags (AND/OR/NOT logic):
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/match_images_by_tags \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/match_images_by_tags \
-H "Content-Type: application/json" \
-d '{"and_tags": [1], "or_tags": [], "not_tags": [2], "cursor": "", "size": 50}'
```
@ -125,13 +140,13 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/match_images_by_ta
List folder contents:
```bash
curl "http://127.0.0.1:7866/infinite_image_browsing/files?folder_path=/path/to/images"
curl "http://127.0.0.1:<port>/infinite_image_browsing/files?folder_path=/path/to/images"
```
Move files:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/move_files \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/move_files \
-H "Content-Type: application/json" \
-d '{"file_paths": ["/path/to/img1.png"], "dest": "/new/folder", "create_dest_folder": true}'
```
@ -139,7 +154,7 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/move_files \
Copy files:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/copy_files \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/copy_files \
-H "Content-Type: application/json" \
-d '{"file_paths": ["/path/to/img1.png"], "dest": "/backup/folder", "create_dest_folder": true}'
```
@ -147,7 +162,7 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/copy_files \
Delete files:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/delete_files \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/delete_files \
-H "Content-Type: application/json" \
-d '{"file_paths": ["/path/to/unwanted.png"]}'
```
@ -157,13 +172,13 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/delete_files \
Get generation parameters (prompt, seed, model, etc.):
```bash
curl "http://127.0.0.1:7866/infinite_image_browsing/image_geninfo?path=/path/to/image.png"
curl "http://127.0.0.1:<port>/infinite_image_browsing/image_geninfo?path=/path/to/image.png"
```
Batch get metadata:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/image_geninfo_batch \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/image_geninfo_batch \
-H "Content-Type: application/json" \
-d '{"paths": ["/path/to/img1.png", "/path/to/img2.png"]}'
```
@ -173,13 +188,13 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/image_geninfo_batch \
List registered paths:
```bash
curl http://127.0.0.1:7866/infinite_image_browsing/db/extra_paths
curl http://127.0.0.1:<port>/infinite_image_browsing/db/extra_paths
```
Add a folder to library:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/extra_paths \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/extra_paths \
-H "Content-Type: application/json" \
-d '{"path": "/new/image/folder", "types": ["scanned"]}'
```
@ -187,7 +202,7 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/extra_paths \
Remove a folder (also cleans up orphaned image records):
```bash
curl -X DELETE http://127.0.0.1:7866/infinite_image_browsing/db/extra_paths \
curl -X DELETE http://127.0.0.1:<port>/infinite_image_browsing/db/extra_paths \
-H "Content-Type: application/json" \
-d '{"path": "/old/folder", "types": ["scanned"]}'
```
@ -195,7 +210,7 @@ curl -X DELETE http://127.0.0.1:7866/infinite_image_browsing/db/extra_paths \
Rebuild index after adding paths:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/rebuild_index
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/rebuild_index
```
Path types:
@ -211,7 +226,7 @@ Automatically organize images into themed folders:
```bash
# Start organization job
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/organize_files_start \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/organize_files_start \
-H "Content-Type: application/json" \
-d '{
"folder_paths": ["/messy/folder"],
@ -223,10 +238,10 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/organize_files_sta
# Returns: {"job_id": "uuid"}
# Check status
curl "http://127.0.0.1:7866/infinite_image_browsing/db/organize_files_status?job_id=<job_id>"
curl "http://127.0.0.1:<port>/infinite_image_browsing/db/organize_files_status?job_id=<job_id>"
# Confirm and execute
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/organize_files_confirm \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/organize_files_confirm \
-H "Content-Type: application/json" \
-d '{"job_id": "<job_id>"}'
```
@ -236,7 +251,7 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/organize_files_con
Analyze images and group by semantic similarity:
```bash
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/cluster_iib_output_job_start \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/cluster_iib_output_job_start \
-H "Content-Type: application/json" \
-d '{
"folder_paths": ["/path/to/images"],
@ -250,3 +265,137 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/cluster_iib_output
## Reference
See [references/api-reference.md](references/api-reference.md) for complete API documentation.
## Viewing Images
When helping users view images, always provide BOTH:
1. A link to view the first/selected image
2. A link to the search results page showing all matching images
### Image Search Results
When a user searches for images (by keyword, tag, etc.):
1. **Provide a summary**: Show how many images were found
2. **First image link**: Use the quick view URL for the first result
3. **Search results page link**: Use the fuzzy-search or tag-search pane URL
**Example response format:**
```markdown
Found 150+ images matching "sunset":
[View First Image](http://127.0.0.1:<port>/infinite_image_browsing?action=view&path=/first/image.png)
[View All Results in IIB](http://127.0.0.1:<port>/infinite_image_browsing?action=pane&type=fuzzy-search&props=%7B%22substr%22%3A%22sunset%22%7D)
| # | Name | Path | Size |
|---|------|------|------|
| 1 | image1.png | /folder | 1.2 MB |
| 2 | image2.png | /folder | 1.1 MB |
...
```
**Note:** The search results page link includes the search term in the URL, so it will automatically display the matching results when opened.
### Building Shareable URLs
When generating URLs for users to open in their browser, you need to properly encode the `props` parameter (JSON URL-encoded).
**URL Format:**
```
http://127.0.0.1:<port>/infinite_image_browsing?action=pane&type=<pane_type>&props=<url_encoded_json>
```
**Encoding the props:**
The `props` parameter must be URL-encoded JSON. Use this encoding method:
| JSON Props | URL Encoded | Notes |
|------------|-------------|-------|
| `{"substr":"sunset"}` | `%7B%22substr%22%3A%22sunset%22%7D` | Standard encoding |
| `{"substr":"(词1\|词2)","isRegex":true}` | `%7B%22substr%22%3A%22(%E8%AF%8D1%7C%E8%AF%8D2)%22%2C%22isRegex%22%3Atrue%7D` | Chinese chars encoded |
| `{"substr":"a\|b","isRegex":true}` | `%7B%22substr%22%3A%22(a%7Cb)%22%2C%22isRegex%22%3Atrue%7D` | Pipe `\|` encoded as `%7C` |
**Common URL patterns:**
| Use Case | URL Pattern |
|----------|-------------|
| Simple keyword search | `?action=pane&type=fuzzy-search&props=%7B%22substr%22%3A%22keyword%22%7D` |
| Regex OR search | `?action=pane&type=fuzzy-search&props=%7B%22substr%22%3A%22(word1%7Cword2)%22%2C%22isRegex%22%3Atrue%7D` |
| Search in folder | `?action=pane&type=fuzzy-search&props=%7B%22substr%22%3A%22keyword%22%2C%22searchScope%22%3A%22%2Fpath%22%7D` |
| View single image | `?action=view&path=/path/to/image.png` |
**Tip:** When generating URLs with non-ASCII characters (Chinese, Japanese, etc.), use proper URL encoding (UTF-8 percent encoding).
**Example: Multi-word search with regex (Chinese)**
```bash
# Search for "太空电梯" OR "三体"
# JSON: {"substr":"(太空电梯|三体)","isRegex":true}
# Encoded: %7B%22substr%22%3A%22(%E6%B1%89%E6%9C%8D%7C%E4%BA%BA%E5%A6%BB)%22%2C%22isRegex%22%3Atrue%7D
?action=pane&type=fuzzy-search&props=%7B%22substr%22%3A%22(%E6%B1%89%E6%9C%8D%7C%E4%BA%BA%E5%A6%BB)%22%2C%22isRegex%22%3Atrue%7D
```
### Available View Options
#### 1. Quick View (Single Image)
```
http://127.0.0.1:<port>/infinite_image_browsing?action=view&path=/path/to/image.png
```
Opens IIB directly to the image in fullscreen preview.
#### 2. Keyword Search Page
```
http://127.0.0.1:<port>/infinite_image_browsing?action=pane&type=fuzzy-search
```
Opens the keyword search page where users can search.
**With pre-filled search term:**
```
http://127.0.0.1:<port>/infinite_image_browsing?action=pane&type=fuzzy-search&props=%7B%22substr%22%3A%22sunset%22%7D
```
Automatically searches for "sunset" when opened.
**Available props for fuzzy-search (URL → Component mapping):**
| URL Prop | Component Prop | Type | Description |
|----------|----------------|------|-------------|
| `substr` | `initialSubstr` | string | Search keyword or regex pattern |
| `isRegex` | `initialIsRegex` | boolean | Use regex mode (default: false) |
| `searchScope` | `searchScope` | string | Folder path to limit search |
| `pathOnly` | `initialPathOnly` | boolean | Search only file paths (default: false) |
| `mediaType` | `initialMediaType` | string | Filter by media type: "all", "image", "video" |
| `autoSearch` | `autoSearch` | boolean | Auto-trigger search (default: true when substr is set) |
**Note:** Use the URL prop names (left column) when constructing URLs. The component prop names (middle column) are for internal use.
**Examples:**
```bash
# Regex search for multiple keywords (OR logic)
?action=pane&type=fuzzy-search&props=%7B%22substr%22%3A%22(sunset%7Csunrise%7Cdawn)%22%2C%22isRegex%22%3Atrue%7D
# Search in specific folder
?action=pane&type=fuzzy-search&props=%7B%22substr%22%3A%22portrait%22%2C%22searchScope%22%3A%22%2Foutputs%22%7D
# Search images only
?action=pane&type=fuzzy-search&props=%7B%22substr%22%3A%22landscape%22%2C%22mediaType%22%3A%22image%22%7D
```
#### 3. Tag Search Page
```
http://127.0.0.1:<port>/infinite_image_browsing?action=pane&type=tag-search
```
Opens the tag search page for filtering by tags.
#### 4. Tag Search Results (Pre-filtered)
```
http://127.0.0.1:<port>/infinite_image_browsing?action=pane&type=tag-search-matched-image-grid&props=%7B%22selectedTagIds%22%3A%7B%22and_tags%22%3A%5B1%2C2%5D%7D%7D%7D
```
Shows images matching specific tags (replace tag IDs with actual values).
#### 5. Image Comparison
```
http://127.0.0.1:<port>/infinite_image_browsing?action=pane&type=img-sli&props=%7B%22left%22%3A%7B%22fullpath%22%3A%22%2Fa.png%22%7D%2C%22right%22%3A%7B%22fullpath%22%3A%22%2Fb.png%22%7D%7D
```
Compare two images side by side.
**Note:** The `props` parameter must be URL-encoded JSON.

View File

@ -1,6 +1,6 @@
# Agent Guide
# Agent Patterns
Task-oriented patterns for common IIB operations.
Task-oriented patterns for common IIB operations when working with AI agents.
## Decision Tree
@ -41,23 +41,23 @@ User wants to manage library
```bash
# 1. Search for images
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/search_by_substr \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-H "Content-Type: application/json" \
-d '{"surstr": "sunset", "cursor": "", "size": 100}'
# Note the fullpath values from response
# 2. Check existing tags
curl http://127.0.0.1:7866/infinite_image_browsing/db/basic_info
curl http://127.0.0.1:<port>/infinite_image_browsing/db/basic_info
# Note tag IDs, or create new tag:
# 3. Create tag if needed
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/add_custom_tag \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/add_custom_tag \
-H "Content-Type: application/json" \
-d '{"tag_name": "sunset-photos"}'
# Returns: {"id": 5, "name": "sunset-photos", ...}
# 4. Apply tag to found images
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/batch_update_image_tag \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/batch_update_image_tag \
-H "Content-Type: application/json" \
-d '{"img_paths": ["/path/to/img1.png", "/path/to/img2.png"], "action": "add", "tag_id": 5}'
```
@ -66,7 +66,7 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/batch_update_image
```bash
# 1. Start AI organization
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/organize_files_start \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/organize_files_start \
-H "Content-Type: application/json" \
-d '{
"folder_paths": ["/Downloads/ai-images"],
@ -78,11 +78,11 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/organize_files_sta
# Returns: {"job_id": "abc123"}
# 2. Poll for completion
curl "http://127.0.0.1:7866/infinite_image_browsing/db/organize_files_status?job_id=abc123"
curl "http://127.0.0.1:<port>/infinite_image_browsing/db/organize_files_status?job_id=abc123"
# Wait until status is "completed"
# 3. Confirm and execute
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/organize_files_confirm \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/organize_files_confirm \
-H "Content-Type: application/json" \
-d '{"job_id": "abc123"}'
```
@ -91,17 +91,17 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/organize_files_con
```bash
# 1. Add path
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/extra_paths \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/extra_paths \
-H "Content-Type: application/json" \
-d '{"path": "/new/image/folder", "types": ["scanned"]}'
# 2. Trigger index update
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/update_image_data \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/update_image_data \
-H "Content-Type: application/json" \
-d '{"path": "/new/image/folder"}'
# 3. Verify images are indexed
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/search_by_substr \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-H "Content-Type: application/json" \
-d '{"surstr": "", "folder_paths": ["/new/image/folder"], "cursor": "", "size": 10}'
```
@ -110,13 +110,13 @@ curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/search_by_substr \
```bash
# First page
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/search_by_substr \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-H "Content-Type: application/json" \
-d '{"surstr": "landscape", "cursor": "", "size": 100}'
# Response: {"files": [...], "cursor": {"has_next": true, "next": "cursor_abc"}}
# Next page
curl -X POST http://127.0.0.1:7866/infinite_image_browsing/db/search_by_substr \
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-H "Content-Type: application/json" \
-d '{"surstr": "landscape", "cursor": "cursor_abc", "size": 100}'
# Continue until has_next is false

View File

@ -0,0 +1,110 @@
# Search Strategies
For complex search requests, use these strategies to find the images users need.
## Multi-Word Searches
**Important:** When users search with multiple words (e.g., "太空电梯 三体", "sunset beach"), these words are likely **NOT concatenated together** in the generation parameters.
### 1. Regex OR Pattern (Recommended)
Use regex OR pattern to search for images containing either word:
```bash
# Chinese: 太空电梯 OR 三体
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-H "Content-Type: application/json" \
-d '{"regexp": "(太空电梯|三体)", "size": 50}'
# English: sunset OR beach
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-d '{"regexp": "(sunset|beach)", "size": 50}'
# Generate URL with regex mode
?action=pane&type=fuzzy-search&props=%7B%22substr%22%3A%22(%E6%B1%89%E6%9C%8D%7C%E4%B8%89%E4%BD%93)%22%2C%22isRegex%22%3Atrue%7D
```
### 2. Multiple Sequential Searches
Search for each condition separately, then cross-reference results:
```bash
# First search (太空电梯)
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-d '{"surstr": "太空电梯", "size": 200}'
# Second search (三体)
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-d '{"surstr": "三体", "size": 200}'
# Compare and find overlapping results
```
### 3. Tag Combination Search (Most Flexible)
Combine multiple tags with AND/OR/NOT logic:
```bash
# AND: Images with ALL these tags
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/match_images_by_tags \
-d '{"and_tags": [1, 2, 3], "size": 50}'
# OR: Images with ANY of these tags
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/match_images_by_tags \
-d '{"and_tags": [], "or_tags": [1, 2, 3], "size": 50}'
# NOT: Images WITHOUT these tags
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/match_images_by_tags \
-d '{"and_tags": [1], "not_tags": [5, 6], "size": 50}'
# Combined: (tag1 AND tag2) OR (tag3) NOT (tag4)
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/match_images_by_tags \
-d '{"and_tags": [1, 2], "or_tags": [3], "not_tags": [4], "size": 50}'
```
## Complex Pattern Matching
Use regex for advanced patterns:
```bash
# Sequential patterns (AND logic)
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-d '{"regexp": "masterpiece.*best.*quality", "size": 50}'
# Multiple patterns (OR logic)
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-d '{"regexp": "(sunset|sunrise|dawn)", "size": 50}'
# Negative patterns (NOT logic) - exclude monochrome and sketch
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-d '{"regexp": "landscape", "size": 50}'
```
## Combining Filters
You can combine multiple filters in a single search:
```bash
# Regex OR + Folder restriction + Media type filter
curl -X POST http://127.0.0.1:<port>/infinite_image_browsing/db/search_by_substr \
-H "Content-Type: application/json" \
-d '{
"regexp": "(sunset|beach)",
"folder_paths": ["/path/to/folder"],
"media_type": "image",
"size": 50
}'
```
## URL Encoding Tips
When generating URLs for users to open in their browser:
1. **JSON Props** must be URL-encoded
2. **Non-ASCII characters** (Chinese, Japanese, etc.) need UTF-8 percent encoding
| JSON Props | URL Encoded |
|------------|-------------|
| `{"substr":"sunset"}` | `%7B%22substr%22%3A%22sunset%22%7D` |
| `{"substr":"(词1|词2)","isRegex":true}` | `%7B%22substr%22%3A%22(%E8%AF%8D1%7C%E8%AF%8D2)%22%2C%22isRegex%22%3Atrue%7D` |
| `{"substr":"a|b","isRegex":true}` | `%7B%22substr%22%3A%22(a%7Cb)%22%2C%22isRegex%22%3Atrue%7D` |

1
vue/dist/assets/FileItem-0881fad3.css vendored Normal file

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

View File

@ -1 +0,0 @@
.img-sli-container[data-v-ec71de83]{position:relative;overflow-y:auto;height:calc(100vh - 40px)}

View File

@ -0,0 +1 @@
.img-sli-container[data-v-ae3fb9a8]{position:relative;overflow-y:auto;height:calc(100vh - 40px)}

View File

@ -1 +0,0 @@
import{d as t,o as a,j as n,c as s,b_ as _,n as o}from"./index-0cf80cdf.js";const c={class:"img-sli-container"},i=t({__name:"ImgSliPagePane",props:{paneIdx:{},tabIdx:{},left:{},right:{}},setup(l){return(e,r)=>(a(),n("div",c,[s(_,{left:e.left,right:e.right},null,8,["left","right"])]))}});const p=o(i,[["__scopeId","data-v-ec71de83"]]);export{p as default};

View File

@ -0,0 +1 @@
import{d as a,o as t,j as n,c as s,c0 as _,n as o}from"./index-5573ae38.js";const c={class:"img-sli-container"},i=a({__name:"ImgSliPagePane",props:{paneIdx:{},tabIdx:{},left:{},right:{}},setup(l){return(e,r)=>(t(),n("div",c,[s(_,{left:e.left,right:e.right},null,8,["left","right"])]))}});const d=o(i,[["__scopeId","data-v-ae3fb9a8"]]);export{d as default};

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.container[data-v-6e837a6f]{background:var(--zp-secondary-background);position:relative}.container .action-bar[data-v-6e837a6f]{display:flex;align-items:center;user-select:none;gap:4px;padding:4px}.container .action-bar>*[data-v-6e837a6f]{flex-wrap:wrap}.container .file-list[data-v-6e837a6f]{list-style:none;padding:8px;overflow:auto;height:calc(var(--pane-max-height) - 40px);width:100%}.container .no-res-hint[data-v-6e837a6f]{height:var(--pane-max-height);display:flex;align-items:center;flex-direction:column;justify-content:center}.container .no-res-hint .hint[data-v-6e837a6f]{font-size:1.6em;margin-bottom:2em;text-align:center}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.container[data-v-795e5ef4]{background:var(--zp-secondary-background);position:relative;height:var(--pane-max-height)}.action-bar[data-v-795e5ef4]{display:flex;align-items:center;user-select:none;gap:6px;padding:6px 8px}.title[data-v-795e5ef4]{font-weight:700;max-width:40vw}.file-list[data-v-795e5ef4]{list-style:none;padding:8px;overflow:auto;height:calc(var(--pane-max-height) - 44px);width:100%}.no-res-hint[data-v-795e5ef4]{height:calc(var(--pane-max-height) - 44px);display:flex;align-items:center;flex-direction:column;justify-content:center}.no-res-hint .hint[data-v-795e5ef4]{font-size:1.2em;opacity:.7}.preview-switch[data-v-795e5ef4]{position:fixed;bottom:24px;right:24px;display:flex;gap:8px;font-size:36px;user-select:none}.disable[data-v-795e5ef4]{opacity:.3;pointer-events:none}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.container[data-v-4815fec6]{background:var(--zp-secondary-background);position:relative}.container .action-bar[data-v-4815fec6]{display:flex;align-items:center;user-select:none;gap:4px;padding:4px}.container .action-bar>*[data-v-4815fec6]{flex-wrap:wrap}.container .file-list[data-v-4815fec6]{list-style:none;padding:8px;overflow:auto;height:calc(var(--pane-max-height) - 40px);width:100%}.container .no-res-hint[data-v-4815fec6]{height:var(--pane-max-height);display:flex;align-items:center;flex-direction:column;justify-content:center}.container .no-res-hint .hint[data-v-4815fec6]{font-size:1.6em;margin-bottom:2em;text-align:center}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.container[data-v-aea581a5]{background:var(--zp-secondary-background);position:relative;height:var(--pane-max-height)}.action-bar[data-v-aea581a5]{display:flex;align-items:center;user-select:none;gap:6px;padding:6px 8px}.title[data-v-aea581a5]{font-weight:700;max-width:40vw}.file-list[data-v-aea581a5]{list-style:none;padding:8px;overflow:auto;height:calc(var(--pane-max-height) - 44px);width:100%}.no-res-hint[data-v-aea581a5]{height:calc(var(--pane-max-height) - 44px);display:flex;align-items:center;flex-direction:column;justify-content:center}.no-res-hint .hint[data-v-aea581a5]{font-size:1.2em;opacity:.7}.preview-switch[data-v-aea581a5]{position:fixed;bottom:24px;right:24px;display:flex;gap:8px;font-size:36px;user-select:none}.disable[data-v-aea581a5]{opacity:.3;pointer-events:none}

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

View File

@ -1 +0,0 @@
[data-v-9726dd9e] .float-panel{position:fixed}.regex-icon[data-v-9726dd9e]{user-select:none;padding:4px;margin:0 4px;cursor:pointer;border:1px solid var(--zp-border);border-radius:4px}.regex-icon img[data-v-9726dd9e]{height:1.5em}.regex-icon[data-v-9726dd9e]:hover{background:var(--zp-border)}.regex-icon.selected[data-v-9726dd9e]{background:var(--primary-color-1);border:1px solid var(--primary-color)}.search-bar[data-v-9726dd9e]{padding:8px 8px 0;display:flex}.search-bar.last[data-v-9726dd9e]{padding-bottom:8px}.search-bar .form-name[data-v-9726dd9e]{flex-shrink:0;padding:4px 8px}.search-bar .actions>*[data-v-9726dd9e]{margin-right:4px}.container[data-v-9726dd9e]{background:var(--zp-secondary-background);position:relative}.container .file-list[data-v-9726dd9e]{list-style:none;padding:8px;height:100%;overflow:auto;height:var(--pane-max-height);width:100%}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
[data-v-3d50503a] .float-panel{position:fixed}.regex-icon[data-v-3d50503a]{user-select:none;padding:4px;margin:0 4px;cursor:pointer;border:1px solid var(--zp-border);border-radius:4px}.regex-icon img[data-v-3d50503a]{height:1.5em}.regex-icon[data-v-3d50503a]:hover{background:var(--zp-border)}.regex-icon.selected[data-v-3d50503a]{background:var(--primary-color-1);border:1px solid var(--primary-color)}.search-bar[data-v-3d50503a]{padding:8px 8px 0;display:flex}.search-bar.last[data-v-3d50503a]{padding-bottom:8px}.search-bar .form-name[data-v-3d50503a]{flex-shrink:0;padding:4px 8px}.search-bar .actions>*[data-v-3d50503a]{margin-right:4px}.container[data-v-3d50503a]{background:var(--zp-secondary-background);position:relative}.container .file-list[data-v-3d50503a]{list-style:none;padding:8px;height:100%;overflow:auto;height:var(--pane-max-height);width:100%}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.tag-hierarchy-graph[data-v-11299bfc]{width:100%;height:calc(100vh - 200px);min-height:600px;position:relative;background:linear-gradient(135deg,#1a1a2e 0%,#16213e 100%);border-radius:8px;overflow:hidden}.loading-container[data-v-11299bfc],.error-container[data-v-11299bfc]{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:#fff}.loading-text[data-v-11299bfc]{margin-top:16px;font-size:14px;color:#aaa}.graph-container[data-v-11299bfc]{width:100%;height:100%;position:relative}.control-panel[data-v-11299bfc]{position:absolute;top:16px;left:16px;z-index:10;background:rgba(0,0,0,.7);padding:8px 12px;border-radius:6px;backdrop-filter:blur(10px)}.chart-container[data-v-11299bfc]{width:100%;height:100%}.topic-search[data-v-d9f73106]{height:var(--pane-max-height);overflow:auto;padding:12px}.toolbar[data-v-d9f73106]{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;background:var(--zp-primary-background);border-radius:12px}.left[data-v-d9f73106]{display:flex;align-items:center;gap:8px}.title[data-v-d9f73106]{display:flex;align-items:center;gap:8px;font-weight:700}.right[data-v-d9f73106]{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.label[data-v-d9f73106]{color:#888}.guide[data-v-d9f73106]{display:flex;flex-direction:column;gap:8px;padding:10px 12px;background:var(--zp-primary-background);border-radius:12px;border:1px solid rgba(0,0,0,.06)}.guide-row[data-v-d9f73106]{display:flex;align-items:center;gap:10px}.guide-hint[data-v-d9f73106]{margin-top:4px;display:flex;align-items:center;gap:10px;opacity:.85}.guide-icon[data-v-d9f73106]{width:22px;text-align:center;flex:0 0 22px}.guide-text[data-v-d9f73106]{flex:1 1 auto;min-width:0;color:#000000bf}.grid[data-v-d9f73106]{margin-top:12px;display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px}.card[data-v-d9f73106]{background:var(--zp-primary-background);border-radius:12px;padding:10px 12px;cursor:pointer;border:1px solid rgba(0,0,0,.06)}.card[data-v-d9f73106]:hover{border-color:#1890ff99}.card-top[data-v-d9f73106]{display:flex;align-items:center;justify-content:space-between;gap:8px}.card-title[data-v-d9f73106]{font-weight:700}.card-count[data-v-d9f73106]{min-width:28px;text-align:right;opacity:.75}.card-desc[data-v-d9f73106]{margin-top:6px;color:#666;font-size:12px}.empty[data-v-d9f73106]{height:calc(var(--pane-max-height) - 72px);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px}.hint[data-v-d9f73106]{font-size:16px;opacity:.75}

View File

@ -0,0 +1 @@
.tag-hierarchy-graph[data-v-eb57f19b]{width:100%;height:calc(100vh - 200px);min-height:600px;position:relative;background:linear-gradient(135deg,#1a1a2e 0%,#16213e 100%);border-radius:8px;overflow:hidden}.loading-container[data-v-eb57f19b],.error-container[data-v-eb57f19b]{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:#fff}.loading-text[data-v-eb57f19b]{margin-top:16px;font-size:14px;color:#aaa}.graph-container[data-v-eb57f19b]{width:100%;height:100%;position:relative}.control-panel[data-v-eb57f19b]{position:absolute;top:16px;left:16px;z-index:10;background:rgba(0,0,0,.7);padding:8px 12px;border-radius:6px;backdrop-filter:blur(10px)}.chart-container[data-v-eb57f19b]{width:100%;height:100%}.topic-search[data-v-4ef869f3]{height:var(--pane-max-height);overflow:auto;padding:12px}.toolbar[data-v-4ef869f3]{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;background:var(--zp-primary-background);border-radius:12px}.left[data-v-4ef869f3]{display:flex;align-items:center;gap:8px}.title[data-v-4ef869f3]{display:flex;align-items:center;gap:8px;font-weight:700}.right[data-v-4ef869f3]{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.label[data-v-4ef869f3]{color:#888}.guide[data-v-4ef869f3]{display:flex;flex-direction:column;gap:8px;padding:10px 12px;background:var(--zp-primary-background);border-radius:12px;border:1px solid rgba(0,0,0,.06)}.guide-row[data-v-4ef869f3]{display:flex;align-items:center;gap:10px}.guide-hint[data-v-4ef869f3]{margin-top:4px;display:flex;align-items:center;gap:10px;opacity:.85}.guide-icon[data-v-4ef869f3]{width:22px;text-align:center;flex:0 0 22px}.guide-text[data-v-4ef869f3]{flex:1 1 auto;min-width:0;color:#000000bf}.grid[data-v-4ef869f3]{margin-top:12px;display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px}.card[data-v-4ef869f3]{background:var(--zp-primary-background);border-radius:12px;padding:10px 12px;cursor:pointer;border:1px solid rgba(0,0,0,.06)}.card[data-v-4ef869f3]:hover{border-color:#1890ff99}.card-top[data-v-4ef869f3]{display:flex;align-items:center;justify-content:space-between;gap:8px}.card-title[data-v-4ef869f3]{font-weight:700}.card-count[data-v-4ef869f3]{min-width:28px;text-align:right;opacity:.75}.card-desc[data-v-4ef869f3]{margin-top:6px;color:#666;font-size:12px}.empty[data-v-4ef869f3]{height:calc(var(--pane-max-height) - 72px);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px}.hint[data-v-4ef869f3]{font-size:16px;opacity:.75}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{b0 as i,aM as t,dI as f,aQ as n}from"./index-0cf80cdf.js";function u(e,a,r){if(!i(r))return!1;var s=typeof a;return(s=="number"?t(r)&&f(a,r.length):s=="string"&&a in r)?n(r[a],e):!1}export{u as i};
import{b2 as i,aO as t,dI as f,aS as n}from"./index-5573ae38.js";function u(e,a,r){if(!i(r))return!1;var s=typeof a;return(s=="number"?t(r)&&f(a,r.length):s=="string"&&a in r)?n(r[a],e):!1}export{u as i};

View File

@ -0,0 +1 @@
import{d as z,p as B,c1 as $,bp as S,o as _,j as w,k as f,c as l,C as d,l as p,t as c,E as s,B as A,U as E,c2 as R,c3 as y,W as x,X as T,a3 as U,a6 as V,n as N}from"./index-5573ae38.js";import{F as j,s as L}from"./FileItem-ab6bf09c.js";import{u as H,b as O,j as W}from"./index-de6cda7b.js";import"./index-5e569728.js";import"./shortcut-f44c6c9d.js";import"./_isIterateeCall-bb321415.js";const q={class:"actions-panel actions"},G={class:"item"},P={key:0,class:"file-list"},Q={class:"hint"},X=z({__name:"batchDownload",props:{tabIdx:{},paneIdx:{},id:{}},setup(J){const{stackViewEl:b}=H().toRefs(),{itemSize:h,gridItems:D,cellWidth:g}=O(),i=B(),m=W(),{selectdFiles:a}=$(m),r=S(),v=async e=>{const t=R(e);t&&m.addFiles(t.nodes)},C=async()=>{r.pushAction(async()=>{const e=await y.value.post("/zip",{paths:a.value.map(u=>u.fullpath),compress:i.batchDownloadCompress,pack_only:!1},{responseType:"blob"}),t=window.URL.createObjectURL(new Blob([e.data])),o=document.createElement("a");o.href=t,o.setAttribute("download",`iib_${new Date().toLocaleString()}.zip`),document.body.appendChild(o),o.click()})},I=async()=>{r.pushAction(async()=>{await y.value.post("/zip",{paths:a.value.map(e=>e.fullpath),compress:i.batchDownloadCompress,pack_only:!0},{responseType:"blob"}),x.success(T("success"))})},F=e=>{a.value.splice(e,1)};return(e,t)=>{const o=U,u=V;return _(),w("div",{class:"container",ref_key:"stackViewEl",ref:b,onDrop:v},[f("div",q,[l(o,{onClick:t[0]||(t[0]=n=>s(m).selectdFiles=[])},{default:d(()=>[p(c(e.$t("clear")),1)]),_:1}),f("div",G,[p(c(e.$t("compressFile"))+": ",1),l(u,{checked:s(i).batchDownloadCompress,"onUpdate:checked":t[1]||(t[1]=n=>s(i).batchDownloadCompress=n)},null,8,["checked"])]),l(o,{onClick:I,type:"primary",loading:!s(r).isIdle},{default:d(()=>[p(c(e.$t("packOnlyNotDownload")),1)]),_:1},8,["loading"]),l(o,{onClick:C,type:"primary",loading:!s(r).isIdle},{default:d(()=>[p(c(e.$t("zipDownload")),1)]),_:1},8,["loading"])]),s(a).length?(_(),A(s(L),{key:1,ref:"scroller",class:"file-list",items:s(a).slice(),"item-size":s(h).first,"key-field":"fullpath","item-secondary-size":s(h).second,gridItems:s(D)},{default:d(({item:n,index:k})=>[l(j,{idx:k,file:n,"cell-width":s(g),"enable-close-icon":"",onCloseIconClick:K=>F(k),"full-screen-preview-image-url":s(E)(n),"enable-right-click-menu":!1},null,8,["idx","file","cell-width","onCloseIconClick","full-screen-preview-image-url"])]),_:1},8,["items","item-size","item-secondary-size","gridItems"])):(_(),w("div",P,[f("p",Q,c(e.$t("batchDownloaDDragAndDropHint")),1)]))],544)}}});const oe=N(X,[["__scopeId","data-v-a2642a17"]]);export{oe as default};

View File

@ -0,0 +1 @@
.container[data-v-a2642a17]{background:var(--zp-secondary-background);height:100%;overflow:auto;display:flex;flex-direction:column}.container .actions-panel[data-v-a2642a17]{padding:8px;background-color:var(--zp-primary-background)}.container .actions-panel.actions[data-v-a2642a17]{display:flex;align-items:center;gap:16px;z-index:333}.container .file-list[data-v-a2642a17]{flex:1;z-index:222;list-style:none;padding:8px;height:var(--pane-max-height);width:100%}.container .file-list .hint[data-v-a2642a17]{text-align:center;font-size:2em;padding:30vh 128px 0}

View File

@ -1 +0,0 @@
.container[data-v-3d7e6f2d]{background:var(--zp-secondary-background);height:100%;overflow:auto;display:flex;flex-direction:column}.container .actions-panel[data-v-3d7e6f2d]{padding:8px;background-color:var(--zp-primary-background)}.container .actions-panel.actions[data-v-3d7e6f2d]{display:flex;align-items:center;gap:16px;z-index:333}.container .file-list[data-v-3d7e6f2d]{flex:1;z-index:222;list-style:none;padding:8px;height:var(--pane-max-height);width:100%}.container .file-list .hint[data-v-3d7e6f2d]{text-align:center;font-size:2em;padding:30vh 128px 0}

View File

@ -1 +0,0 @@
import{d as F,p as z,b$ as B,bn as S,o as _,j as w,k as f,c as l,x as d,l as p,t as c,y as s,v as R,O as x,c0 as A,c1 as y,R as T,T as E,$ as V,a2 as N,n as U}from"./index-0cf80cdf.js";import{F as j,s as L}from"./FileItem-1c8f6c40.js";import{u as O,b as H,j as q}from"./index-70bdf486.js";import"./index-5acf9bcb.js";import"./shortcut-76a3e0c5.js";import"./_isIterateeCall-6a3838fa.js";const G={class:"actions-panel actions"},P={class:"item"},Q={key:0,class:"file-list"},W={class:"hint"},J=F({__name:"batchDownload",props:{tabIdx:{},paneIdx:{},id:{}},setup(K){const{stackViewEl:b}=O().toRefs(),{itemSize:h,gridItems:D,cellWidth:g}=H(),i=z(),m=q(),{selectdFiles:a}=B(m),r=S(),v=async e=>{const t=A(e);t&&m.addFiles(t.nodes)},C=async()=>{r.pushAction(async()=>{const e=await y.value.post("/zip",{paths:a.value.map(u=>u.fullpath),compress:i.batchDownloadCompress,pack_only:!1},{responseType:"blob"}),t=window.URL.createObjectURL(new Blob([e.data])),o=document.createElement("a");o.href=t,o.setAttribute("download",`iib_${new Date().toLocaleString()}.zip`),document.body.appendChild(o),o.click()})},I=async()=>{r.pushAction(async()=>{await y.value.post("/zip",{paths:a.value.map(e=>e.fullpath),compress:i.batchDownloadCompress,pack_only:!0},{responseType:"blob"}),T.success(E("success"))})},$=e=>{a.value.splice(e,1)};return(e,t)=>{const o=V,u=N;return _(),w("div",{class:"container",ref_key:"stackViewEl",ref:b,onDrop:v},[f("div",G,[l(o,{onClick:t[0]||(t[0]=n=>s(m).selectdFiles=[])},{default:d(()=>[p(c(e.$t("clear")),1)]),_:1}),f("div",P,[p(c(e.$t("compressFile"))+": ",1),l(u,{checked:s(i).batchDownloadCompress,"onUpdate:checked":t[1]||(t[1]=n=>s(i).batchDownloadCompress=n)},null,8,["checked"])]),l(o,{onClick:I,type:"primary",loading:!s(r).isIdle},{default:d(()=>[p(c(e.$t("packOnlyNotDownload")),1)]),_:1},8,["loading"]),l(o,{onClick:C,type:"primary",loading:!s(r).isIdle},{default:d(()=>[p(c(e.$t("zipDownload")),1)]),_:1},8,["loading"])]),s(a).length?(_(),R(s(L),{key:1,ref:"scroller",class:"file-list",items:s(a).slice(),"item-size":s(h).first,"key-field":"fullpath","item-secondary-size":s(h).second,gridItems:s(D)},{default:d(({item:n,index:k})=>[l(j,{idx:k,file:n,"cell-width":s(g),"enable-close-icon":"",onCloseIconClick:M=>$(k),"full-screen-preview-image-url":s(x)(n),"enable-right-click-menu":!1},null,8,["idx","file","cell-width","onCloseIconClick","full-screen-preview-image-url"])]),_:1},8,["items","item-size","item-secondary-size","gridItems"])):(_(),w("div",Q,[f("p",W,c(e.$t("batchDownloaDDragAndDropHint")),1)]))],544)}}});const oe=U(J,[["__scopeId","data-v-3d7e6f2d"]]);export{oe as default};

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

View File

@ -1 +0,0 @@
.container[data-v-0c31f6b2]{background:var(--zp-secondary-background);height:100%;overflow:auto;display:flex;flex-direction:column}.container .actions-panel[data-v-0c31f6b2]{padding:8px;background-color:var(--zp-primary-background)}.container .file-list[data-v-0c31f6b2]{flex:1;list-style:none;padding:8px;height:var(--pane-max-height);width:100%}.container .file-list .hint[data-v-0c31f6b2]{text-align:center;font-size:2em;padding:30vh 128px 0}

1
vue/dist/assets/gridView-a16cb29a.js vendored Normal file
View File

@ -0,0 +1 @@
import{F as w,s as y}from"./FileItem-ab6bf09c.js";import{u as k,b as x}from"./index-de6cda7b.js";import{d as F,p as b,bm as h,r as C,c4 as D,c5 as I,o as E,j as V,c,C as z,E as e,U as S,c2 as B,c6 as R,n as A}from"./index-5573ae38.js";import"./index-5e569728.js";import"./shortcut-f44c6c9d.js";import"./_isIterateeCall-bb321415.js";const K=F({__name:"gridView",props:{tabIdx:{},paneIdx:{},id:{},removable:{type:Boolean},allowDragAndDrop:{type:Boolean},files:{},paneKey:{}},setup(p){const o=p,m=b(),{stackViewEl:d}=k().toRefs(),{itemSize:i,gridItems:u,cellWidth:f}=x(),g=h(),s=C(o.files??[]),_=async t=>{const l=B(t);o.allowDragAndDrop&&l&&(s.value=R([...s.value,...l.nodes]))},v=t=>{s.value.splice(t,1)};return D(()=>{m.pageFuncExportMap.set(o.paneKey,{getFiles:()=>I(s.value),setFiles:t=>s.value=t})}),(t,l)=>(E(),V("div",{class:"container",ref_key:"stackViewEl",ref:d,onDrop:_},[c(e(y),{ref:"scroller",class:"file-list",items:s.value.slice(),"item-size":e(i).first,"key-field":"fullpath","item-secondary-size":e(i).second,gridItems:e(u)},{default:z(({item:a,index:r})=>{var n;return[c(w,{idx:r,file:a,"cell-width":e(f),"enable-close-icon":o.removable,onCloseIconClick:T=>v(r),"full-screen-preview-image-url":e(S)(a),"extra-tags":(n=a==null?void 0:a.tags)==null?void 0:n.map(e(g).tagConvert),"enable-right-click-menu":!1},null,8,["idx","file","cell-width","enable-close-icon","onCloseIconClick","full-screen-preview-image-url","extra-tags"])]}),_:1},8,["items","item-size","item-secondary-size","gridItems"])],544))}});const M=A(K,[["__scopeId","data-v-f35f4802"]]);export{M as default};

1
vue/dist/assets/gridView-eef9ac55.css vendored Normal file
View File

@ -0,0 +1 @@
.container[data-v-f35f4802]{background:var(--zp-secondary-background);height:100%;overflow:auto;display:flex;flex-direction:column}.container .actions-panel[data-v-f35f4802]{padding:8px;background-color:var(--zp-primary-background)}.container .file-list[data-v-f35f4802]{flex:1;list-style:none;padding:8px;height:var(--pane-max-height);width:100%}.container .file-list .hint[data-v-f35f4802]{text-align:center;font-size:2em;padding:30vh 128px 0}

View File

@ -1 +0,0 @@
import{F as w,s as y}from"./FileItem-1c8f6c40.js";import{u as k,b as x}from"./index-70bdf486.js";import{d as b,p as F,bk as h,r as D,c2 as I,c3 as C,o as E,j as V,c as n,x as z,y as e,O as S,c0 as B,c4 as R,n as A}from"./index-0cf80cdf.js";import"./index-5acf9bcb.js";import"./shortcut-76a3e0c5.js";import"./_isIterateeCall-6a3838fa.js";const K=b({__name:"gridView",props:{tabIdx:{},paneIdx:{},id:{},removable:{type:Boolean},allowDragAndDrop:{type:Boolean},files:{},paneKey:{}},setup(p){const o=p,m=F(),{stackViewEl:d}=k().toRefs(),{itemSize:i,gridItems:u,cellWidth:f}=x(),g=h(),s=D(o.files??[]),_=async t=>{const l=B(t);o.allowDragAndDrop&&l&&(s.value=R([...s.value,...l.nodes]))},v=t=>{s.value.splice(t,1)};return I(()=>{m.pageFuncExportMap.set(o.paneKey,{getFiles:()=>C(s.value),setFiles:t=>s.value=t})}),(t,l)=>(E(),V("div",{class:"container",ref_key:"stackViewEl",ref:d,onDrop:_},[n(e(y),{ref:"scroller",class:"file-list",items:s.value.slice(),"item-size":e(i).first,"key-field":"fullpath","item-secondary-size":e(i).second,gridItems:e(u)},{default:z(({item:a,index:r})=>{var c;return[n(w,{idx:r,file:a,"cell-width":e(f),"enable-close-icon":o.removable,onCloseIconClick:T=>v(r),"full-screen-preview-image-url":e(S)(a),"extra-tags":(c=a==null?void 0:a.tags)==null?void 0:c.map(e(g).tagConvert),"enable-right-click-menu":!1},null,8,["idx","file","cell-width","enable-close-icon","onCloseIconClick","full-screen-preview-image-url","extra-tags"])]}),_:1},8,["items","item-size","item-secondary-size","gridItems"])],544))}});const N=A(K,[["__scopeId","data-v-0c31f6b2"]]);export{N as default};

View File

@ -1 +0,0 @@
import{aJ as F,r as g,bD as P,bE as S,ao as A,af as R,bn as q,bF as z,bG as L}from"./index-0cf80cdf.js";import{u as O,b as Q,f as j,c as H,d as T,e as U,i as W,h as B}from"./index-70bdf486.js";let K=0;const V=()=>++K,X=(n,i,{dataUpdateStrategy:l="replace"}={})=>{const a=F([""]),c=g(!1),t=g(),o=g(!1);let f=g(-1);const v=new Set,w=e=>{var s;l==="replace"?t.value=e:l==="merge"&&(A((Array.isArray(t.value)||typeof t.value>"u")&&Array.isArray(e),"数据更新策略为合并时仅可用于值为数组的情况"),t.value=[...(s=t==null?void 0:t.value)!==null&&s!==void 0?s:[],...e])},d=e=>S(void 0,void 0,void 0,function*(){if(o.value||c.value&&typeof e>"u")return!1;o.value=!0;const s=V();f.value=s;try{let r;if(typeof e=="number"){if(r=a[e],typeof r!="string")return!1}else r=a[a.length-1];const m=yield n(r);if(v.has(s))return v.delete(s),!1;w(i(m));const u=m.cursor;if((e===a.length-1||typeof e!="number")&&(c.value=!u.has_next,u.has_next)){const y=u.next_cursor||u.next;A(typeof y=="string"),a.push(y)}}finally{f.value===s&&(o.value=!1)}return!0}),p=()=>{v.add(f.value),o.value=!1},x=(e=!1)=>S(void 0,void 0,void 0,function*(){const{refetch:s,force:r}=typeof e=="object"?e:{refetch:e};r&&p(),A(!o.value),a.splice(0,a.length,""),o.value=!1,t.value=void 0,c.value=!1,s&&(yield d())}),I=()=>({next:()=>S(void 0,void 0,void 0,function*(){if(o.value)throw new Error("不允许同时迭代");return{done:!(yield d()),value:t.value}})});return P({abort:p,load:c,next:d,res:t,loading:o,cursorStack:a,reset:x,[Symbol.asyncIterator]:I,iter:{[Symbol.asyncIterator]:I}})},ee=n=>F(X(n,i=>i.files,{dataUpdateStrategy:"merge"})),te=n=>{const i=F(new Set),l=R(()=>(n.res??[]).filter(h=>!i.has(h.fullpath))),a=q(),{stackViewEl:c,multiSelectedIdxs:t,stack:o,scroller:f,props:v}=O({images:l}).toRefs(),{itemSize:w,gridItems:d,cellWidth:p,onScroll:x}=Q({fetchNext:()=>n.next()}),{showMenuIdx:I}=j(),{onFileDragStart:e,onFileDragEnd:s}=H(),{showGenInfo:r,imageGenInfo:m,q:u,onContextMenuClick:y,onFileItemClick:C}=T({openNext:z}),{previewIdx:E,previewing:_,onPreviewVisibleChange:D,previewImgMove:J,canPreview:M}=U({loadNext:()=>n.next()}),G=async(h,b,N)=>{o.value=[{curr:"",files:l.value}],await y(h,b,N)};W("removeFiles",async({paths:h})=>{h.forEach(b=>i.add(b))});const k=()=>{L(l.value)};return{images:l,scroller:f,queue:a,iter:n,onContextMenuClickU:G,stackViewEl:c,previewIdx:E,previewing:_,onPreviewVisibleChange:D,previewImgMove:J,canPreview:M,itemSize:w,gridItems:d,showGenInfo:r,imageGenInfo:m,q:u,onContextMenuClick:y,onFileItemClick:C,showMenuIdx:I,multiSelectedIdxs:t,onFileDragStart:e,onFileDragEnd:s,cellWidth:p,onScroll:x,saveLoadedFileAsJson:k,saveAllFileAsJson:async()=>{for(;!n.load;)await n.next();k()},props:v,...B()}};export{ee as c,te as u};

1
vue/dist/assets/hook-88a5eca7.js vendored Normal file
View File

@ -0,0 +1 @@
import{aL as F,r as g,bF as P,bG as S,ar as A,aj as R,bp as q,bH as L,bI as j}from"./index-5573ae38.js";import{u as z,b as H,f as O,c as Q,d as T,e as U,i as W,h as B}from"./index-de6cda7b.js";let K=0;const V=()=>++K,X=(n,i,{dataUpdateStrategy:l="replace"}={})=>{const a=F([""]),c=g(!1),t=g(),o=g(!1);let f=g(-1);const v=new Set,w=e=>{var s;l==="replace"?t.value=e:l==="merge"&&(A((Array.isArray(t.value)||typeof t.value>"u")&&Array.isArray(e),"数据更新策略为合并时仅可用于值为数组的情况"),t.value=[...(s=t==null?void 0:t.value)!==null&&s!==void 0?s:[],...e])},d=e=>S(void 0,void 0,void 0,function*(){if(o.value||c.value&&typeof e>"u")return!1;o.value=!0;const s=V();f.value=s;try{let r;if(typeof e=="number"){if(r=a[e],typeof r!="string")return!1}else r=a[a.length-1];const m=yield n(r);if(v.has(s))return v.delete(s),!1;w(i(m));const u=m.cursor;if((e===a.length-1||typeof e!="number")&&(c.value=!u.has_next,u.has_next)){const y=u.next_cursor||u.next;A(typeof y=="string"),a.push(y)}}finally{f.value===s&&(o.value=!1)}return!0}),I=()=>{v.add(f.value),o.value=!1},x=(e=!1)=>S(void 0,void 0,void 0,function*(){const{refetch:s,force:r}=typeof e=="object"?e:{refetch:e};r&&I(),A(!o.value),a.splice(0,a.length,""),o.value=!1,t.value=void 0,c.value=!1,s&&(yield d())}),h=()=>({next:()=>S(void 0,void 0,void 0,function*(){if(o.value)throw new Error("不允许同时迭代");return{done:!(yield d()),value:t.value}})});return P({abort:I,load:c,next:d,res:t,loading:o,cursorStack:a,reset:x,[Symbol.asyncIterator]:h,iter:{[Symbol.asyncIterator]:h}})},ee=n=>F(X(n,i=>i.files,{dataUpdateStrategy:"merge"})),te=n=>{const i=F(new Set),l=R(()=>(n.res??[]).filter(p=>!i.has(p.fullpath))),a=q(),{stackViewEl:c,multiSelectedIdxs:t,stack:o,scroller:f,props:v}=z({images:l}).toRefs(),{itemSize:w,gridItems:d,cellWidth:I,onScroll:x}=H({fetchNext:()=>n.next()}),{showMenuIdx:h}=O(),{onFileDragStart:e,onFileDragEnd:s}=Q(),{showGenInfo:r,imageGenInfo:m,q:u,onContextMenuClick:y,onFileItemClick:C}=T({openNext:L}),{previewIdx:_,previewing:E,onPreviewVisibleChange:M,previewImgMove:D,canPreview:G}=U({loadNext:()=>n.next()}),J=async(p,b,N)=>{o.value=[{curr:"",files:l.value}],await y(p,b,N)};W("removeFiles",async({paths:p})=>{p.forEach(b=>i.add(b))});const k=()=>{j(l.value)};return{images:l,scroller:f,queue:a,iter:n,onContextMenuClickU:J,stackViewEl:c,previewIdx:_,previewing:E,onPreviewVisibleChange:M,previewImgMove:D,canPreview:G,itemSize:w,gridItems:d,showGenInfo:r,imageGenInfo:m,q:u,onContextMenuClick:y,onFileItemClick:C,showMenuIdx:h,multiSelectedIdxs:t,onFileDragStart:e,onFileDragEnd:s,cellWidth:I,onScroll:x,saveLoadedFileAsJson:k,saveAllFileAsJson:async()=>{for(;!n.load;)await n.next();k()},props:v,...B()}};export{ee as c,te as u};

1
vue/dist/assets/index-10e5d5f2.js vendored Normal file
View File

@ -0,0 +1 @@
import{d as x,p as $,an as g,c7 as b,r as w,o as p,j as d,k as a,c as l,C as i,l as u,t as n,F as B,K as I,E as m,as as W,W as _,X as v,a2 as D,a3 as F,c8 as N,n as R}from"./index-5573ae38.js";const V={class:"container"},E={class:"actions"},K={class:"uni-desc"},L={class:"snapshot"},j=x({__name:"index",props:{tabIdx:{},paneIdx:{},id:{},paneKey:{}},setup(q){const h=$(),t=g(),f=e=>{h.tabList=W(e.tabs)},k=b(async e=>{await N(`workspace_snapshot_${e.id}`),t.snapshots=t.snapshots.filter(c=>c.id!==e.id),_.success(v("deleteSuccess"))}),o=w(""),C=async()=>{if(!o.value){_.error(v("nameRequired"));return}const e=t.createSnapshot(o.value);await t.addSnapshot(e),_.success(v("saveCompleted"))};return(e,c)=>{const y=D,r=F;return p(),d("div",V,[a("div",E,[l(y,{value:o.value,"onUpdate:value":c[0]||(c[0]=s=>o.value=s),placeholder:e.$t("name"),style:{"max-width":"300px"}},null,8,["value","placeholder"]),l(r,{type:"primary",onClick:C},{default:i(()=>[u(n(e.$t("saveWorkspaceSnapshot")),1)]),_:1})]),a("p",K,n(e.$t("WorkspaceSnapshotDesc")),1),a("ul",L,[(p(!0),d(B,null,I(m(t).snapshots,s=>(p(),d("li",{key:s.id},[a("div",null,[a("span",null,n(s.name),1)]),a("div",null,[l(r,{onClick:S=>f(s)},{default:i(()=>[u(n(e.$t("restore")),1)]),_:2},1032,["onClick"]),l(r,{onClick:S=>m(k)(s)},{default:i(()=>[u(n(e.$t("remove")),1)]),_:2},1032,["onClick"])])]))),128))])])}}});const G=R(j,[["__scopeId","data-v-2c44013c"]]);export{G as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
vue/dist/assets/index-5e569728.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.container[data-v-e55e3025]{background:var(--zp-secondary-background);height:100%;overflow:auto;display:flex;flex-direction:column;padding:16px}.container .actions[data-v-e55e3025]{margin-bottom:16px}.container .actions *[data-v-e55e3025]{margin-right:10px}.snapshot[data-v-e55e3025]{list-style:none;padding:0;margin:0;width:512px}.snapshot li[data-v-e55e3025]{display:flex;justify-content:space-between;align-items:center;padding:10px;background-color:var(--zp-secondary-variant-background);border-radius:4px;margin-bottom:10px;transition:all .3s ease;border-bottom:2px solid var(--zp-luminous-deep)}.snapshot li[data-v-e55e3025]:hover{border-bottom:2px solid var(--zp-luminous)}.snapshot li div[data-v-e55e3025]:first-child{flex-grow:1;font-weight:700}.snapshot li div[data-v-e55e3025]:last-child{display:flex;gap:10px}

View File

@ -1 +0,0 @@
import{d as S,p as $,aj as g,c5 as b,r as w,o as p,j as d,k as a,c as l,x as i,l as u,t as n,F as B,G as I,y as m,ap as R,R as _,T as v,Z as D,$ as F,c6 as N,n as V}from"./index-0cf80cdf.js";const W={class:"container"},j={class:"actions"},G={class:"uni-desc"},L={class:"snapshot"},T=S({__name:"index",props:{tabIdx:{},paneIdx:{},id:{},paneKey:{}},setup(q){const h=$(),t=g(),f=e=>{h.tabList=R(e.tabs)},k=b(async e=>{await N(`workspace_snapshot_${e.id}`),t.snapshots=t.snapshots.filter(c=>c.id!==e.id),_.success(v("deleteSuccess"))}),o=w(""),y=async()=>{if(!o.value){_.error(v("nameRequired"));return}const e=t.createSnapshot(o.value);await t.addSnapshot(e),_.success(v("saveCompleted"))};return(e,c)=>{const C=D,r=F;return p(),d("div",W,[a("div",j,[l(C,{value:o.value,"onUpdate:value":c[0]||(c[0]=s=>o.value=s),placeholder:e.$t("name"),style:{"max-width":"300px"}},null,8,["value","placeholder"]),l(r,{type:"primary",onClick:y},{default:i(()=>[u(n(e.$t("saveWorkspaceSnapshot")),1)]),_:1})]),a("p",G,n(e.$t("WorkspaceSnapshotDesc")),1),a("ul",L,[(p(!0),d(B,null,I(m(t).snapshots,s=>(p(),d("li",{key:s.id},[a("div",null,[a("span",null,n(s.name),1)]),a("div",null,[l(r,{onClick:x=>f(s)},{default:i(()=>[u(n(e.$t("restore")),1)]),_:2},1032,["onClick"]),l(r,{onClick:x=>m(k)(s)},{default:i(()=>[u(n(e.$t("remove")),1)]),_:2},1032,["onClick"])])]))),128))])])}}});const E=V(T,[["__scopeId","data-v-e55e3025"]]);export{E as default};

1
vue/dist/assets/index-bd568ec0.css vendored Normal file
View File

@ -0,0 +1 @@
.container[data-v-2c44013c]{background:var(--zp-secondary-background);height:100%;overflow:auto;display:flex;flex-direction:column;padding:16px}.container .actions[data-v-2c44013c]{margin-bottom:16px}.container .actions *[data-v-2c44013c]{margin-right:10px}.snapshot[data-v-2c44013c]{list-style:none;padding:0;margin:0;width:512px}.snapshot li[data-v-2c44013c]{display:flex;justify-content:space-between;align-items:center;padding:10px;background-color:var(--zp-secondary-variant-background);border-radius:4px;margin-bottom:10px;transition:all .3s ease;border-bottom:2px solid var(--zp-luminous-deep)}.snapshot li[data-v-2c44013c]:hover{border-bottom:2px solid var(--zp-luminous)}.snapshot li div[data-v-2c44013c]:first-child{flex-grow:1;font-weight:700}.snapshot li div[data-v-2c44013c]:last-child{display:flex;gap:10px}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

Before

Width:  |  Height:  |  Size: 195 B

After

Width:  |  Height:  |  Size: 195 B

View File

@ -1 +1 @@
.container[data-v-e1531e89]{background:var(--zp-secondary-background);height:100%;overflow:auto;display:flex;flex-direction:column}.container .actions-panel[data-v-e1531e89]{padding:8px;background-color:var(--zp-primary-background)}.container .refresh-button[data-v-e1531e89]{position:absolute;top:90%;left:50%;transform:translate(-50%,-50%);z-index:99;background:white;border-radius:9999px;box-shadow:0 0 20px var(--zp-secondary);padding:4px;display:flex;align-items:center;gap:8px}.container .file-list[data-v-e1531e89]{flex:1;list-style:none;padding:8px;height:var(--pane-max-height);width:100%}.container .file-list .hint[data-v-e1531e89]{text-align:center;font-size:2em;padding:30vh 128px 0}
.container[data-v-49082269]{background:var(--zp-secondary-background);height:100%;overflow:auto;display:flex;flex-direction:column}.container .actions-panel[data-v-49082269]{padding:8px;background-color:var(--zp-primary-background)}.container .refresh-button[data-v-49082269]{position:absolute;top:90%;left:50%;transform:translate(-50%,-50%);z-index:99;background:white;border-radius:9999px;box-shadow:0 0 20px var(--zp-secondary);padding:4px;display:flex;align-items:center;gap:8px}.container .file-list[data-v-49082269]{flex:1;list-style:none;padding:8px;height:var(--pane-max-height);width:100%}.container .file-list .hint[data-v-49082269]{text-align:center;font-size:2em;padding:30vh 128px 0}

View File

@ -0,0 +1 @@
import{d as Y,p as ee,r as F,aC as te,aD as le,v as se,c9 as ie,o as v,j as N,c as s,E as e,k as g,C as n,l as k,t as u,G as R,I as oe,H as ae,U as ne,V as A,m as $,B as re,W as w,X as ce,ca as de,a3 as ue,a1 as me,Z as fe,n as pe}from"./index-5573ae38.js";import{F as ve,s as ge}from"./FileItem-ab6bf09c.js";import{u as ke,g as we,c as he,b as Ce,d as Se,e as _e,o as z}from"./index-de6cda7b.js";import{M as Ie,L as ye,R as xe,f as be}from"./MultiSelectKeep-e448072f.js";import"./index-5e569728.js";import"./shortcut-f44c6c9d.js";import"./_isIterateeCall-bb321415.js";/* empty css */const Ve={class:"refresh-button"},Me={class:"hint"},Te={key:0,class:"preview-switch"},Fe=Y({__name:"randomImage",props:{tabIdx:{},paneIdx:{},id:{},paneKey:{}},setup(Ne){const B=ee(),m=F(!1),l=F([]),r=l,h=te(`${le}randomImageSettingNotificationShown`,!1),P=()=>{h.value||(w.info({content:ce("randomImageSettingNotification"),duration:6,key:"randomImageSetting"}),h.value=!0)},f=async()=>{try{m.value=!0;const i=await de();i.length===0&&w.warn("No data, please generate index in image search page first"),l.value=i}finally{m.value=!1,_()}},C=()=>{if(l.value.length===0){w.warn("没有图片可以浏览");return}z(l.value,a.value||0)};se(()=>{f(),setTimeout(()=>{P()},2e3)});const{stackViewEl:D,multiSelectedIdxs:p,stack:E,scroller:G}=ke({images:l}).toRefs(),{onClearAllSelected:U,onSelectAll:K,onReverseSelect:L}=we();he();const{itemSize:S,gridItems:O,cellWidth:W,onScroll:_}=Ce(),{showGenInfo:c,imageGenInfo:I,q:H,onContextMenuClick:j,onFileItemClick:q}=Se({openNext:ie}),{previewIdx:a,previewing:y,onPreviewVisibleChange:Q,previewImgMove:x,canPreview:b}=_e(),V=async(i,t,d)=>{E.value=[{curr:"",files:l.value}],await j(i,t,d)};return(i,t)=>{var M;const d=ue,X=me,Z=fe;return v(),N("div",{class:"container",ref_key:"stackViewEl",ref:D},[s(Ie,{show:!!e(p).length||e(B).keepMultiSelect,onClearAllSelected:e(U),onSelectAll:e(K),onReverseSelect:e(L)},null,8,["show","onClearAllSelected","onSelectAll","onReverseSelect"]),g("div",Ve,[s(d,{onClick:f,onTouchstart:R(f,["prevent"]),type:"primary",loading:m.value,shape:"round"},{default:n(()=>[k(u(i.$t("shuffle")),1)]),_:1},8,["onTouchstart","loading"]),s(d,{onClick:C,onTouchstart:R(C,["prevent"]),type:"default",disabled:!((M=l.value)!=null&&M.length),shape:"round"},{default:n(()=>[k(u(i.$t("tiktokView")),1)]),_:1},8,["onTouchstart","disabled"])]),s(Z,{visible:e(c),"onUpdate:visible":t[1]||(t[1]=o=>ae(c)?c.value=o:null),width:"70vw","mask-closable":"",onOk:t[2]||(t[2]=o=>c.value=!1)},{cancelText:n(()=>[]),default:n(()=>[s(X,{active:"",loading:!e(H).isIdle},{default:n(()=>[g("div",{style:{width:"100%","word-break":"break-all","white-space":"pre-line","max-height":"70vh",overflow:"auto"},onDblclick:t[0]||(t[0]=o=>e(oe)(e(I)))},[g("div",Me,u(i.$t("doubleClickToCopy")),1),k(" "+u(e(I)),1)],32)]),_:1},8,["loading"])]),_:1},8,["visible"]),s(e(ge),{ref_key:"scroller",ref:G,class:"file-list",items:l.value.slice(),"item-size":e(S).first,"key-field":"fullpath","item-secondary-size":e(S).second,gridItems:e(O),onScroll:e(_)},{default:n(({item:o,index:T})=>[s(ve,{idx:T,file:o,"cell-width":e(W),"full-screen-preview-image-url":e(r)[e(a)]?e(ne)(e(r)[e(a)]):"",onContextMenuClick:V,onPreviewVisibleChange:e(Q),"is-selected-mutil-files":e(p).length>1,selected:e(p).includes(T),onFileItemClick:e(q),onTiktokView:(Re,J)=>e(z)(l.value,J)},null,8,["idx","file","cell-width","full-screen-preview-image-url","onPreviewVisibleChange","is-selected-mutil-files","selected","onFileItemClick","onTiktokView"])]),_:1},8,["items","item-size","item-secondary-size","gridItems","onScroll"]),e(y)?(v(),N("div",Te,[s(e(ye),{onClick:t[3]||(t[3]=o=>e(x)("prev")),class:A({disable:!e(b)("prev")})},null,8,["class"]),s(e(xe),{onClick:t[4]||(t[4]=o=>e(x)("next")),class:A({disable:!e(b)("next")})},null,8,["class"])])):$("",!0),e(y)&&e(r)&&e(r)[e(a)]?(v(),re(be,{key:1,file:e(r)[e(a)],idx:e(a),onContextMenuClick:V},null,8,["file","idx"])):$("",!0)],512)}}});const Ue=pe(Fe,[["__scopeId","data-v-49082269"]]);export{Ue as default};

View File

@ -1 +0,0 @@
import{d as Z,p as ee,r as R,az as te,aA as le,ak as se,c7 as ie,o as v,j as A,c as s,y as e,k as g,x as a,l as k,t as u,z as F,C as oe,B as ne,O as ae,Q as N,m as $,v as re,R as w,T as ce,c8 as de,$ as ue,Y as me,V as fe,n as pe}from"./index-0cf80cdf.js";import{F as ve,s as ge}from"./FileItem-1c8f6c40.js";import{u as ke,g as we,c as he,b as Ce,d as Se,e as _e,o as z}from"./index-70bdf486.js";import{M as Ie,L as ye,R as xe,f as be}from"./MultiSelectKeep-9dd111ee.js";import"./index-5acf9bcb.js";import"./shortcut-76a3e0c5.js";import"./_isIterateeCall-6a3838fa.js";/* empty css */const Ve={class:"refresh-button"},Me={class:"hint"},Te={key:0,class:"preview-switch"},Re=Z({__name:"randomImage",props:{tabIdx:{},paneIdx:{},id:{},paneKey:{}},setup(Ae){const B=ee(),m=R(!1),l=R([]),r=l,h=te(`${le}randomImageSettingNotificationShown`,!1),P=()=>{h.value||(w.info({content:ce("randomImageSettingNotification"),duration:6,key:"randomImageSetting"}),h.value=!0)},f=async()=>{try{m.value=!0;const i=await de();i.length===0&&w.warn("No data, please generate index in image search page first"),l.value=i}finally{m.value=!1,_()}},C=()=>{if(l.value.length===0){w.warn("没有图片可以浏览");return}z(l.value,n.value||0)};se(()=>{f(),setTimeout(()=>{P()},2e3)});const{stackViewEl:O,multiSelectedIdxs:p,stack:D,scroller:E}=ke({images:l}).toRefs(),{onClearAllSelected:G,onSelectAll:K,onReverseSelect:L}=we();he();const{itemSize:S,gridItems:U,cellWidth:Q,onScroll:_}=Ce(),{showGenInfo:c,imageGenInfo:I,q:W,onContextMenuClick:j,onFileItemClick:q}=Se({openNext:ie}),{previewIdx:n,previewing:y,onPreviewVisibleChange:H,previewImgMove:x,canPreview:b}=_e(),V=async(i,t,d)=>{D.value=[{curr:"",files:l.value}],await j(i,t,d)};return(i,t)=>{var M;const d=ue,Y=me,J=fe;return v(),A("div",{class:"container",ref_key:"stackViewEl",ref:O},[s(Ie,{show:!!e(p).length||e(B).keepMultiSelect,onClearAllSelected:e(G),onSelectAll:e(K),onReverseSelect:e(L)},null,8,["show","onClearAllSelected","onSelectAll","onReverseSelect"]),g("div",Ve,[s(d,{onClick:f,onTouchstart:F(f,["prevent"]),type:"primary",loading:m.value,shape:"round"},{default:a(()=>[k(u(i.$t("shuffle")),1)]),_:1},8,["onTouchstart","loading"]),s(d,{onClick:C,onTouchstart:F(C,["prevent"]),type:"default",disabled:!((M=l.value)!=null&&M.length),shape:"round"},{default:a(()=>[k(u(i.$t("tiktokView")),1)]),_:1},8,["onTouchstart","disabled"])]),s(J,{visible:e(c),"onUpdate:visible":t[1]||(t[1]=o=>ne(c)?c.value=o:null),width:"70vw","mask-closable":"",onOk:t[2]||(t[2]=o=>c.value=!1)},{cancelText:a(()=>[]),default:a(()=>[s(Y,{active:"",loading:!e(W).isIdle},{default:a(()=>[g("div",{style:{width:"100%","word-break":"break-all","white-space":"pre-line","max-height":"70vh",overflow:"auto"},onDblclick:t[0]||(t[0]=o=>e(oe)(e(I)))},[g("div",Me,u(i.$t("doubleClickToCopy")),1),k(" "+u(e(I)),1)],32)]),_:1},8,["loading"])]),_:1},8,["visible"]),s(e(ge),{ref_key:"scroller",ref:E,class:"file-list",items:l.value.slice(),"item-size":e(S).first,"key-field":"fullpath","item-secondary-size":e(S).second,gridItems:e(U),onScroll:e(_)},{default:a(({item:o,index:T})=>[s(ve,{idx:T,file:o,"cell-width":e(Q),"full-screen-preview-image-url":e(r)[e(n)]?e(ae)(e(r)[e(n)]):"",onContextMenuClick:V,onPreviewVisibleChange:e(H),"is-selected-mutil-files":e(p).length>1,selected:e(p).includes(T),onFileItemClick:e(q),onTiktokView:(Fe,X)=>e(z)(l.value,X)},null,8,["idx","file","cell-width","full-screen-preview-image-url","onPreviewVisibleChange","is-selected-mutil-files","selected","onFileItemClick","onTiktokView"])]),_:1},8,["items","item-size","item-secondary-size","gridItems","onScroll"]),e(y)?(v(),A("div",Te,[s(e(ye),{onClick:t[3]||(t[3]=o=>e(x)("prev")),class:N({disable:!e(b)("prev")})},null,8,["class"]),s(e(xe),{onClick:t[4]||(t[4]=o=>e(x)("next")),class:N({disable:!e(b)("next")})},null,8,["class"])])):$("",!0),e(y)&&e(r)&&e(r)[e(n)]?(v(),re(be,{key:1,file:e(r)[e(n)],idx:e(n),onContextMenuClick:V},null,8,["file","idx"])):$("",!0)],512)}}});const Ge=pe(Re,[["__scopeId","data-v-e1531e89"]]);export{Ge as default};

View File

@ -1 +0,0 @@
[data-v-fff181dd] .ant-row .ant-col:nth-child(1){font-weight:700}.record-container[data-v-fff181dd]{list-style:none;padding:8px;margin:16px;max-height:50vh;overflow:auto}.record[data-v-fff181dd]{display:flex;flex-direction:row;justify-content:space-between;align-items:center;padding:10px;border-bottom:1px solid var(--zp-tertiary);position:relative;flex-wrap:nowrap;transition:all .3s ease}.record[data-v-fff181dd]:hover{background:var(--zp-secondary-background)}.record .rec-actions[data-v-fff181dd]{user-select:none;display:flex;gap:8px}.record .pin[data-v-fff181dd]{cursor:pointer;padding:4px 8px;border-radius:4px;transition:all .3s ease}.record .pin[data-v-fff181dd]:hover{background:var(--zp-primary-background)}

View File

@ -1 +0,0 @@
import{bH as f,bI as y,bJ as v,c as d,A as P,d as w,o as a,j as c,k as r,F as b,G as S,ad as O,x as V,l as u,t as p,y as $,$ as R,n as x,an as H,az as _,aA as g}from"./index-0cf80cdf.js";const A=f(y),D=f(v);var L={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M878.3 392.1L631.9 145.7c-6.5-6.5-15-9.7-23.5-9.7s-17 3.2-23.5 9.7L423.8 306.9c-12.2-1.4-24.5-2-36.8-2-73.2 0-146.4 24.1-206.5 72.3-15.4 12.3-16.6 35.4-2.7 49.4l181.7 181.7-215.4 215.2a15.8 15.8 0 00-4.6 9.8l-3.4 37.2c-.9 9.4 6.6 17.4 15.9 17.4.5 0 1 0 1.5-.1l37.2-3.4c3.7-.3 7.2-2 9.8-4.6l215.4-215.4 181.7 181.7c6.5 6.5 15 9.7 23.5 9.7 9.7 0 19.3-4.2 25.9-12.4 56.3-70.3 79.7-158.3 70.2-243.4l161.1-161.1c12.9-12.8 12.9-33.8 0-46.8z"}}]},name:"pushpin",theme:"filled"};const z=L;function h(t){for(var e=1;e<arguments.length;e++){var s=arguments[e]!=null?Object(arguments[e]):{},n=Object.keys(s);typeof Object.getOwnPropertySymbols=="function"&&(n=n.concat(Object.getOwnPropertySymbols(s).filter(function(i){return Object.getOwnPropertyDescriptor(s,i).enumerable}))),n.forEach(function(i){F(t,i,s[i])})}return t}function F(t,e,s){return e in t?Object.defineProperty(t,e,{value:s,enumerable:!0,configurable:!0,writable:!0}):t[e]=s,t}var l=function(e,s){var n=h({},e,s.attrs);return d(P,h({},n,{icon:z}),null)};l.displayName="PushpinFilled";l.inheritAttrs=!1;const N=l,C={class:"record-container"},I={style:{flex:"1"}},k={class:"rec-actions"},j=["onClick"],B=w({__name:"HistoryRecord",props:{records:{}},emits:["reuseRecord"],setup(t){return(e,s)=>{const n=R;return a(),c("div",null,[r("ul",C,[(a(!0),c(b,null,S(e.records.getRecords(),i=>(a(),c("li",{key:i.id,class:"record"},[r("div",I,[O(e.$slots,"default",{record:i},void 0,!0)]),r("div",k,[d(n,{onClick:m=>e.$emit("reuseRecord",i),type:"primary"},{default:V(()=>[u(p(e.$t("restore")),1)]),_:2},1032,["onClick"]),r("div",{class:"pin",onClick:m=>e.records.switchPin(i)},[d($(N)),u(" "+p(e.records.isPinned(i)?e.$t("unpin"):e.$t("pin")),1)],8,j)])]))),128))])])}}});const E=x(B,[["__scopeId","data-v-fff181dd"]]);class o{constructor(e=128,s=[],n=[]){this.maxLength=e,this.records=s,this.pinnedValues=n}isPinned(e){return this.pinnedValues.some(s=>s.id===e.id)}add(e){this.records.length>=this.maxLength&&this.records.pop(),this.records.unshift({...e,id:H()+Date.now(),time:new Date().toLocaleString()})}pin(e){const s=this.records.findIndex(n=>n.id===e.id);s!==-1&&this.records.splice(s,1),this.pinnedValues.push(e)}unpin(e){const s=this.pinnedValues.findIndex(n=>n.id===e.id);s!==-1&&this.pinnedValues.splice(s,1),this.records.unshift(e)}switchPin(e){this.isPinned(e)?this.unpin(e):this.pin(e)}getRecords(){return[...this.pinnedValues,...this.records]}getPinnedValues(){return this.pinnedValues}}const q=_(`${g}fuzzy-search-HistoryRecord`,new o,{serializer:{read:t=>{const e=JSON.parse(t);return new o(e.maxLength,e.records,e.pinnedValues)},write:JSON.stringify}}),G=_(`${g}tag-search-HistoryRecord`,new o,{serializer:{read:t=>{const e=JSON.parse(t);return new o(e.maxLength,e.records,e.pinnedValues)},write:JSON.stringify}});export{E as H,D as _,A as a,q as f,G as t};

View File

@ -0,0 +1 @@
import{bJ as f,bK as y,bL as v,c as d,A as P,d as w,o,j as c,k as r,F as b,K as S,ah as O,C as V,l as u,t as p,E as $,a3 as R,n as L,aq as x,aC as _,aD as g}from"./index-5573ae38.js";const J=f(y),A=f(v);var C={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M878.3 392.1L631.9 145.7c-6.5-6.5-15-9.7-23.5-9.7s-17 3.2-23.5 9.7L423.8 306.9c-12.2-1.4-24.5-2-36.8-2-73.2 0-146.4 24.1-206.5 72.3-15.4 12.3-16.6 35.4-2.7 49.4l181.7 181.7-215.4 215.2a15.8 15.8 0 00-4.6 9.8l-3.4 37.2c-.9 9.4 6.6 17.4 15.9 17.4.5 0 1 0 1.5-.1l37.2-3.4c3.7-.3 7.2-2 9.8-4.6l215.4-215.4 181.7 181.7c6.5 6.5 15 9.7 23.5 9.7 9.7 0 19.3-4.2 25.9-12.4 56.3-70.3 79.7-158.3 70.2-243.4l161.1-161.1c12.9-12.8 12.9-33.8 0-46.8z"}}]},name:"pushpin",theme:"filled"};const H=C;function h(t){for(var e=1;e<arguments.length;e++){var s=arguments[e]!=null?Object(arguments[e]):{},n=Object.keys(s);typeof Object.getOwnPropertySymbols=="function"&&(n=n.concat(Object.getOwnPropertySymbols(s).filter(function(i){return Object.getOwnPropertyDescriptor(s,i).enumerable}))),n.forEach(function(i){F(t,i,s[i])})}return t}function F(t,e,s){return e in t?Object.defineProperty(t,e,{value:s,enumerable:!0,configurable:!0,writable:!0}):t[e]=s,t}var l=function(e,s){var n=h({},e,s.attrs);return d(P,h({},n,{icon:H}),null)};l.displayName="PushpinFilled";l.inheritAttrs=!1;const N=l,z={class:"record-container"},k={style:{flex:"1"}},I={class:"rec-actions"},j=["onClick"],B=w({__name:"HistoryRecord",props:{records:{}},emits:["reuseRecord"],setup(t){return(e,s)=>{const n=R;return o(),c("div",null,[r("ul",z,[(o(!0),c(b,null,S(e.records.getRecords(),i=>(o(),c("li",{key:i.id,class:"record"},[r("div",k,[O(e.$slots,"default",{record:i},void 0,!0)]),r("div",I,[d(n,{onClick:m=>e.$emit("reuseRecord",i),type:"primary"},{default:V(()=>[u(p(e.$t("restore")),1)]),_:2},1032,["onClick"]),r("div",{class:"pin",onClick:m=>e.records.switchPin(i)},[d($(N)),u(" "+p(e.records.isPinned(i)?e.$t("unpin"):e.$t("pin")),1)],8,j)])]))),128))])])}}});const E=L(B,[["__scopeId","data-v-834a248f"]]);class a{constructor(e=128,s=[],n=[]){this.maxLength=e,this.records=s,this.pinnedValues=n}isPinned(e){return this.pinnedValues.some(s=>s.id===e.id)}add(e){this.records.length>=this.maxLength&&this.records.pop(),this.records.unshift({...e,id:x()+Date.now(),time:new Date().toLocaleString()})}pin(e){const s=this.records.findIndex(n=>n.id===e.id);s!==-1&&this.records.splice(s,1),this.pinnedValues.push(e)}unpin(e){const s=this.pinnedValues.findIndex(n=>n.id===e.id);s!==-1&&this.pinnedValues.splice(s,1),this.records.unshift(e)}switchPin(e){this.isPinned(e)?this.unpin(e):this.pin(e)}getRecords(){return[...this.pinnedValues,...this.records]}getPinnedValues(){return this.pinnedValues}}const q=_(`${g}fuzzy-search-HistoryRecord`,new a,{serializer:{read:t=>{const e=JSON.parse(t);return new a(e.maxLength,e.records,e.pinnedValues)},write:JSON.stringify}}),M=_(`${g}tag-search-HistoryRecord`,new a,{serializer:{read:t=>{const e=JSON.parse(t);return new a(e.maxLength,e.records,e.pinnedValues)},write:JSON.stringify}});export{E as H,A as _,J as a,q as f,M as t};

View File

@ -0,0 +1 @@
[data-v-834a248f] .ant-row .ant-col:nth-child(1){font-weight:700}.record-container[data-v-834a248f]{list-style:none;padding:8px;margin:16px;max-height:50vh;overflow:auto}.record[data-v-834a248f]{display:flex;flex-direction:row;justify-content:space-between;align-items:center;padding:10px;border-bottom:1px solid var(--zp-tertiary);position:relative;flex-wrap:nowrap;transition:all .3s ease}.record[data-v-834a248f]:hover{background:var(--zp-secondary-background)}.record .rec-actions[data-v-834a248f]{user-select:none;display:flex;gap:8px}.record .pin[data-v-834a248f]{cursor:pointer;padding:4px 8px;border-radius:4px;transition:all .3s ease}.record .pin[data-v-834a248f]:hover{background:var(--zp-primary-background)}

View File

@ -1 +1 @@
.ant-slider{box-sizing:border-box;color:#000000d9;font-size:14px;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:"tnum";position:relative;height:12px;margin:10px 6px;padding:4px 0;cursor:pointer;touch-action:none}.ant-slider-vertical{width:12px;height:100%;margin:6px 10px;padding:0 4px}.ant-slider-vertical .ant-slider-rail{width:4px;height:100%}.ant-slider-vertical .ant-slider-track{width:4px}.ant-slider-vertical .ant-slider-handle{margin-top:-6px;margin-left:-5px}.ant-slider-vertical .ant-slider-mark{top:0;left:12px;width:18px;height:100%}.ant-slider-vertical .ant-slider-mark-text{left:4px;white-space:nowrap}.ant-slider-vertical .ant-slider-step{width:4px;height:100%}.ant-slider-vertical .ant-slider-dot{top:auto;left:2px;margin-bottom:-4px}.ant-slider-tooltip .ant-tooltip-inner{min-width:unset}.ant-slider-rtl.ant-slider-vertical .ant-slider-handle{margin-right:-5px;margin-left:0}.ant-slider-rtl.ant-slider-vertical .ant-slider-mark{right:12px;left:auto}.ant-slider-rtl.ant-slider-vertical .ant-slider-mark-text{right:4px;left:auto}.ant-slider-rtl.ant-slider-vertical .ant-slider-dot{right:2px;left:auto}.ant-slider-with-marks{margin-bottom:28px}.ant-slider-rail{position:absolute;width:100%;height:4px;background-color:#f5f5f5;border-radius:2px;transition:background-color .3s}.ant-slider-track{position:absolute;height:4px;background-color:#f7ae83;border-radius:2px;transition:background-color .3s}.ant-slider-handle{position:absolute;width:14px;height:14px;margin-top:-5px;background-color:#fff;border:solid 2px #f7ae83;border-radius:50%;box-shadow:0;cursor:pointer;transition:border-color .3s,box-shadow .6s,transform .3s cubic-bezier(.18,.89,.32,1.28)}.ant-slider-handle-dragging.ant-slider-handle-dragging.ant-slider-handle-dragging{border-color:#d9653b;box-shadow:0 0 0 5px #d03f0a1f}.ant-slider-handle:focus{border-color:#d9653b;outline:none;box-shadow:0 0 0 5px #d03f0a1f}.ant-slider-handle.ant-tooltip-open{border-color:#d03f0a}.ant-slider:hover .ant-slider-rail{background-color:#e1e1e1}.ant-slider:hover .ant-slider-track{background-color:#eb8857}.ant-slider:hover .ant-slider-handle:not(.ant-tooltip-open){border-color:#eb8857}.ant-slider-mark{position:absolute;top:14px;left:0;width:100%;font-size:14px}.ant-slider-mark-text{position:absolute;display:inline-block;color:#00000073;text-align:center;word-break:keep-all;cursor:pointer;user-select:none}.ant-slider-mark-text-active{color:#000000d9}.ant-slider-step{position:absolute;width:100%;height:4px;background:transparent}.ant-slider-dot{position:absolute;top:-2px;width:8px;height:8px;margin-left:-4px;background-color:#fff;border:2px solid #f0f0f0;border-radius:50%;cursor:pointer}.ant-slider-dot:first-child{margin-left:-4px}.ant-slider-dot:last-child{margin-left:-4px}.ant-slider-dot-active{border-color:#e89f85}.ant-slider-disabled{cursor:not-allowed}.ant-slider-disabled .ant-slider-rail{background-color:#f5f5f5!important}.ant-slider-disabled .ant-slider-track{background-color:#00000040!important}.ant-slider-disabled .ant-slider-handle,.ant-slider-disabled .ant-slider-dot{background-color:#fff;border-color:#00000040!important;box-shadow:none;cursor:not-allowed}.ant-slider-disabled .ant-slider-mark-text,.ant-slider-disabled .ant-slider-dot{cursor:not-allowed!important}.ant-slider-rtl{direction:rtl}.ant-slider-rtl .ant-slider-mark{right:0;left:auto}.ant-slider-rtl .ant-slider-dot,.ant-slider-rtl .ant-slider-dot:first-child{margin-right:-4px;margin-left:0}.ant-slider-rtl .ant-slider-dot:last-child{margin-right:-4px;margin-left:0}.num-input[data-v-bd954eda]{display:flex}.num-input .slide[data-v-bd954eda]{flex:1;min-width:128px;max-width:256px;margin-left:8px}
.ant-slider{box-sizing:border-box;color:#000000d9;font-size:14px;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:"tnum";position:relative;height:12px;margin:10px 6px;padding:4px 0;cursor:pointer;touch-action:none}.ant-slider-vertical{width:12px;height:100%;margin:6px 10px;padding:0 4px}.ant-slider-vertical .ant-slider-rail{width:4px;height:100%}.ant-slider-vertical .ant-slider-track{width:4px}.ant-slider-vertical .ant-slider-handle{margin-top:-6px;margin-left:-5px}.ant-slider-vertical .ant-slider-mark{top:0;left:12px;width:18px;height:100%}.ant-slider-vertical .ant-slider-mark-text{left:4px;white-space:nowrap}.ant-slider-vertical .ant-slider-step{width:4px;height:100%}.ant-slider-vertical .ant-slider-dot{top:auto;left:2px;margin-bottom:-4px}.ant-slider-tooltip .ant-tooltip-inner{min-width:unset}.ant-slider-rtl.ant-slider-vertical .ant-slider-handle{margin-right:-5px;margin-left:0}.ant-slider-rtl.ant-slider-vertical .ant-slider-mark{right:12px;left:auto}.ant-slider-rtl.ant-slider-vertical .ant-slider-mark-text{right:4px;left:auto}.ant-slider-rtl.ant-slider-vertical .ant-slider-dot{right:2px;left:auto}.ant-slider-with-marks{margin-bottom:28px}.ant-slider-rail{position:absolute;width:100%;height:4px;background-color:#f5f5f5;border-radius:2px;transition:background-color .3s}.ant-slider-track{position:absolute;height:4px;background-color:#f7ae83;border-radius:2px;transition:background-color .3s}.ant-slider-handle{position:absolute;width:14px;height:14px;margin-top:-5px;background-color:#fff;border:solid 2px #f7ae83;border-radius:50%;box-shadow:0;cursor:pointer;transition:border-color .3s,box-shadow .6s,transform .3s cubic-bezier(.18,.89,.32,1.28)}.ant-slider-handle-dragging.ant-slider-handle-dragging.ant-slider-handle-dragging{border-color:#d9653b;box-shadow:0 0 0 5px #d03f0a1f}.ant-slider-handle:focus{border-color:#d9653b;outline:none;box-shadow:0 0 0 5px #d03f0a1f}.ant-slider-handle.ant-tooltip-open{border-color:#d03f0a}.ant-slider:hover .ant-slider-rail{background-color:#e1e1e1}.ant-slider:hover .ant-slider-track{background-color:#eb8857}.ant-slider:hover .ant-slider-handle:not(.ant-tooltip-open){border-color:#eb8857}.ant-slider-mark{position:absolute;top:14px;left:0;width:100%;font-size:14px}.ant-slider-mark-text{position:absolute;display:inline-block;color:#00000073;text-align:center;word-break:keep-all;cursor:pointer;user-select:none}.ant-slider-mark-text-active{color:#000000d9}.ant-slider-step{position:absolute;width:100%;height:4px;background:transparent}.ant-slider-dot{position:absolute;top:-2px;width:8px;height:8px;margin-left:-4px;background-color:#fff;border:2px solid #f0f0f0;border-radius:50%;cursor:pointer}.ant-slider-dot:first-child{margin-left:-4px}.ant-slider-dot:last-child{margin-left:-4px}.ant-slider-dot-active{border-color:#e89f85}.ant-slider-disabled{cursor:not-allowed}.ant-slider-disabled .ant-slider-rail{background-color:#f5f5f5!important}.ant-slider-disabled .ant-slider-track{background-color:#00000040!important}.ant-slider-disabled .ant-slider-handle,.ant-slider-disabled .ant-slider-dot{background-color:#fff;border-color:#00000040!important;box-shadow:none;cursor:not-allowed}.ant-slider-disabled .ant-slider-mark-text,.ant-slider-disabled .ant-slider-dot{cursor:not-allowed!important}.ant-slider-rtl{direction:rtl}.ant-slider-rtl .ant-slider-mark{right:0;left:auto}.ant-slider-rtl .ant-slider-dot,.ant-slider-rtl .ant-slider-dot:first-child{margin-right:-4px;margin-left:0}.ant-slider-rtl .ant-slider-dot:last-child{margin-right:-4px;margin-left:0}.num-input[data-v-55978858]{display:flex}.num-input .slide[data-v-55978858]{flex:1;min-width:128px;max-width:256px;margin-left:8px}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
.ant-breadcrumb{box-sizing:border-box;margin:0;padding:0;color:#000000d9;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:"tnum";color:#00000073;font-size:14px}.ant-breadcrumb .anticon{font-size:14px}.ant-breadcrumb a{color:#00000073;transition:color .3s}.ant-breadcrumb a:hover{color:#de632f}.ant-breadcrumb>span:last-child{color:#000000d9}.ant-breadcrumb>span:last-child a{color:#000000d9}.ant-breadcrumb>span:last-child .ant-breadcrumb-separator{display:none}.ant-breadcrumb-separator{margin:0 8px;color:#00000073}.ant-breadcrumb-link>.anticon+span,.ant-breadcrumb-link>.anticon+a{margin-left:4px}.ant-breadcrumb-overlay-link>.anticon{margin-left:4px}.ant-breadcrumb-rtl{direction:rtl}.ant-breadcrumb-rtl:before{display:table;content:""}.ant-breadcrumb-rtl:after{display:table;clear:both;content:""}.ant-breadcrumb-rtl>span{float:right}.ant-breadcrumb-rtl .ant-breadcrumb-link>.anticon+span,.ant-breadcrumb-rtl .ant-breadcrumb-link>.anticon+a{margin-right:4px;margin-left:0}.ant-breadcrumb-rtl .ant-breadcrumb-overlay-link>.anticon{margin-right:4px;margin-left:0}.nprogress{pointer-events:none}.nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}.nprogress .peg{display:block;position:absolute;right:0px;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translate(0px,-4px);-ms-transform:rotate(3deg) translate(0px,-4px);transform:rotate(3deg) translateY(-4px)}.nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}.nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:solid 2px transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent .nprogress .spinner,.nprogress-custom-parent .nprogress .bar{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.base-info[data-v-7f9c0b15]{position:absolute;padding:4px;font-size:.8em;background:var(--zp-primary-background);color:var(--zp-primary);left:0;bottom:0;border-top-right-radius:4px}.location-act[data-v-188f8e07]{margin-left:8px}.location-act .copy[data-v-188f8e07]{margin-right:4px}@media (max-width: 768px){.location-act[data-v-188f8e07]{display:flex;flex-direction:column}.location-act>*[data-v-188f8e07],.location-act .copy[data-v-188f8e07]{margin:2px}}.breadcrumb[data-v-188f8e07]{display:flex;align-items:center}.breadcrumb>*[data-v-188f8e07]{margin-right:4px}@media (max-width: 768px){.breadcrumb[data-v-188f8e07]{width:100%}.breadcrumb .ant-breadcrumb>*[data-v-188f8e07]{display:inline-block}}.container[data-v-188f8e07]{background:var(--zp-secondary-background);height:var(--pane-max-height)}.location-bar[data-v-188f8e07]{padding:4px 16px;background:var(--zp-primary-background);border-bottom:1px solid var(--zp-border);display:flex;align-items:center;justify-content:space-between}@media (max-width: 768px){.location-bar[data-v-188f8e07]{flex-direction:column}.location-bar[data-v-188f8e07] ::-webkit-scrollbar{height:2px;background-color:var(--zp-secondary-variant-background)}.location-bar .actions[data-v-188f8e07]{padding:4px 0;width:100%;overflow:auto;display:flex;align-items:center}.location-bar .actions>*[data-v-188f8e07]{flex-shrink:0}}.location-bar .actions[data-v-188f8e07]{display:flex;align-items:center;flex-shrink:0}.location-bar a.opt[data-v-188f8e07]{margin-left:8px}.view[data-v-188f8e07]{padding:8px;height:calc(100vh - 48px)}.view .file-list[data-v-188f8e07]{list-style:none;padding:8px;height:100%;overflow:auto}.hint[data-v-188f8e07]{padding:4px;border:4px;background:var(--zp-secondary-background);border:1px solid var(--zp-border)}
.ant-breadcrumb{box-sizing:border-box;margin:0;padding:0;color:#000000d9;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:"tnum";color:#00000073;font-size:14px}.ant-breadcrumb .anticon{font-size:14px}.ant-breadcrumb a{color:#00000073;transition:color .3s}.ant-breadcrumb a:hover{color:#de632f}.ant-breadcrumb>span:last-child{color:#000000d9}.ant-breadcrumb>span:last-child a{color:#000000d9}.ant-breadcrumb>span:last-child .ant-breadcrumb-separator{display:none}.ant-breadcrumb-separator{margin:0 8px;color:#00000073}.ant-breadcrumb-link>.anticon+span,.ant-breadcrumb-link>.anticon+a{margin-left:4px}.ant-breadcrumb-overlay-link>.anticon{margin-left:4px}.ant-breadcrumb-rtl{direction:rtl}.ant-breadcrumb-rtl:before{display:table;content:""}.ant-breadcrumb-rtl:after{display:table;clear:both;content:""}.ant-breadcrumb-rtl>span{float:right}.ant-breadcrumb-rtl .ant-breadcrumb-link>.anticon+span,.ant-breadcrumb-rtl .ant-breadcrumb-link>.anticon+a{margin-right:4px;margin-left:0}.ant-breadcrumb-rtl .ant-breadcrumb-overlay-link>.anticon{margin-right:4px;margin-left:0}.nprogress{pointer-events:none}.nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}.nprogress .peg{display:block;position:absolute;right:0px;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translate(0px,-4px);-ms-transform:rotate(3deg) translate(0px,-4px);transform:rotate(3deg) translateY(-4px)}.nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}.nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:solid 2px transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent .nprogress .spinner,.nprogress-custom-parent .nprogress .bar{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.base-info[data-v-afd25667]{position:absolute;padding:4px;font-size:.8em;background:var(--zp-primary-background);color:var(--zp-primary);left:0;bottom:0;border-top-right-radius:4px}.location-act[data-v-9516e28d]{margin-left:8px}.location-act .copy[data-v-9516e28d]{margin-right:4px}@media (max-width: 768px){.location-act[data-v-9516e28d]{display:flex;flex-direction:column}.location-act>*[data-v-9516e28d],.location-act .copy[data-v-9516e28d]{margin:2px}}.breadcrumb[data-v-9516e28d]{display:flex;align-items:center}.breadcrumb>*[data-v-9516e28d]{margin-right:4px}@media (max-width: 768px){.breadcrumb[data-v-9516e28d]{width:100%}.breadcrumb .ant-breadcrumb>*[data-v-9516e28d]{display:inline-block}}.container[data-v-9516e28d]{background:var(--zp-secondary-background);height:var(--pane-max-height)}.location-bar[data-v-9516e28d]{padding:4px 16px;background:var(--zp-primary-background);border-bottom:1px solid var(--zp-border);display:flex;align-items:center;justify-content:space-between}@media (max-width: 768px){.location-bar[data-v-9516e28d]{flex-direction:column}.location-bar[data-v-9516e28d] ::-webkit-scrollbar{height:2px;background-color:var(--zp-secondary-variant-background)}.location-bar .actions[data-v-9516e28d]{padding:4px 0;width:100%;overflow:auto;display:flex;align-items:center}.location-bar .actions>*[data-v-9516e28d]{flex-shrink:0}}.location-bar .actions[data-v-9516e28d]{display:flex;align-items:center;flex-shrink:0}.location-bar a.opt[data-v-9516e28d]{margin-left:8px}.view[data-v-9516e28d]{padding:8px;height:calc(100vh - 48px)}.view .file-list[data-v-9516e28d]{list-style:none;padding:8px;height:100%;overflow:auto}.hint[data-v-9516e28d]{padding:4px;border:4px;background:var(--zp-secondary-background);border:1px solid var(--zp-border)}

1
vue/dist/assets/stackView-8d68da7b.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
vue/dist/index.html vendored
View File

@ -7,8 +7,8 @@
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Infinite Image Browsing</title>
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-0cf80cdf.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-6d9e7c10.css">
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-5573ae38.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-dd273d5b.css">
</head>
<body>

View File

@ -22,7 +22,21 @@ import { useTagStore } from '@/store/useTagStore'
import { useLocalStorage } from '@vueuse/core'
const tagStore = useTagStore()
const showAutoUpdateFeatureTip = useLocalStorage('iib_auto_update_feature_tip_shown', false)
const props = defineProps<{ tabIdx: number; paneIdx: number, searchScope?: string }>()
const props = defineProps<{
tabIdx: number
paneIdx: number
searchScope?: string
/** Initial search keyword value */
initialSubstr?: string
/** Initial regex mode value */
initialIsRegex?: boolean
/** Initial path-only mode value */
initialPathOnly?: boolean
/** Initial media type filter value */
initialMediaType?: 'all' | 'image' | 'video'
/** Whether to auto-search on mount */
autoSearch?: boolean
}>()
const isRegex = ref(false)
const substr = ref('')
const pathOnly = ref(false)
@ -83,7 +97,24 @@ onMounted(async () => {
await onUpdateBtnClick()
}
}
if (props.searchScope) {
// Apply pre-filled values from props
if (props.initialSubstr !== undefined) {
substr.value = props.initialSubstr
}
if (props.initialIsRegex !== undefined) {
isRegex.value = props.initialIsRegex
}
if (props.initialPathOnly !== undefined) {
pathOnly.value = props.initialPathOnly
}
if (props.initialMediaType !== undefined) {
mediaType.value = props.initialMediaType
}
// Auto-search if substr is provided and autoSearch is not false
if (props.initialSubstr && props.autoSearch !== false) {
await query()
} else if (props.searchScope && !props.initialSubstr) {
// Legacy behavior: only search if searchScope but no search term
await query()
}
})

View File

@ -23,7 +23,6 @@ import 'multi-nprogress/nprogress.css'
// @ts-ignore
import { RecycleScroller } from '@zanllp/vue-virtual-scroller'
import '@zanllp/vue-virtual-scroller/dist/vue-virtual-scroller.css'
import { watch } from 'vue'
import FileItem from '@/components/FileItem.vue'
import fullScreenContextMenu from './fullScreenContextMenu.vue'
import BaseFileListInfo from '@/components/BaseFileListInfo.vue'
@ -35,7 +34,9 @@ import MultiSelectKeep from '@/components/MultiSelectKeep.vue'
import { openSmartOrganizeConfig } from '@/util/smartOrganize'
import { Modal, message } from 'ant-design-vue'
import { t } from '@/i18n'
import { h, ref } from 'vue'
import { h, ref, watch, onMounted, nextTick } from 'vue'
import { openImageFullscreenPreview } from '@/util/imagePreviewOperation'
import { normalize } from '@/util/path'
const global = useGlobalStore()
const props = defineProps<{
@ -46,6 +47,8 @@ const props = defineProps<{
*/
path?: string
mode?: Props['mode']
targetFile?: string
openPreview?: boolean
/**
* 页面栈,跳过不必要的api请求
*/
@ -81,7 +84,7 @@ const {
} = useFilesDisplay()
const { onDrop, onFileDragStart, onFileDragEnd, onFileDropToFolder } = useFileTransfer()
const { onFileItemClick, onContextMenuClick, showGenInfo, imageGenInfo, q } = useFileItemActions({ openNext })
const { previewIdx, onPreviewVisibleChange, previewing, previewImgMove, canPreview, scrollToFileId } = usePreview()
const { previewIdx, onPreviewVisibleChange, previewing, previewImgMove, canPreview, scrollToFileId,scrollToIndex } = usePreview()
const tiktokStore = useTiktokStore()
const { showMenuIdx } = useMobileOptimization()
const { onClearAllSelected, onReverseSelect, onSelectAll } = useKeepMultiSelect()
@ -201,6 +204,45 @@ watch(
}
)
// Handle view action: open target file in fullscreen preview
onMounted(() => {
const { targetFile, openPreview } = props
if (!targetFile || !openPreview) {
return
}
console.log('StackView mounted with targetFile:', targetFile, 'openPreview:', openPreview)
// Wait for files to load, then find and open the target file
nextTick(() => {
// Watch for sortedFiles to be populated
const unwatch = watch(
() => sortedFiles.value,
(files) => {
if (files && files.length > 0) {
const targetIdx = files.map(v => normalize(v.fullpath)).indexOf(normalize(targetFile))
if (targetIdx !== -1) {
unwatch()
nextTick(() => {
console.log('Found target file in stack view:', targetFile, 'at index', targetIdx)
scrollToIndex(targetIdx)
// Trigger preview by setting previewIdx and fullscreenPreviewInitialUrl
setTimeout(() => {
openImageFullscreenPreview(targetIdx,stackViewEl.value!)
}, 300)
})
}
}
},
{ immediate: true }
)
// Auto-cleanup after 5 seconds if file not found
setTimeout(() => {
unwatch()
}, 5000)
})
})
</script>
<template>

View File

@ -1,11 +1,145 @@
import type { FileTransferTabPane, useGlobalStore } from './store/useGlobalStore'
import type { FileTransferTabPane, TabPane, useGlobalStore, TagSearchMatchedImageGridTabPane, TopicSearchMatchedImageGridTabPane, GridViewTabPane, ImgSliTabPane, TagSearchTabPane, FuzzySearchTabPane } from './store/useGlobalStore'
import { Dict, removeQueryParams, switch2IIB } from './util'
import { uniqueId } from 'lodash-es'
import { getParentDirectory, basename, normalize } from './util/path'
const createPaneFromType = (type: TabPane['type'], props: any): TabPane | null => {
const base = {
key: uniqueId(),
name: props.name ?? ''
}
switch (type) {
case 'local': {
const pane: FileTransferTabPane = {
...base,
type,
path: props.path,
mode: props.mode,
stackKey: props.stackKey,
targetFile: props.targetFile,
openPreview: props.openPreview
}
return pane
}
case 'tag-search': {
const pane: TagSearchTabPane = {
...base,
type,
searchScope: props.searchScope
}
return pane
}
case 'fuzzy-search': {
const pane: FuzzySearchTabPane = {
...base,
type,
searchScope: props.searchScope,
initialSubstr: props.substr,
initialIsRegex: props.isRegex,
initialPathOnly: props.pathOnly,
initialMediaType: props.mediaType,
autoSearch: props.autoSearch
}
return pane
}
case 'tag-search-matched-image-grid': {
const pane: TagSearchMatchedImageGridTabPane = {
...base,
type,
selectedTagIds: props.selectedTagIds,
id: props.id ?? uniqueId()
}
return pane
}
case 'topic-search-matched-image-grid': {
const pane: TopicSearchMatchedImageGridTabPane = {
...base,
type,
id: props.id ?? uniqueId(),
title: props.title ?? '',
paths: props.paths ?? []
}
return pane
}
case 'grid-view': {
const pane: GridViewTabPane = {
...base,
type,
files: props.files ?? [],
removable: props.removable,
allowDragAndDrop: props.allowDragAndDrop
}
return pane
}
case 'img-sli': {
const pane: ImgSliTabPane = {
...base,
type,
left: props.left,
right: props.right
}
return pane
}
case 'random-image': {
const pane: TabPane = {
...base,
type
}
return pane
}
case 'topic-search':
case 'batch-download':
case 'workspace-snapshot':
case 'global-setting': {
const pane: TabPane = {
...base,
type
}
return pane
}
default:
return null
}
}
export const resolveQueryActions = async (g: ReturnType<typeof useGlobalStore>) => {
const paths = g.conf?.global_setting
const params = new URLSearchParams(parent.location.search)
switch (params.get('action')) {
const action = params.get('action')
switch (action) {
case 'view': {
// Quick view action: open image in fullscreen preview
// Usage: ?action=view&path=/path/to/image.png
let imagePath = params.get('path')
if (!imagePath) {
console.error('[IIB] view action requires path parameter')
return
}
imagePath = normalize(imagePath)
// Get parent folder and use scanned-fixed mode
const folderPath = getParentDirectory(imagePath)
const imageName = basename(imagePath)
const tab = g.tabList[0]
const pane: FileTransferTabPane = {
type: 'local',
path: folderPath,
key: uniqueId(),
name: imageName,
mode: 'scanned-fixed',
targetFile: imagePath,
openPreview: true
}
tab.panes.unshift(pane)
tab.key = pane.key
switch2IIB()
removeQueryParams(['action', 'path'])
break
}
case 'open': {
let path = params.get('path')
@ -34,5 +168,51 @@ export const resolveQueryActions = async (g: ReturnType<typeof useGlobalStore>)
removeQueryParams(['action', 'path', 'mode'])
break
}
case 'pane': {
const type = params.get('type') as TabPane['type']
const propsJson = params.get('props')
// Validate pane type
const validTypes: TabPane['type'][] = [
'local',
'tag-search',
'fuzzy-search',
'tag-search-matched-image-grid',
'topic-search-matched-image-grid',
'grid-view',
'img-sli',
'random-image',
'topic-search',
'batch-download',
'workspace-snapshot',
'global-setting',
'empty'
]
if (!type || !validTypes.includes(type)) {
console.error('[IIB] Invalid or missing pane type:', type)
return
}
let props: any = {}
try {
if (propsJson) {
props = JSON.parse(decodeURIComponent(propsJson))
}
} catch (e) {
console.error('[IIB] Failed to parse pane props:', e)
return
}
const pane = createPaneFromType(type, props)
if (pane) {
const tab = g.tabList[0]
tab.panes.unshift(pane)
tab.key = pane.key
switch2IIB()
}
removeQueryParams(['action', 'type', 'props'])
break
}
}
}

View File

@ -48,7 +48,7 @@ export interface GridViewFile extends FileNodeInfo {
/**
* A tab pane that displays files in a grid view.
*/
interface GridViewTabPane extends TabPaneBase {
export interface GridViewTabPane extends TabPaneBase {
type: 'grid-view'
/**
* Indicates whether the files in the grid view can be deleted.
@ -73,7 +73,7 @@ export interface GridViewFile extends FileNodeInfo {
/**
* A tab pane that displays files in a grid view.
*/
interface GridViewTabPane extends TabPaneBase {
export interface GridViewTabPane extends TabPaneBase {
type: 'grid-view'
/**
* Indicates whether the files in the grid view can be deleted.
@ -86,13 +86,13 @@ interface GridViewTabPane extends TabPaneBase {
files: GridViewFile[]
}
interface TagSearchMatchedImageGridTabPane extends TabPaneBase {
export interface TagSearchMatchedImageGridTabPane extends TabPaneBase {
type: 'tag-search-matched-image-grid'
selectedTagIds: MatchImageByTagsReq
id: string
}
interface TopicSearchMatchedImageGridTabPane extends TabPaneBase {
export interface TopicSearchMatchedImageGridTabPane extends TabPaneBase {
type: 'topic-search-matched-image-grid'
id: string
title: string
@ -109,6 +109,10 @@ export interface FileTransferTabPane extends TabPaneBase {
path?: string
mode?: FileTransferProps['mode']
stackKey?: string
/** Target file path to scroll to and optionally preview */
targetFile?: string
/** Whether to open fullscreen preview for targetFile */
openPreview?: boolean
}
export interface TagSearchTabPane extends TabPaneBase {
@ -119,6 +123,16 @@ export interface TagSearchTabPane extends TabPaneBase {
export interface FuzzySearchTabPane extends TabPaneBase {
type: 'fuzzy-search'
searchScope?: string
/** Initial search keyword value (used as-is if isRegex is true, otherwise as keyword) */
initialSubstr?: string
/** Initial regex mode value */
initialIsRegex?: boolean
/** Initial path-only mode value */
initialPathOnly?: boolean
/** Initial media type filter value */
initialMediaType?: 'all' | 'image' | 'video'
/** Whether to auto-search on mount */
autoSearch?: boolean
}
export type TabPane =

View File

@ -1,8 +1,10 @@
import { memoize } from 'lodash-es'
export function isAbsolute (path: string): boolean {
return /^(?:\/|[a-z]:)/i.test(normalize(path))
}
export function normalize (path: string): string {
function _normalize (path: string): string {
if (!path) {
return ''
}
@ -34,6 +36,7 @@ export function normalize (path: string): string {
return result
}
}
export const normalize = memoize(_normalize)
export function join (...paths: string[]): string {
if (!paths.length) {
@ -78,3 +81,13 @@ export function getParentDirectory(filePath: string) {
}
return filePath.substring(0, lastSeparatorIndex);
}
export function basename(filePath: string) {
const lastSlashIndex = filePath.lastIndexOf('/');
const lastBackslashIndex = filePath.lastIndexOf('\\');
const lastSeparatorIndex = Math.max(lastSlashIndex, lastBackslashIndex);
if (lastSeparatorIndex === -1) {
return filePath;
}
return filePath.substring(lastSeparatorIndex + 1);
}