feat: 优化query搜索并加入tag图(去掉max抽象层与d3依赖)

feature/tag-relationship-graph
wuqinchuan 2026-01-11 00:53:33 +08:00
parent 67cbda36d1
commit 59bef3915e
77 changed files with 884 additions and 940 deletions

View File

@ -1,252 +0,0 @@
# Tag Relationship Graph 功能说明
## 功能概述
Tag 关系图是基于聚类结果的 keywords 构建的交互式可视化系统,帮助用户更直观地探索和索引图像数据集。
## 核心特性
### 1. 节点类型
- **Tag 节点**(圆形)
- 大小:正比于权重(出现频率 × 图片数量)
- 颜色:根据类别区分
- 蓝色:人物 (character)
- 绿色:风格 (style)
- 黄色:场景 (scene)
- 红色:物体 (object)
- 紫色:其他 (other)
- 社区着色:启用社区检测后,同社区的节点使用相同颜色
- **Cluster 节点**圆形cyan 色)
- 大小:正比于簇内图片数量
- 标签:簇的标题
### 2. 边(连线)
- **Tag-Tag 连线**
- 粗细:正比于共现权重(两个 tag 同时出现在簇中的图片数)
- 透明度0.4(降低视觉干扰)
- **Cluster-Tag 连线**
- 连接簇节点与其包含的 tag
- 便于查看簇的组成
### 3. 权重计算
使用混合权重模式alpha=0.3
```python
weight = 0.3 × 频次权重 + 0.7 × TF-IDF 权重
频次权重 = Σ(所在聚类的图片数)
TF-IDF 权重 = 频次权重 × log(总聚类数 / 包含该tag的聚类数)
```
**效果**
- 突出特征性 tag如 "Warhammer 40K"、"赛博朋克"
- 压制通用 tag如 "女孩"、"风景"
### 4. 社区检测
使用 Louvain 算法自动发现 tag 社区:
- 相关的 tag 自动聚成一组
- 每个社区用不同颜色区分
- 示例:
- 社区1赛博朋克、机甲、霓虹、未来
- 社区2森林、精灵、魔法、奇幻
- 社区3女孩、制服、校园、青春
## 使用方法
### 1. 切换到 Tag Graph 视图
1. 在 Topic Search 页面
2. 点击右上角的 Switch 切换按钮
3. 选择 "Tag Graph"
### 2. 参数调整
- **Top Tags**: 显示前 N 个权重最高的 tag默认 50
- **Top Clusters**: 显示前 N 个簇节点(默认 20
- **Show Clusters**: 是否显示簇节点(勾选/取消)
### 3. 交互操作
#### 基本交互
- **拖拽**:拖动节点调整布局
- **缩放**:鼠标滚轮缩放视图
- **平移**:按住 Shift + 拖拽平移视图
#### 节点操作
- **悬停**:显示详细信息(类别、图片数、聚类数、社区)
- **点击**:在右侧面板显示详细信息
- **搜索**:点击 "Search Images" 按钮,自动搜索该 tag 的图片
#### 工具栏
- **Refresh**: 重新加载图数据
- **Fit View**: 自动调整视图以显示所有节点
### 4. 典型使用场景
#### 场景1探索式浏览
```
问题:不知道数据集里有什么主题
操作:
1. 打开 Tag Graph
2. 观察大节点(核心主题)
3. 点击感兴趣的 tag
4. 点击 "Search Images" 查看图片
```
#### 场景2精准索引
```
需求:找所有包含"机甲"和"战斗"的图片
操作:
1. 在图中找到"机甲"节点
2. 观察相连的节点
3. 如果有"战斗"节点且相连,说明有共现
4. 分别搜索两个 tag 的交集
```
#### 场景3主题发现
```
发现:原来数据集有强烈的 Warhammer 40K 主题
可视化效果:
- "Warhammer 40K" 节点很大(高 TF-IDF
- 周围连接 "星际战士"、"帝国"、"混沌"
- 形成明显的社区簇(同色区域)
```
## 技术实现
### 后端 API
```python
POST /infinite_image_browsing/db/cluster_tag_graph
Request:
{
"folder_paths": [...],
"top_n_tags": 50,
"top_n_clusters": 20,
"show_clusters": true,
"detect_communities": true,
"weight_mode": "hybrid",
"alpha": 0.3
}
Response:
{
"nodes": [
{
"id": "tag_机甲",
"label": "机甲",
"weight": 629.5,
"image_count": 224,
"cluster_count": 3,
"category": "object",
"community": 0
},
...
],
"links": [
{
"source": "tag_机甲",
"target": "tag_科幻",
"weight": 192,
"image_count": 192,
"cluster_count": 2
},
...
],
"communities": [
{
"id": 0,
"nodes": ["tag_机甲", "tag_科幻", ...],
"size": 5
},
...
]
}
```
### 前端组件
- 基于 **ECharts Graph** 实现
- 力导向布局Force-Directed Layout
- 支持 roam缩放/平移/拖拽)
### 依赖
**后端**(可选):
```bash
pip install networkx
```
如果没有 networkx社区检测功能会被禁用但其他功能正常工作。
**前端**
- echarts已包含在项目中
## 性能优化
- **Top-N 截断**:只显示权重最高的节点,避免视觉混乱
- **边权重过滤**:低权重的边不显示
- **标签隐藏**只有大节点显示标签labelLayout.hideOverlap
- **力学参数**:调整斥力/引力平衡,避免节点重叠
## 后续优化方向
1. **增量更新**:新增图片时只更新受影响的节点
2. **多层级钻取**:点击 tag 展开为更细粒度的子 tag
3. **热力图叠加**:显示 tag 的热度分布
4. **时序动画**:展示 tag 关系随时间的演变
5. **导出功能**:导出为 PNG/SVG/JSON
## 常见问题
### Q1: 为什么有些常见词(如"女孩")节点不大?
A: 因为使用了 TF-IDF 权重,压制了过于常见的 tag。如果想突出频次可以修改 alpha 参数(增大到 0.8-1.0)。
### Q2: 为什么有些 tag 之间没有连线?
A: 只有在同一个簇中同时出现的 tag 才会有连线。如果两个 tag 从未共现,则不会连接。
### Q3: 社区检测有什么用?
A: 自动发现主题群组,用颜色区分。例如"赛博朋克相关"、"奇幻相关"、"校园相关"会自动分组。
### Q4: 如何调整图的疏密度?
A: 修改 ECharts 的 force 参数:
- `repulsion`: 增大 = 节点更分散
- `edgeLength`: 增大 = 边更长
- `gravity`: 增大 = 更聚拢到中心
### Q5: 为什么点击 Search Images 没反应?
A: 需要先运行一次聚类Refresh 按钮),确保有聚类结果。
## 贡献
欢迎提交 PR 改进此功能!
重点优化方向:
- 更好的布局算法
- 更丰富的交互方式
- 更精准的权重计算
- 性能优化(处理 100+ tag
---
**开发者**: Claude Code
**版本**: 1.0.0
**更新日期**: 2026-01-09

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-632e7cf6.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-d385cc4f.css">
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-d6594e8e.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-af514ea9.css">
</head>
<body>

View File

@ -1264,6 +1264,8 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
verify_secret=verify_secret,
embedding_model=EMBEDDING_MODEL,
ai_model=AI_MODEL,
openai_base_url=OPENAI_BASE_URL,
openai_api_key=OPENAI_API_KEY,
)

View File

@ -66,6 +66,9 @@ class DataBase:
# 创建连接并打开数据库
conn = connect(clz.get_db_file_path())
# # 禁用 WAL 模式,使用传统的 DELETE 日志模式
# conn.execute("PRAGMA journal_mode=DELETE")
def regexp(expr, item):
if not isinstance(item, str):
return False

View File

@ -1,64 +1,50 @@
"""
Tag relationship graph generation for topic clusters.
Builds a weighted graph showing connections between tags from cluster keywords.
Hierarchical tag graph generation for topic clusters.
Builds a multi-layer neural-network-style visualization with LLM-driven abstraction.
"""
import hashlib
import json
import math
from typing import Dict, List, Optional, Set, Tuple
from typing import Dict, List, Optional
from pydantic import BaseModel
from fastapi import Depends, FastAPI, HTTPException
from scripts.iib.db.datamodel import DataBase, TopicClusterCache
from scripts.iib.db.datamodel import DataBase, GlobalSetting
from scripts.iib.tool import normalize_output_lang
# Cache version for tag abstraction - increment to invalidate all caches
TAG_ABSTRACTION_CACHE_VERSION = 2
class TagGraphReq(BaseModel):
folder_paths: List[str]
model: Optional[str] = None
threshold: Optional[float] = 0.90
min_cluster_size: Optional[int] = 2
lang: Optional[str] = None
# Graph parameters
top_n_tags: Optional[int] = 50
top_n_clusters: Optional[int] = 20
weight_mode: Optional[str] = "hybrid" # frequency / tfidf / hybrid
alpha: Optional[float] = 0.3 # for hybrid mode
show_clusters: Optional[bool] = True
detect_communities: Optional[bool] = True
lang: Optional[str] = "en" # Language for LLM output
class TagNode(BaseModel):
class LayerNode(BaseModel):
id: str
label: str
weight: float
image_count: int
cluster_count: int
category: str
community: Optional[int] = None
size: float # Weight/importance of this node
metadata: Optional[dict] = None
class ClusterNode(BaseModel):
id: str
label: str
weight: float
size: int
category: str = "cluster"
community: Optional[int] = None
class GraphLayer(BaseModel):
level: int
name: str # Layer name: "Clusters", "Tags", "Abstract-1", "Abstract-2"
nodes: List[LayerNode]
class GraphLink(BaseModel):
source: str
target: str
weight: float
image_count: Optional[int] = None
cluster_count: Optional[int] = None
class TagGraphResp(BaseModel):
nodes: List[dict] # TagNode or ClusterNode
layers: List[GraphLayer]
links: List[GraphLink]
communities: Optional[List[dict]] = None
stats: dict
@ -68,115 +54,180 @@ def mount_tag_graph_routes(
verify_secret,
embedding_model: str,
ai_model: str,
openai_base_url: str,
openai_api_key: str,
):
"""Mount tag relationship graph endpoints"""
"""Mount hierarchical tag graph endpoints"""
def _get_cluster_cache_key(req: TagGraphReq, model: str) -> str:
"""Compute cache key for cluster result lookup"""
from scripts.iib.topic_cluster import _PROMPT_NORMALIZE_VERSION, _PROMPT_NORMALIZE_MODE
async def _call_llm_for_abstraction(
tags: List[str],
lang: str,
model: str,
base_url: str,
api_key: str
) -> dict:
"""
Call LLM to create hierarchical abstraction of tags.
Returns a dict with layers and groupings.
"""
import asyncio
import requests
import re
cache_params = {
"model": model,
"threshold": float(req.threshold or 0.90),
"min_cluster_size": int(req.min_cluster_size or 2),
"assign_noise_threshold": None,
"title_model": None,
"lang": str(req.lang or ""),
"nv": _PROMPT_NORMALIZE_VERSION,
"nm": _PROMPT_NORMALIZE_MODE,
}
h = hashlib.sha1()
h.update(
json.dumps(
{"folders": sorted(req.folder_paths), "params": cache_params},
ensure_ascii=False,
sort_keys=True,
).encode("utf-8")
)
return h.hexdigest()
def _normalize_base_url(url: str) -> str:
url = url.strip()
if not url.startswith(("http://", "https://")):
url = f"https://{url}"
return url.rstrip("/")
def _infer_tag_category(tag: str) -> str:
"""Infer tag category from content"""
tag_lower = tag.lower()
def _call_sync():
if not api_key:
raise HTTPException(500, "OpenAI API Key not configured")
# Character
char_keywords = [
"girl", "boy", "woman", "man", "character", "person",
"女孩", "男孩", "女性", "男性", "人物", "角色", "战士", "精灵"
]
if any(k in tag_lower for k in char_keywords):
return "character"
url = f"{_normalize_base_url(base_url)}/chat/completions"
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
# Style
style_keywords = [
"style", "punk", "sci-fi", "fantasy", "realistic", "anime",
"风格", "朋克", "科幻", "奇幻", "写实", "动漫", "赛博"
]
if any(k in tag_lower for k in style_keywords):
return "style"
# Normalize language for consistent LLM output
normalized_lang = normalize_output_lang(lang)
sys_prompt = f"""You are a tag categorization assistant. Organize tags into hierarchical categories.
# Scene
scene_keywords = [
"forest", "city", "mountain", "ocean", "indoor", "outdoor", "landscape",
"森林", "城市", "", "", "室内", "室外", "风景", "场景"
]
if any(k in tag_lower for k in scene_keywords):
return "scene"
STRICT RULES:
1. Create 5-15 Level 1 categories (broad groupings)
2. Optionally create 2-5 Level 2 super-categories IF Level 1 has 8+ categories
3. Every tag must belong to exactly ONE Level 1 category
4. Use {normalized_lang} for all category labels
5. Category IDs must be simple lowercase (e.g., "style", "char", "scene1")
# Object
object_keywords = [
"sword", "weapon", "machine", "building", "vehicle",
"机甲", "武器", "建筑", "", ""
]
if any(k in tag_lower for k in object_keywords):
return "object"
OUTPUT ONLY VALID JSON - NO markdown, NO explanations, NO extra text:
{{"layers":[{{"level":1,"groups":[{{"id":"cat1","label":"Label1","tags":["tag1"]}},{{"id":"cat2","label":"Label2","tags":["tag2"]}}]}},{{"level":2,"groups":[{{"id":"super1","label":"SuperLabel","categories":["cat1","cat2"]}}]}}]}}
return "other"
If unsure about Level 2, OMIT it entirely. Start response with {{ and end with }}"""
def _detect_communities(nodes: List[dict], links: List[GraphLink]) -> Optional[List[dict]]:
"""Detect communities using Louvain algorithm"""
try:
import networkx as nx
from networkx.algorithms import community
except ImportError:
return None
user_prompt = f"Tags to categorize:\n{', '.join(tags)}"
# Build graph
G = nx.Graph()
for node in nodes:
G.add_node(node["id"], weight=node["weight"])
payload = {
"model": model,
"messages": [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt}
],
"temperature": 0.0,
"max_tokens": 2048,
}
for link in links:
if G.has_node(link.source) and G.has_node(link.target):
G.add_edge(link.source, link.target, weight=link.weight)
# Retry up to 5 times
last_error = ""
for attempt in range(1, 6):
try:
resp = requests.post(url, json=payload, headers=headers, timeout=60)
except requests.RequestException as e:
last_error = f"network_error: {type(e).__name__}: {e}"
continue
if len(G.nodes()) < 2:
return None
# Retry on 429 or 5xx, fail immediately on other 4xx
if resp.status_code != 200:
body = (resp.text or "")[:400]
if resp.status_code == 429 or resp.status_code >= 500:
last_error = f"api_error_retriable: status={resp.status_code}"
continue
# 4xx client error - fail immediately
raise Exception(f"API client error: {resp.status_code} {body}")
# Detect communities
try:
communities = community.louvain_communities(G, weight="weight", resolution=1.0)
except Exception:
return None
try:
data = resp.json()
except Exception as e:
last_error = f"response_not_json: {type(e).__name__}"
continue
# Format result
result = []
node_to_community = {}
for idx, comm in enumerate(communities):
comm_list = list(comm)
result.append({
"id": idx,
"nodes": comm_list,
"size": len(comm_list),
})
for node_id in comm_list:
node_to_community[node_id] = idx
choice0 = (data.get("choices") or [{}])[0]
msg = (choice0 or {}).get("message") or {}
content = (msg.get("content") or "").strip()
# Assign community to nodes
for node in nodes:
node["community"] = node_to_community.get(node["id"], -1)
# Extract JSON from content - try multiple strategies
json_str = None
return result
# Strategy 1: Direct parse (if response is pure JSON)
try:
result = json.loads(content)
if isinstance(result, dict) and "layers" in result:
return result
except:
pass
# Strategy 2: Extract JSON from markdown code blocks
code_block = re.search(r"```(?:json)?\s*(\{[\s\S]*?\})\s*```", content)
if code_block:
json_str = code_block.group(1)
else:
# Strategy 3: Find largest JSON object
m = re.search(r"\{[\s\S]*\}", content)
if m:
json_str = m.group(0)
if not json_str:
last_error = f"no_json_found: {content[:200]}"
continue
# Clean up common JSON issues
json_str = json_str.strip()
# Remove trailing commas before closing braces/brackets
json_str = re.sub(r',(\s*[}\]])', r'\1', json_str)
try:
result = json.loads(json_str)
except json.JSONDecodeError as e:
last_error = f"json_parse_error: {e}"
continue
# Validate structure
if not isinstance(result, dict) or "layers" not in result:
last_error = f"invalid_structure: {str(result)[:200]}"
continue
# Success!
return result
# All retries exhausted
# Fallback: create simple heuristic grouping
# Group by first character/prefix for basic organization
groups_dict = {}
for tag in tags:
# Simple heuristic: group by first 2 chars or common keywords
prefix = tag[:2] if len(tag) >= 2 else tag
if prefix not in groups_dict:
groups_dict[prefix] = []
groups_dict[prefix].append(tag)
# Merge small groups
merged_groups = []
for prefix, tag_list in sorted(groups_dict.items()):
if len(merged_groups) > 0 and len(tag_list) < 3:
# Merge into last group if this group is too small
merged_groups[-1]["tags"].extend(tag_list)
else:
group_id = f"group{len(merged_groups) + 1}"
label = f"Group {len(merged_groups) + 1}"
merged_groups.append({
"id": group_id,
"label": label,
"tags": tag_list
})
# Limit to max 12 groups
if len(merged_groups) > 12:
# Merge smallest groups
merged_groups = sorted(merged_groups, key=lambda g: len(g["tags"]), reverse=True)[:12]
return {
"layers": [
{
"level": 1,
"groups": merged_groups
}
]
}
return await asyncio.to_thread(_call_sync)
@app.post(
f"{db_api_base}/cluster_tag_graph",
@ -184,36 +235,98 @@ def mount_tag_graph_routes(
)
async def cluster_tag_graph(req: TagGraphReq):
"""
Build tag relationship graph from clustering results.
Returns nodes (tags + clusters) and weighted edges.
Build hierarchical tag graph from clustering results.
Returns multi-layer structure similar to neural network visualization.
Layer structure (bottom to top):
- Layer 0: Cluster nodes
- Layer 1: Tag nodes (deduplicated cluster keywords)
- Layer 2+: Abstract groupings (LLM-generated, max 2 layers)
"""
# Validate
if not req.folder_paths:
raise HTTPException(400, "folder_paths is required")
folders = sorted(req.folder_paths)
model = req.model or embedding_model
# Get cached cluster result
# Get the latest cluster result for these folders
conn = DataBase.get_conn()
cache_key = _get_cluster_cache_key(req, model)
cached = TopicClusterCache.get(conn, cache_key)
if not cached or not isinstance(cached.get("result"), dict):
from contextlib import closing
with closing(conn.cursor()) as cur:
cur.execute(
"""SELECT cache_key, folders, result FROM topic_cluster_cache
ORDER BY updated_at DESC"""
)
rows = cur.fetchall()
# Find a cache that matches the folders (order-independent)
folders_set = set(folders)
row = None
for cache_row in rows:
try:
cached_folders = json.loads(cache_row[1]) if isinstance(cache_row[1], str) else cache_row[1]
if isinstance(cached_folders, list) and set(cached_folders) == folders_set:
row = (cache_row[0], cache_row[2])
break
except Exception:
continue
if not row:
raise HTTPException(
400,
"No clustering result found. Please run clustering first."
f"No clustering result found for these {len(folders)} folders. Please run clustering first."
)
result = cached["result"]
cached_result = json.loads(row[1]) if isinstance(row[1], str) else row[1]
if not cached_result or not isinstance(cached_result, dict):
raise HTTPException(400, "Invalid clustering result format.")
result = cached_result
clusters = result.get("clusters", [])
if not clusters:
raise HTTPException(400, "No clusters found in result")
# 1. Collect tag statistics
# === Layer 0: Cluster Nodes ===
top_n_clusters = req.top_n_clusters or 20
top_clusters = sorted(clusters, key=lambda c: c.get("size", 0), reverse=True)[:top_n_clusters]
cluster_nodes = []
cluster_to_tags_links = []
for cluster in top_clusters:
cluster_id = cluster.get("id", "")
cluster_title = cluster.get("title", "Untitled")
cluster_size = cluster.get("size", 0)
keywords = cluster.get("keywords", [])
paths = cluster.get("paths", [])
node_id = f"cluster_{cluster_id}"
cluster_nodes.append(LayerNode(
id=node_id,
label=cluster_title,
size=float(cluster_size),
metadata={
"type": "cluster",
"image_count": cluster_size,
"paths": paths
}
))
# Store links from clusters to their tags (will be created later)
for keyword in keywords:
if keyword:
cluster_to_tags_links.append({
"cluster_id": node_id,
"tag": str(keyword).strip(),
"weight": float(cluster_size)
})
# === Layer 1: Tag Nodes (deduplicated) ===
tag_stats: Dict[str, Dict] = {}
total_images = sum(c.get("size", 0) for c in clusters)
for cluster in clusters:
keywords = cluster.get("keywords", [])
@ -227,141 +340,190 @@ def mount_tag_graph_routes(
if keyword not in tag_stats:
tag_stats[keyword] = {
"clusters": [],
"cluster_ids": [],
"total_images": 0,
}
tag_stats[keyword]["clusters"].append(cluster_id)
tag_stats[keyword]["cluster_ids"].append(cluster_id)
tag_stats[keyword]["total_images"] += cluster_size
# 2. Calculate tag weights
tag_nodes = []
alpha = float(req.alpha or 0.3)
for tag, stats in tag_stats.items():
cluster_count = len(stats["clusters"])
image_count = stats["total_images"]
# Frequency weight
freq_weight = float(image_count)
# TF-IDF weight
idf = math.log(len(clusters) / max(cluster_count, 1))
tfidf_weight = freq_weight * idf
# Hybrid weight
if req.weight_mode == "frequency":
weight = freq_weight
elif req.weight_mode == "tfidf":
weight = tfidf_weight
else: # hybrid
weight = alpha * freq_weight + (1 - alpha) * tfidf_weight
tag_nodes.append({
"id": f"tag_{tag}",
"label": tag,
"weight": weight,
"image_count": image_count,
"cluster_count": cluster_count,
"category": _infer_tag_category(tag),
})
# 3. Filter top-N tags
# Filter and sort tags
top_n_tags = req.top_n_tags or 50
tag_nodes = sorted(tag_nodes, key=lambda x: x["weight"], reverse=True)[:top_n_tags]
selected_tags = {node["label"] for node in tag_nodes}
sorted_tags = sorted(
tag_stats.items(),
key=lambda x: x[1]["total_images"],
reverse=True
)[:top_n_tags]
# 4. Calculate tag-tag co-occurrence
tag_pairs: Dict[Tuple[str, str], Dict] = {}
tag_nodes = []
selected_tags = set()
for cluster in clusters:
keywords = cluster.get("keywords", [])
keywords = [k for k in keywords if k in selected_tags]
cluster_size = cluster.get("size", 0)
cluster_id = cluster.get("id", "")
for tag, stats in sorted_tags:
tag_id = f"tag_{tag}"
selected_tags.add(tag)
tag_nodes.append(LayerNode(
id=tag_id,
label=tag,
size=float(stats["total_images"]),
metadata={
"type": "tag",
"cluster_count": len(stats["cluster_ids"]),
"image_count": stats["total_images"]
}
))
for i in range(len(keywords)):
for j in range(i + 1, len(keywords)):
tag1, tag2 = sorted([keywords[i], keywords[j]])
key = (tag1, tag2)
# Filter cluster->tag links to only include selected tags
layer0_to_1_links = []
for link in cluster_to_tags_links:
if link["tag"] in selected_tags:
layer0_to_1_links.append(GraphLink(
source=link["cluster_id"],
target=f"tag_{link['tag']}",
weight=link["weight"]
))
if key not in tag_pairs:
tag_pairs[key] = {
"clusters": [],
"images": 0,
}
# === Layer 2+: LLM-driven abstraction ===
abstract_layers = []
layer1_to_2_links = []
tag_pairs[key]["clusters"].append(cluster_id)
tag_pairs[key]["images"] += cluster_size
if len(selected_tags) > 3: # Only abstract if we have enough tags
# Use language from request
lang = req.lang or "en"
# 5. Build tag-tag links
tag_links = []
for (tag1, tag2), stats in tag_pairs.items():
tag_links.append(
GraphLink(
source=f"tag_{tag1}",
target=f"tag_{tag2}",
weight=float(stats["images"]),
image_count=stats["images"],
cluster_count=len(stats["clusters"]),
# Generate cache key for this set of tags (with version)
import hashlib
tags_sorted = sorted(selected_tags)
cache_input = f"v{TAG_ABSTRACTION_CACHE_VERSION}|{ai_model}|{lang}|{','.join(tags_sorted)}"
cache_key_hash = hashlib.md5(cache_input.encode()).hexdigest()
cache_key = f"tag_abstraction_v{TAG_ABSTRACTION_CACHE_VERSION}_{cache_key_hash}"
# Try to get from cache
abstraction = None
try:
cached_data = GlobalSetting.get_setting(conn, cache_key)
if cached_data and isinstance(cached_data, dict):
abstraction = cached_data
except:
pass
# Call LLM if not cached
if not abstraction:
abstraction = await _call_llm_for_abstraction(
list(selected_tags),
lang,
ai_model,
openai_base_url,
openai_api_key
)
)
# 6. Add cluster nodes (if requested)
cluster_nodes = []
cluster_links = []
# Save to cache if successful
if abstraction and isinstance(abstraction, dict) and abstraction.get("layers"):
try:
GlobalSetting.save_setting(conn, cache_key, json.dumps(abstraction, ensure_ascii=False))
except Exception:
pass
if req.show_clusters:
# Filter top-N clusters by size
top_n_clusters = req.top_n_clusters or 20
top_clusters = sorted(clusters, key=lambda c: c.get("size", 0), reverse=True)[:top_n_clusters]
# Build abstract layers from LLM response
llm_layers = abstraction.get("layers", [])
for cluster in top_clusters:
cluster_id = cluster.get("id", "")
cluster_title = cluster.get("title", "Untitled")
cluster_size = cluster.get("size", 0)
keywords = cluster.get("keywords", [])
for layer_info in llm_layers:
level = layer_info.get("level", 1)
groups = layer_info.get("groups", [])
cluster_nodes.append({
"id": f"cluster_{cluster_id}",
"label": cluster_title,
"weight": float(cluster_size),
"size": cluster_size,
"category": "cluster",
})
if not groups:
continue
# Link cluster to its tags
for keyword in keywords:
if keyword in selected_tags:
cluster_links.append(
GraphLink(
source=f"cluster_{cluster_id}",
target=f"tag_{keyword}",
weight=float(cluster_size),
)
abstract_nodes = []
# Process each group in this layer
for group in groups:
group_id = f"abstract_l{level}_{group.get('id', 'unknown')}"
group_label = group.get("label", "Unnamed")
# Calculate size based on contained tags/categories
if "tags" in group:
# Level 1: references tags directly
contained_tags = group.get("tags", [])
total_size = sum(
tag_stats.get(tag, {}).get("total_images", 0)
for tag in contained_tags
if tag in selected_tags
)
# 7. Combine all nodes and links
all_nodes = tag_nodes + cluster_nodes
all_links = [link.dict() for link in (tag_links + cluster_links)]
abstract_nodes.append(LayerNode(
id=group_id,
label=group_label,
size=float(total_size),
metadata={"type": "abstract", "level": level}
))
# 8. Community detection
communities = None
if req.detect_communities:
communities = _detect_communities(all_nodes, tag_links + cluster_links)
# Create links from tags to this abstract node
for tag in contained_tags:
if tag in selected_tags:
tag_id = f"tag_{tag}"
layer1_to_2_links.append(GraphLink(
source=tag_id,
target=group_id,
weight=float(tag_stats.get(tag, {}).get("total_images", 1))
))
elif "categories" in group and level == 2:
# Level 2: references Level 1 categories
# Calculate size from contained categories
contained_cats = group.get("categories", [])
total_size = 0.0
# Find size from level 1 nodes
level1_nodes = abstract_layers[0].nodes if abstract_layers else []
for cat_id in contained_cats:
full_cat_id = f"abstract_l1_{cat_id}"
for node in level1_nodes:
if node.id == full_cat_id:
total_size += node.size
break
abstract_nodes.append(LayerNode(
id=group_id,
label=group_label,
size=float(total_size),
metadata={"type": "abstract", "level": level}
))
# Create links from Level 1 categories to this Level 2 node
for cat_id in contained_cats:
full_cat_id = f"abstract_l1_{cat_id}"
# Verify the category exists
if any(n.id == full_cat_id for n in level1_nodes):
layer1_to_2_links.append(GraphLink(
source=full_cat_id,
target=group_id,
weight=total_size # Use aggregated weight
))
if abstract_nodes:
abstract_layers.append(GraphLayer(
level=level + 1, # +1 because Layer 0=clusters, Layer 1=tags
name=f"Abstract-{level}",
nodes=abstract_nodes
))
# === Build final response ===
layers = [
GraphLayer(level=0, name="Clusters", nodes=cluster_nodes),
GraphLayer(level=1, name="Tags", nodes=tag_nodes),
] + abstract_layers
all_links = [link.dict() for link in (layer0_to_1_links + layer1_to_2_links)]
# 9. Build response
return TagGraphResp(
nodes=all_nodes,
layers=layers,
links=all_links,
communities=communities,
stats={
"total_tags": len(tag_stats),
"selected_tags": len(tag_nodes),
"total_clusters": len(clusters),
"selected_clusters": len(cluster_nodes),
"total_images": total_images,
"tag_links": len(tag_links),
"cluster_links": len(cluster_links),
},
"total_tags": len(tag_stats),
"selected_tags": len(tag_nodes),
"abstraction_layers": len(abstract_layers),
"total_links": len(all_links),
}
)

View File

@ -5,7 +5,7 @@ import platform
import re
import tempfile
import subprocess
from typing import Dict, List
from typing import Dict, List, Optional
import sys
import piexif
import piexif.helper
@ -36,6 +36,37 @@ cwd = os.getcwd() if is_exe_ver else os.path.normpath(os.path.join(__file__, "..
is_win = platform.system().lower().find("windows") != -1
def normalize_output_lang(lang: Optional[str]) -> str:
"""
Map frontend language keys to a human-readable instruction for LLM output language.
Frontend uses: en / zhHans / zhHant / de
Args:
lang: Language code from frontend (e.g., "zhHans", "en", "de")
Returns:
Human-readable language name for LLM instruction
"""
if not lang:
return "English"
l = str(lang).strip()
ll = l.lower()
# Simplified Chinese
if ll in ["zh", "zhhans", "zh-hans", "zh_cn", "zh-cn", "cn", "zh-hans-cn", "zhs"]:
return "Chinese (Simplified)"
# Traditional Chinese (Taiwan, Hong Kong, Macau)
if ll in ["zhhant", "zh-hant", "zh_tw", "zh-tw", "zh_hk", "zh-hk", "zh_mo", "zh-mo", "tw", "hk", "mo", "macau", "macao", "zht"]:
return "Chinese (Traditional)"
# German
if ll.startswith("de"):
return "German"
# English
if ll.startswith("en"):
return "English"
# fallback
return "English"
try:

1
vue/components.d.ts vendored
View File

@ -39,6 +39,7 @@ declare module '@vue/runtime-core' {
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
ASkeleton: typeof import('ant-design-vue/es')['Skeleton']
ASlider: typeof import('ant-design-vue/es')['Slider']
ASpace: typeof import('ant-design-vue/es')['Space']
ASpin: typeof import('ant-design-vue/es')['Spin']
ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
ASwitch: typeof import('ant-design-vue/es')['Switch']

View File

@ -1 +1 @@
import{d as E,bC as $,r as f,m as M,_ as T,a as c,an as W,h as m,c as v,P as z}from"./index-632e7cf6.js";var G=["prefixCls","name","id","type","disabled","readonly","tabindex","autofocus","value","required"],H={prefixCls:String,name:String,id:String,type:String,defaultChecked:{type:[Boolean,Number],default:void 0},checked:{type:[Boolean,Number],default:void 0},disabled:Boolean,tabindex:{type:[Number,String]},readonly:Boolean,autofocus:Boolean,value:z.any,required:Boolean};const L=E({compatConfig:{MODE:3},name:"Checkbox",inheritAttrs:!1,props:$(H,{prefixCls:"rc-checkbox",type:"checkbox",defaultChecked:!1}),emits:["click","change"],setup:function(a,d){var t=d.attrs,h=d.emit,g=d.expose,o=f(a.checked===void 0?a.defaultChecked:a.checked),i=f();M(function(){return a.checked},function(){o.value=a.checked}),g({focus:function(){var e;(e=i.value)===null||e===void 0||e.focus()},blur:function(){var e;(e=i.value)===null||e===void 0||e.blur()}});var l=f(),x=function(e){if(!a.disabled){a.checked===void 0&&(o.value=e.target.checked),e.shiftKey=l.value;var r={target:c(c({},a),{},{checked:e.target.checked}),stopPropagation:function(){e.stopPropagation()},preventDefault:function(){e.preventDefault()},nativeEvent:e};a.checked!==void 0&&(i.value.checked=!!a.checked),h("change",r),l.value=!1}},C=function(e){h("click",e),l.value=e.shiftKey};return function(){var n,e=a.prefixCls,r=a.name,s=a.id,p=a.type,b=a.disabled,K=a.readonly,P=a.tabindex,B=a.autofocus,S=a.value,N=a.required,_=T(a,G),q=t.class,D=t.onFocus,j=t.onBlur,w=t.onKeydown,A=t.onKeypress,F=t.onKeyup,y=c(c({},_),t),O=Object.keys(y).reduce(function(k,u){return(u.substr(0,5)==="aria-"||u.substr(0,5)==="data-"||u==="role")&&(k[u]=y[u]),k},{}),R=W(e,q,(n={},m(n,"".concat(e,"-checked"),o.value),m(n,"".concat(e,"-disabled"),b),n)),V=c(c({name:r,id:s,type:p,readonly:K,disabled:b,tabindex:P,class:"".concat(e,"-input"),checked:!!o.value,autofocus:B,value:S},O),{},{onChange:x,onClick:C,onFocus:D,onBlur:j,onKeydown:w,onKeypress:A,onKeyup:F,required:N});return v("span",{class:R},[v("input",c({ref:i},V),null),v("span",{class:"".concat(e,"-inner")},null)])}}});export{L as V};
import{d as E,bC as $,r as f,m as M,_ as T,a as c,an as W,h as m,c as v,P as z}from"./index-d6594e8e.js";var G=["prefixCls","name","id","type","disabled","readonly","tabindex","autofocus","value","required"],H={prefixCls:String,name:String,id:String,type:String,defaultChecked:{type:[Boolean,Number],default:void 0},checked:{type:[Boolean,Number],default:void 0},disabled:Boolean,tabindex:{type:[Number,String]},readonly:Boolean,autofocus:Boolean,value:z.any,required:Boolean};const L=E({compatConfig:{MODE:3},name:"Checkbox",inheritAttrs:!1,props:$(H,{prefixCls:"rc-checkbox",type:"checkbox",defaultChecked:!1}),emits:["click","change"],setup:function(a,d){var t=d.attrs,h=d.emit,g=d.expose,o=f(a.checked===void 0?a.defaultChecked:a.checked),i=f();M(function(){return a.checked},function(){o.value=a.checked}),g({focus:function(){var e;(e=i.value)===null||e===void 0||e.focus()},blur:function(){var e;(e=i.value)===null||e===void 0||e.blur()}});var l=f(),x=function(e){if(!a.disabled){a.checked===void 0&&(o.value=e.target.checked),e.shiftKey=l.value;var r={target:c(c({},a),{},{checked:e.target.checked}),stopPropagation:function(){e.stopPropagation()},preventDefault:function(){e.preventDefault()},nativeEvent:e};a.checked!==void 0&&(i.value.checked=!!a.checked),h("change",r),l.value=!1}},C=function(e){h("click",e),l.value=e.shiftKey};return function(){var n,e=a.prefixCls,r=a.name,s=a.id,p=a.type,b=a.disabled,K=a.readonly,P=a.tabindex,B=a.autofocus,S=a.value,N=a.required,_=T(a,G),q=t.class,D=t.onFocus,j=t.onBlur,w=t.onKeydown,A=t.onKeypress,F=t.onKeyup,y=c(c({},_),t),O=Object.keys(y).reduce(function(k,u){return(u.substr(0,5)==="aria-"||u.substr(0,5)==="data-"||u==="role")&&(k[u]=y[u]),k},{}),R=W(e,q,(n={},m(n,"".concat(e,"-checked"),o.value),m(n,"".concat(e,"-disabled"),b),n)),V=c(c({name:r,id:s,type:p,readonly:K,disabled:b,tabindex:P,class:"".concat(e,"-input"),checked:!!o.value,autofocus:B,value:S},O),{},{onChange:x,onClick:C,onFocus:D,onBlur:j,onKeydown:w,onKeypress:A,onKeyup:F,required:N});return v("span",{class:R},[v("input",c({ref:i},V),null),v("span",{class:"".concat(e,"-inner")},null)])}}});export{L as V};

File diff suppressed because one or more lines are too long

1
vue/dist/assets/FileItem-6eff082d.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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
.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

@ -0,0 +1 @@
.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

@ -1 +0,0 @@
.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}

View File

@ -1 +0,0 @@
.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}

View File

@ -1 +0,0 @@
.full-screen-menu[data-v-bb3c8014]{position:fixed;z-index:9999;background:var(--zp-primary-background);padding:8px 16px;box-shadow:0 0 4px var(--zp-secondary);border-radius:4px}.full-screen-menu .tags-container[data-v-bb3c8014]{margin:4px 0}.full-screen-menu .tags-container .tag[data-v-bb3c8014]{margin-right:4px;margin-bottom:4px;padding:2px 16px;border-radius:4px;display:inline-block;cursor:pointer;font-weight:700;transition:.5s all ease;border:2px solid var(--tag-color);color:var(--tag-color);background:var(--zp-primary-background);user-select:none}.full-screen-menu .tags-container .tag.selected[data-v-bb3c8014]{background:var(--tag-color);color:#fff}.full-screen-menu .container[data-v-bb3c8014]{height:100%;display:flex;overflow:hidden;flex-direction:column}.full-screen-menu .gen-info[data-v-bb3c8014]{flex:1;word-break:break-all;white-space:pre-line;overflow:auto;z-index:1;padding-top:4px;position:relative}.full-screen-menu .gen-info code[data-v-bb3c8014]{font-size:.9em;display:block;padding:4px;background:var(--zp-primary-background);border-radius:4px;margin-right:20px;white-space:pre-wrap;word-break:break-word;line-height:1.78em}.full-screen-menu .gen-info code[data-v-bb3c8014] .natural-text{margin:.5em 0;line-height:1.6em;text-align:justify;color:var(--zp-primary)}.full-screen-menu .gen-info code[data-v-bb3c8014] .short-tag{word-break:break-all;white-space:nowrap}.full-screen-menu .gen-info code[data-v-bb3c8014] span.tag{background:var(--zp-secondary-variant-background);color:var(--zp-primary);padding:2px 4px;border-radius:6px;margin-right:6px;margin-top:4px;line-height:1.3em;display:inline-block}.full-screen-menu .gen-info code[data-v-bb3c8014] .has-parentheses.tag{background:rgba(255,100,100,.14)}.full-screen-menu .gen-info code[data-v-bb3c8014] span.tag:hover{background:rgba(120,0,0,.15)}.full-screen-menu .gen-info table[data-v-bb3c8014]{font-size:1em;border-radius:4px;border-collapse:separate;margin-bottom:3em}.full-screen-menu .gen-info table tr td[data-v-bb3c8014]:first-child{white-space:nowrap;vertical-align:top}.full-screen-menu .gen-info table.extra-meta-table .extra-meta-value[data-v-bb3c8014]{display:block;max-height:200px;overflow:auto;white-space:pre-wrap;word-break:break-word;font-size:.85em;background:var(--zp-secondary-variant-background);padding:8px;border-radius:4px}.full-screen-menu .gen-info table td[data-v-bb3c8014]{padding-right:14px;padding-left:4px;border-bottom:1px solid var(--zp-secondary);border-collapse:collapse}.full-screen-menu .gen-info .info-tags .info-tag[data-v-bb3c8014]{display:inline-block;overflow:hidden;border-radius:4px;margin-right:8px;border:2px solid var(--zp-primary)}.full-screen-menu .gen-info .info-tags .name[data-v-bb3c8014]{background-color:var(--zp-primary);color:var(--zp-primary-background);padding:4px;border-bottom-right-radius:4px}.full-screen-menu .gen-info .info-tags .value[data-v-bb3c8014]{padding:4px}.full-screen-menu.unset-size[data-v-bb3c8014]{width:unset!important;height:unset!important}.full-screen-menu .mouse-sensor[data-v-bb3c8014]{position:absolute;bottom:0;right:0;transform:rotate(90deg);cursor:se-resize;z-index:1;background:var(--zp-primary-background);border-radius:2px}.full-screen-menu .mouse-sensor>*[data-v-bb3c8014]{font-size:18px;padding:4px}.full-screen-menu .action-bar[data-v-bb3c8014]{display:flex;align-items:center;user-select:none;gap:4px}.full-screen-menu .action-bar .icon[data-v-bb3c8014]{font-size:1.5em;padding:2px 4px;border-radius:4px}.full-screen-menu .action-bar .icon[data-v-bb3c8014]:hover{background:var(--zp-secondary-variant-background)}.full-screen-menu .action-bar>*[data-v-bb3c8014]{flex-wrap:wrap}.full-screen-menu.lr[data-v-bb3c8014]{top:var(--3fe0608e)!important;right:0!important;bottom:0!important;left:100vw!important;height:unset!important;width:var(--2653b66c)!important;transition:left ease .3s}.full-screen-menu.lr.always-on[data-v-bb3c8014],.full-screen-menu.lr.mouse-in[data-v-bb3c8014]{left:var(--1097f730)!important}.tag-alpha-item[data-v-bb3c8014]{display:flex;margin-top:4px}.tag-alpha-item h4[data-v-bb3c8014]{width:32px;flex-shrink:0}.sort-tag-switch[data-v-bb3c8014]{display:inline-block;padding-right:16px;padding-left:8px;cursor:pointer;user-select:none}.sort-tag-switch span[data-v-bb3c8014]{transition:all ease .3s;transform:scale(1.2)}.sort-tag-switch:hover span[data-v-bb3c8014]{transform:scale(1.3)}.lr-layout-control[data-v-bb3c8014]{display:flex;align-items:center;gap:16px;padding:4px 8px;flex-wrap:wrap;border-radius:2px;border-left:3px solid var(--zp-luminous);background-color:var(--zp-secondary-background)}.lr-layout-control .ctrl-item[data-v-bb3c8014]{display:flex;align-items:center;gap:4px;flex-wrap:nowrap}.select-actions[data-v-b04c3508]>:not(:last-child){margin-right:4px}.float-panel[data-v-b04c3508]{position:absolute;bottom:32px;right:32px;background:var(--zp-primary-background);border-radius:4px;z-index:1000;padding:8px;box-shadow:0 0 4px var(--zp-secondary)}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.full-screen-menu[data-v-c7e0b9b7]{position:fixed;z-index:9999;background:var(--zp-primary-background);padding:8px 16px;box-shadow:0 0 4px var(--zp-secondary);border-radius:4px}.full-screen-menu .tags-container[data-v-c7e0b9b7]{margin:4px 0}.full-screen-menu .tags-container .tag[data-v-c7e0b9b7]{margin-right:4px;margin-bottom:4px;padding:2px 16px;border-radius:4px;display:inline-block;cursor:pointer;font-weight:700;transition:.5s all ease;border:2px solid var(--tag-color);color:var(--tag-color);background:var(--zp-primary-background);user-select:none}.full-screen-menu .tags-container .tag.selected[data-v-c7e0b9b7]{background:var(--tag-color);color:#fff}.full-screen-menu .container[data-v-c7e0b9b7]{height:100%;display:flex;overflow:hidden;flex-direction:column}.full-screen-menu .gen-info[data-v-c7e0b9b7]{flex:1;word-break:break-all;white-space:pre-line;overflow:auto;z-index:1;padding-top:4px;position:relative}.full-screen-menu .gen-info code[data-v-c7e0b9b7]{font-size:.9em;display:block;padding:4px;background:var(--zp-primary-background);border-radius:4px;margin-right:20px;white-space:pre-wrap;word-break:break-word;line-height:1.78em}.full-screen-menu .gen-info code[data-v-c7e0b9b7] .natural-text{margin:.5em 0;line-height:1.6em;text-align:justify;color:var(--zp-primary)}.full-screen-menu .gen-info code[data-v-c7e0b9b7] .short-tag{word-break:break-all;white-space:nowrap}.full-screen-menu .gen-info code[data-v-c7e0b9b7] span.tag{background:var(--zp-secondary-variant-background);color:var(--zp-primary);padding:2px 4px;border-radius:6px;margin-right:6px;margin-top:4px;line-height:1.3em;display:inline-block}.full-screen-menu .gen-info code[data-v-c7e0b9b7] .has-parentheses.tag{background:rgba(255,100,100,.14)}.full-screen-menu .gen-info code[data-v-c7e0b9b7] span.tag:hover{background:rgba(120,0,0,.15)}.full-screen-menu .gen-info table[data-v-c7e0b9b7]{font-size:1em;border-radius:4px;border-collapse:separate;margin-bottom:3em}.full-screen-menu .gen-info table tr td[data-v-c7e0b9b7]:first-child{white-space:nowrap;vertical-align:top}.full-screen-menu .gen-info table.extra-meta-table .extra-meta-value[data-v-c7e0b9b7]{display:block;max-height:200px;overflow:auto;white-space:pre-wrap;word-break:break-word;font-size:.85em;background:var(--zp-secondary-variant-background);padding:8px;border-radius:4px}.full-screen-menu .gen-info table td[data-v-c7e0b9b7]{padding-right:14px;padding-left:4px;border-bottom:1px solid var(--zp-secondary);border-collapse:collapse}.full-screen-menu .gen-info .info-tags .info-tag[data-v-c7e0b9b7]{display:inline-block;overflow:hidden;border-radius:4px;margin-right:8px;border:2px solid var(--zp-primary)}.full-screen-menu .gen-info .info-tags .name[data-v-c7e0b9b7]{background-color:var(--zp-primary);color:var(--zp-primary-background);padding:4px;border-bottom-right-radius:4px}.full-screen-menu .gen-info .info-tags .value[data-v-c7e0b9b7]{padding:4px}.full-screen-menu.unset-size[data-v-c7e0b9b7]{width:unset!important;height:unset!important}.full-screen-menu .mouse-sensor[data-v-c7e0b9b7]{position:absolute;bottom:0;right:0;transform:rotate(90deg);cursor:se-resize;z-index:1;background:var(--zp-primary-background);border-radius:2px}.full-screen-menu .mouse-sensor>*[data-v-c7e0b9b7]{font-size:18px;padding:4px}.full-screen-menu .action-bar[data-v-c7e0b9b7]{display:flex;align-items:center;user-select:none;gap:4px}.full-screen-menu .action-bar .icon[data-v-c7e0b9b7]{font-size:1.5em;padding:2px 4px;border-radius:4px}.full-screen-menu .action-bar .icon[data-v-c7e0b9b7]:hover{background:var(--zp-secondary-variant-background)}.full-screen-menu .action-bar>*[data-v-c7e0b9b7]{flex-wrap:wrap}.full-screen-menu.lr[data-v-c7e0b9b7]{top:var(--b7cd59ce)!important;right:0!important;bottom:0!important;left:100vw!important;height:unset!important;width:var(--0e09e1cc)!important;transition:left ease .3s}.full-screen-menu.lr.always-on[data-v-c7e0b9b7],.full-screen-menu.lr.mouse-in[data-v-c7e0b9b7]{left:var(--62228ae0)!important}.tag-alpha-item[data-v-c7e0b9b7]{display:flex;margin-top:4px}.tag-alpha-item h4[data-v-c7e0b9b7]{width:32px;flex-shrink:0}.sort-tag-switch[data-v-c7e0b9b7]{display:inline-block;padding-right:16px;padding-left:8px;cursor:pointer;user-select:none}.sort-tag-switch span[data-v-c7e0b9b7]{transition:all ease .3s;transform:scale(1.2)}.sort-tag-switch:hover span[data-v-c7e0b9b7]{transform:scale(1.3)}.lr-layout-control[data-v-c7e0b9b7]{display:flex;align-items:center;gap:16px;padding:4px 8px;flex-wrap:wrap;border-radius:2px;border-left:3px solid var(--zp-luminous);background-color:var(--zp-secondary-background)}.lr-layout-control .ctrl-item[data-v-c7e0b9b7]{display:flex;align-items:center;gap:4px;flex-wrap:nowrap}.select-actions[data-v-b6f9a67c]>:not(:last-child){margin-right:4px}.float-panel[data-v-b6f9a67c]{position:absolute;bottom:32px;right:32px;background:var(--zp-primary-background);border-radius:4px;z-index:1000;padding:8px;box-shadow:0 0 4px var(--zp-secondary)}

View File

@ -1 +0,0 @@
[data-v-e1bd92bd] .float-panel{position:fixed}.regex-icon[data-v-e1bd92bd]{user-select:none;padding:4px;margin:0 4px;cursor:pointer;border:1px solid var(--zp-border);border-radius:4px}.regex-icon img[data-v-e1bd92bd]{height:1.5em}.regex-icon[data-v-e1bd92bd]:hover{background:var(--zp-border)}.regex-icon.selected[data-v-e1bd92bd]{background:var(--primary-color-1);border:1px solid var(--primary-color)}.search-bar[data-v-e1bd92bd]{padding:8px 8px 0;display:flex}.search-bar.last[data-v-e1bd92bd]{padding-bottom:8px}.search-bar .form-name[data-v-e1bd92bd]{flex-shrink:0;padding:4px 8px}.search-bar .actions>*[data-v-e1bd92bd]{margin-right:4px}.container[data-v-e1bd92bd]{background:var(--zp-secondary-background);position:relative}.container .file-list[data-v-e1bd92bd]{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-2a7f2e56] .float-panel{position:fixed}.regex-icon[data-v-2a7f2e56]{user-select:none;padding:4px;margin:0 4px;cursor:pointer;border:1px solid var(--zp-border);border-radius:4px}.regex-icon img[data-v-2a7f2e56]{height:1.5em}.regex-icon[data-v-2a7f2e56]:hover{background:var(--zp-border)}.regex-icon.selected[data-v-2a7f2e56]{background:var(--primary-color-1);border:1px solid var(--primary-color)}.search-bar[data-v-2a7f2e56]{padding:8px 8px 0;display:flex}.search-bar.last[data-v-2a7f2e56]{padding-bottom:8px}.search-bar .form-name[data-v-2a7f2e56]{flex-shrink:0;padding:4px 8px}.search-bar .actions>*[data-v-2a7f2e56]{margin-right:4px}.container[data-v-2a7f2e56]{background:var(--zp-secondary-background);position:relative}.container .file-list[data-v-2a7f2e56]{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

File diff suppressed because one or more lines are too long

62
vue/dist/assets/TopicSearch-5af266be.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

View File

@ -1 +1 @@
import{bV as i,b1 as t,e5 as f,bM as n}from"./index-632e7cf6.js";function u(e,s,r){if(!i(r))return!1;var a=typeof s;return(a=="number"?t(r)&&f(s,r.length):a=="string"&&s in r)?n(r[s],e):!1}export{u as i};
import{bV as i,b1 as t,e6 as f,bM as n}from"./index-d6594e8e.js";function u(e,s,r){if(!i(r))return!1;var a=typeof s;return(a=="number"?t(r)&&f(s,r.length):a=="string"&&s in r)?n(r[s],e):!1}export{u as i};

View File

@ -1 +0,0 @@
.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

@ -0,0 +1 @@
.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 +1 @@
import{d as F,a1 as B,cO as $,cc as S,U as _,V as w,W as f,c as l,a3 as d,X as p,Y as c,a4 as s,a2 as A,af as R,cP as V,cQ as y,z as x,B as E,ak as T,a0 as U}from"./index-632e7cf6.js";import{_ as N}from"./index-1bd869eb.js";import{u as L,a as O,f as H,F as P,d as Q}from"./FileItem-70868dea.js";import"./numInput.vue_vue_type_style_index_0_scoped_55978858_lang-221425da.js";/* empty css */import"./index-a2a27adc.js";import"./_isIterateeCall-582f579a.js";import"./index-9d95a206.js";import"./index-a44b2ffa.js";const W={class:"actions-panel actions"},j={class:"item"},q={key:0,class:"file-list"},G={class:"hint"},X=F({__name:"batchDownload",props:{tabIdx:{},paneIdx:{},id:{}},setup(Y){const{stackViewEl:D}=L().toRefs(),{itemSize:h,gridItems:b,cellWidth:g}=O(),i=B(),m=H(),{selectdFiles:a}=$(m),r=S(),v=async e=>{const t=V(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(E("success"))})},z=e=>{a.value.splice(e,1)};return(e,t)=>{const o=T,u=N;return _(),w("div",{class:"container",ref_key:"stackViewEl",ref:D,onDrop:v},[f("div",W,[l(o,{onClick:t[0]||(t[0]=n=>s(m).selectdFiles=[])},{default:d(()=>[p(c(e.$t("clear")),1)]),_:1}),f("div",j,[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(Q),{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(b)},{default:d(({item:n,index:k})=>[l(P,{idx:k,file:n,"cell-width":s(g),"enable-close-icon":"",onCloseIconClick:J=>z(k),"full-screen-preview-image-url":s(R)(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",G,c(e.$t("batchDownloaDDragAndDropHint")),1)]))],544)}}});const le=U(X,[["__scopeId","data-v-a2642a17"]]);export{le as default};
import{d as F,a1 as B,cP as $,cc as S,U as _,V as w,W as f,c as l,a3 as d,X as p,Y as c,a4 as s,a2 as R,af as A,cQ as V,cR as y,z as x,B as E,ak as T,a0 as U}from"./index-d6594e8e.js";import{_ as N}from"./index-e620c28b.js";import{u as L,a as H,f as O,F as P,d as Q}from"./FileItem-2eb00024.js";import"./numInput.vue_vue_type_style_index_0_scoped_bd954eda_lang-c8c8013d.js";/* empty css */import"./index-cb6a3d31.js";import"./_isIterateeCall-bfd79848.js";import"./index-30b8f89a.js";import"./index-9b184148.js";const W={class:"actions-panel actions"},j={class:"item"},q={key:0,class:"file-list"},G={class:"hint"},X=F({__name:"batchDownload",props:{tabIdx:{},paneIdx:{},id:{}},setup(Y){const{stackViewEl:D}=L().toRefs(),{itemSize:h,gridItems:b,cellWidth:g}=H(),i=B(),m=O(),{selectdFiles:a}=$(m),r=S(),v=async e=>{const t=V(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(E("success"))})},z=e=>{a.value.splice(e,1)};return(e,t)=>{const o=T,u=N;return _(),w("div",{class:"container",ref_key:"stackViewEl",ref:D,onDrop:v},[f("div",W,[l(o,{onClick:t[0]||(t[0]=n=>s(m).selectdFiles=[])},{default:d(()=>[p(c(e.$t("clear")),1)]),_:1}),f("div",j,[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(Q),{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(b)},{default:d(({item:n,index:k})=>[l(P,{idx:k,file:n,"cell-width":s(g),"enable-close-icon":"",onCloseIconClick:J=>z(k),"full-screen-preview-image-url":s(A)(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",G,c(e.$t("batchDownloaDDragAndDropHint")),1)]))],544)}}});const le=U(X,[["__scopeId","data-v-3d7e6f2d"]]);export{le 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

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

@ -0,0 +1 @@
import{u as w,a as y,F as b,d as k}from"./FileItem-2eb00024.js";import{d as x,a1 as h,c9 as F,r as D,bh as I,bl as C,U as V,V as E,c as n,a3 as S,a4 as e,af as z,cQ as B,cS as R,a0 as A}from"./index-d6594e8e.js";import"./numInput.vue_vue_type_style_index_0_scoped_bd954eda_lang-c8c8013d.js";/* empty css */import"./index-cb6a3d31.js";import"./_isIterateeCall-bfd79848.js";import"./index-30b8f89a.js";import"./index-9b184148.js";const K=x({__name:"gridView",props:{tabIdx:{},paneIdx:{},id:{},removable:{type:Boolean},allowDragAndDrop:{type:Boolean},files:{},paneKey:{}},setup(p){const o=p,m=h(),{stackViewEl:d}=w().toRefs(),{itemSize:i,gridItems:u,cellWidth:f}=y(),g=F(),a=D(o.files??[]),_=async s=>{const l=B(s);o.allowDragAndDrop&&l&&(a.value=R([...a.value,...l.nodes]))},v=s=>{a.value.splice(s,1)};return I(()=>{m.pageFuncExportMap.set(o.paneKey,{getFiles:()=>C(a.value),setFiles:s=>a.value=s})}),(s,l)=>(V(),E("div",{class:"container",ref_key:"stackViewEl",ref:d,onDrop:_},[n(e(k),{ref:"scroller",class:"file-list",items:a.value.slice(),"item-size":e(i).first,"key-field":"fullpath","item-secondary-size":e(i).second,gridItems:e(u)},{default:S(({item:t,index:r})=>{var c;return[n(b,{idx:r,file:t,"cell-width":e(f),"enable-close-icon":o.removable,onCloseIconClick:T=>v(r),"full-screen-preview-image-url":e(z)(t),"extra-tags":(c=t==null?void 0:t.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 W=A(K,[["__scopeId","data-v-0c31f6b2"]]);export{W as default};

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

@ -0,0 +1 @@
.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}

View File

@ -1 +0,0 @@
import{u as w,a as y,F as k,d as x}from"./FileItem-70868dea.js";import{d as h,a1 as F,c9 as b,r as D,bh as I,bl as C,U as V,V as E,c,a3 as z,a4 as e,af as S,cP as B,cR as R,a0 as A}from"./index-632e7cf6.js";import"./numInput.vue_vue_type_style_index_0_scoped_55978858_lang-221425da.js";/* empty css */import"./index-a2a27adc.js";import"./_isIterateeCall-582f579a.js";import"./index-9d95a206.js";import"./index-a44b2ffa.js";const K=h({__name:"gridView",props:{tabIdx:{},paneIdx:{},id:{},removable:{type:Boolean},allowDragAndDrop:{type:Boolean},files:{},paneKey:{}},setup(p){const o=p,m=F(),{stackViewEl:d}=w().toRefs(),{itemSize:i,gridItems:u,cellWidth:f}=y(),g=b(),a=D(o.files??[]),_=async s=>{const l=B(s);o.allowDragAndDrop&&l&&(a.value=R([...a.value,...l.nodes]))},v=s=>{a.value.splice(s,1)};return I(()=>{m.pageFuncExportMap.set(o.paneKey,{getFiles:()=>C(a.value),setFiles:s=>a.value=s})}),(s,l)=>(V(),E("div",{class:"container",ref_key:"stackViewEl",ref:d,onDrop:_},[c(e(x),{ref:"scroller",class:"file-list",items:a.value.slice(),"item-size":e(i).first,"key-field":"fullpath","item-secondary-size":e(i).second,gridItems:e(u)},{default:z(({item:t,index:r})=>{var n;return[c(k,{idx:r,file:t,"cell-width":e(f),"enable-close-icon":o.removable,onCloseIconClick:T=>v(r),"full-screen-preview-image-url":e(S)(t),"extra-tags":(n=t==null?void 0:t.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 W=A(K,[["__scopeId","data-v-f35f4802"]]);export{W as default};

View File

@ -1 +0,0 @@
.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 +1 @@
import{am as F,r as g,l as P,k as A,O as b,G as R,cc as q,cn as O,cs as z}from"./index-632e7cf6.js";import{u as L,a as Q,b as j,e as H}from"./FileItem-70868dea.js";import{a as T,b as U,c as W}from"./MultiSelectKeep-a8fa9049.js";import{u as B}from"./useGenInfoDiff-021d05e1.js";let K=0;const V=()=>++K,X=(n,i,{dataUpdateStrategy:l="replace"}={})=>{const o=F([""]),c=g(!1),t=g(),a=g(!1);let f=g(-1);const v=new Set,w=e=>{var s;l==="replace"?t.value=e:l==="merge"&&(b((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=>A(void 0,void 0,void 0,function*(){if(a.value||c.value&&typeof e>"u")return!1;a.value=!0;const s=V();f.value=s;try{let r;if(typeof e=="number"){if(r=o[e],typeof r!="string")return!1}else r=o[o.length-1];const h=yield n(r);if(v.has(s))return v.delete(s),!1;w(i(h));const u=h.cursor;if((e===o.length-1||typeof e!="number")&&(c.value=!u.has_next,u.has_next)){const m=u.next_cursor||u.next;b(typeof m=="string"),o.push(m)}}finally{f.value===s&&(a.value=!1)}return!0}),p=()=>{v.add(f.value),a.value=!1},x=(e=!1)=>A(void 0,void 0,void 0,function*(){const{refetch:s,force:r}=typeof e=="object"?e:{refetch:e};r&&p(),b(!a.value),o.splice(0,o.length,""),a.value=!1,t.value=void 0,c.value=!1,s&&(yield d())}),I=()=>({next:()=>A(void 0,void 0,void 0,function*(){if(a.value)throw new Error("不允许同时迭代");return{done:!(yield d()),value:t.value}})});return P({abort:p,load:c,next:d,res:t,loading:a,cursorStack:o,reset:x,[Symbol.asyncIterator]:I,iter:{[Symbol.asyncIterator]:I}})},se=n=>F(X(n,i=>i.files,{dataUpdateStrategy:"merge"})),ne=n=>{const i=F(new Set),l=R(()=>(n.res??[]).filter(y=>!i.has(y.fullpath))),o=q(),{stackViewEl:c,multiSelectedIdxs:t,stack:a,scroller:f,props:v}=L({images:l}).toRefs(),{itemSize:w,gridItems:d,cellWidth:p,onScroll:x}=Q({fetchNext:()=>n.next()}),{showMenuIdx:I}=j(),{onFileDragStart:e,onFileDragEnd:s}=T(),{showGenInfo:r,imageGenInfo:h,q:u,onContextMenuClick:m,onFileItemClick:C}=U({openNext:O}),{previewIdx:_,previewing:E,onPreviewVisibleChange:M,previewImgMove:D,canPreview:G}=W({loadNext:()=>n.next()}),J=async(y,S,N)=>{a.value=[{curr:"",files:l.value}],await m(y,S,N)};H("removeFiles",async({paths:y})=>{y.forEach(S=>i.add(S))});const k=()=>{z(l.value)};return{images:l,scroller:f,queue:o,iter:n,onContextMenuClickU:J,stackViewEl:c,previewIdx:_,previewing:E,onPreviewVisibleChange:M,previewImgMove:D,canPreview:G,itemSize:w,gridItems:d,showGenInfo:r,imageGenInfo:h,q:u,onContextMenuClick:m,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{se as c,ne as u};
import{am as F,r as g,l as P,k as A,O as b,G as R,cc as q,cn as O,cs as z}from"./index-d6594e8e.js";import{u as L,a as Q,b as j,e as H}from"./FileItem-2eb00024.js";import{a as T,b as U,c as W}from"./MultiSelectKeep-ba3817a4.js";import{u as B}from"./useGenInfoDiff-f63a5430.js";let K=0;const V=()=>++K,X=(n,i,{dataUpdateStrategy:l="replace"}={})=>{const o=F([""]),c=g(!1),t=g(),a=g(!1);let f=g(-1);const v=new Set,w=e=>{var s;l==="replace"?t.value=e:l==="merge"&&(b((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=>A(void 0,void 0,void 0,function*(){if(a.value||c.value&&typeof e>"u")return!1;a.value=!0;const s=V();f.value=s;try{let r;if(typeof e=="number"){if(r=o[e],typeof r!="string")return!1}else r=o[o.length-1];const h=yield n(r);if(v.has(s))return v.delete(s),!1;w(i(h));const u=h.cursor;if((e===o.length-1||typeof e!="number")&&(c.value=!u.has_next,u.has_next)){const m=u.next_cursor||u.next;b(typeof m=="string"),o.push(m)}}finally{f.value===s&&(a.value=!1)}return!0}),p=()=>{v.add(f.value),a.value=!1},x=(e=!1)=>A(void 0,void 0,void 0,function*(){const{refetch:s,force:r}=typeof e=="object"?e:{refetch:e};r&&p(),b(!a.value),o.splice(0,o.length,""),a.value=!1,t.value=void 0,c.value=!1,s&&(yield d())}),I=()=>({next:()=>A(void 0,void 0,void 0,function*(){if(a.value)throw new Error("不允许同时迭代");return{done:!(yield d()),value:t.value}})});return P({abort:p,load:c,next:d,res:t,loading:a,cursorStack:o,reset:x,[Symbol.asyncIterator]:I,iter:{[Symbol.asyncIterator]:I}})},se=n=>F(X(n,i=>i.files,{dataUpdateStrategy:"merge"})),ne=n=>{const i=F(new Set),l=R(()=>(n.res??[]).filter(y=>!i.has(y.fullpath))),o=q(),{stackViewEl:c,multiSelectedIdxs:t,stack:a,scroller:f,props:v}=L({images:l}).toRefs(),{itemSize:w,gridItems:d,cellWidth:p,onScroll:x}=Q({fetchNext:()=>n.next()}),{showMenuIdx:I}=j(),{onFileDragStart:e,onFileDragEnd:s}=T(),{showGenInfo:r,imageGenInfo:h,q:u,onContextMenuClick:m,onFileItemClick:C}=U({openNext:O}),{previewIdx:_,previewing:E,onPreviewVisibleChange:M,previewImgMove:D,canPreview:G}=W({loadNext:()=>n.next()}),J=async(y,S,N)=>{a.value=[{curr:"",files:l.value}],await m(y,S,N)};H("removeFiles",async({paths:y})=>{y.forEach(S=>i.add(S))});const k=()=>{z(l.value)};return{images:l,scroller:f,queue:o,iter:n,onContextMenuClickU:J,stackViewEl:c,previewIdx:_,previewing:E,onPreviewVisibleChange:M,previewImgMove:D,canPreview:G,itemSize:w,gridItems:d,showGenInfo:r,imageGenInfo:h,q:u,onContextMenuClick:m,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{se as c,ne as u};

1
vue/dist/assets/index-154b94da.js vendored Normal file
View File

@ -0,0 +1 @@
import{r as o,o as t,cJ as n}from"./index-d6594e8e.js";const a=function(){var e=o(!1);return t(function(){e.value=n()}),e};export{a as u};

View File

@ -1 +1 @@
import{d as F,u as S,G as k,an as j,h as d,c as s,aq as U,e6 as W,r as q,bh as G,Z as V,dv as Z,P as N,cb as z}from"./index-632e7cf6.js";var H=function(){return{prefixCls:String,checked:{type:Boolean,default:void 0},onChange:{type:Function},onClick:{type:Function},"onUpdate:checked":Function}},J=F({compatConfig:{MODE:3},name:"ACheckableTag",props:H(),setup:function(e,i){var l=i.slots,r=i.emit,g=S("tag",e),u=g.prefixCls,o=function(C){var v=e.checked;r("update:checked",!v),r("change",!v),r("click",C)},p=k(function(){var a;return j(u.value,(a={},d(a,"".concat(u.value,"-checkable"),!0),d(a,"".concat(u.value,"-checkable-checked"),e.checked),a))});return function(){var a;return s("span",{class:p.value,onClick:o},[(a=l.default)===null||a===void 0?void 0:a.call(l)])}}});const b=J;var K=new RegExp("^(".concat(U.join("|"),")(-inverse)?$")),L=new RegExp("^(".concat(W.join("|"),")$")),Q=function(){return{prefixCls:String,color:{type:String},closable:{type:Boolean,default:!1},closeIcon:N.any,visible:{type:Boolean,default:void 0},onClose:{type:Function},"onUpdate:visible":Function,icon:N.any}},f=F({compatConfig:{MODE:3},name:"ATag",props:Q(),slots:["closeIcon","icon"],setup:function(e,i){var l=i.slots,r=i.emit,g=i.attrs,u=S("tag",e),o=u.prefixCls,p=u.direction,a=q(!0);G(function(){e.visible!==void 0&&(a.value=e.visible)});var C=function(t){t.stopPropagation(),r("update:visible",!1),r("close",t),!t.defaultPrevented&&e.visible===void 0&&(a.value=!1)},v=k(function(){var n=e.color;return n?K.test(n)||L.test(n):!1}),E=k(function(){var n;return j(o.value,(n={},d(n,"".concat(o.value,"-").concat(e.color),v.value),d(n,"".concat(o.value,"-has-color"),e.color&&!v.value),d(n,"".concat(o.value,"-hidden"),!a.value),d(n,"".concat(o.value,"-rtl"),p.value==="rtl"),n))});return function(){var n,t,h,m=e.icon,R=m===void 0?(n=l.icon)===null||n===void 0?void 0:n.call(l):m,y=e.color,_=e.closeIcon,P=_===void 0?(t=l.closeIcon)===null||t===void 0?void 0:t.call(l):_,x=e.closable,w=x===void 0?!1:x,B=function(){return w?P?s("span",{class:"".concat(o.value,"-close-icon"),onClick:C},[P]):s(z,{class:"".concat(o.value,"-close-icon"),onClick:C},null):null},O={backgroundColor:y&&!v.value?y:void 0},I=R||null,T=(h=l.default)===null||h===void 0?void 0:h.call(l),A=I?s(V,null,[I,s("span",null,[T])]):T,D="onClick"in g,$=s("span",{class:E.value,style:O},[A,B()]);return D?s(Z,null,{default:function(){return[$]}}):$}}});f.CheckableTag=b;f.install=function(c){return c.component(f.name,f),c.component(b.name,b),c};const Y=f;export{Y as _};
import{d as F,u as S,G as k,an as j,h as d,c as s,aq as U,e7 as W,r as q,bh as G,Z as V,dw as Z,P as N,cb as z}from"./index-d6594e8e.js";var H=function(){return{prefixCls:String,checked:{type:Boolean,default:void 0},onChange:{type:Function},onClick:{type:Function},"onUpdate:checked":Function}},J=F({compatConfig:{MODE:3},name:"ACheckableTag",props:H(),setup:function(e,i){var l=i.slots,r=i.emit,g=S("tag",e),u=g.prefixCls,o=function(C){var v=e.checked;r("update:checked",!v),r("change",!v),r("click",C)},p=k(function(){var a;return j(u.value,(a={},d(a,"".concat(u.value,"-checkable"),!0),d(a,"".concat(u.value,"-checkable-checked"),e.checked),a))});return function(){var a;return s("span",{class:p.value,onClick:o},[(a=l.default)===null||a===void 0?void 0:a.call(l)])}}});const b=J;var K=new RegExp("^(".concat(U.join("|"),")(-inverse)?$")),L=new RegExp("^(".concat(W.join("|"),")$")),Q=function(){return{prefixCls:String,color:{type:String},closable:{type:Boolean,default:!1},closeIcon:N.any,visible:{type:Boolean,default:void 0},onClose:{type:Function},"onUpdate:visible":Function,icon:N.any}},f=F({compatConfig:{MODE:3},name:"ATag",props:Q(),slots:["closeIcon","icon"],setup:function(e,i){var l=i.slots,r=i.emit,g=i.attrs,u=S("tag",e),o=u.prefixCls,p=u.direction,a=q(!0);G(function(){e.visible!==void 0&&(a.value=e.visible)});var C=function(t){t.stopPropagation(),r("update:visible",!1),r("close",t),!t.defaultPrevented&&e.visible===void 0&&(a.value=!1)},v=k(function(){var n=e.color;return n?K.test(n)||L.test(n):!1}),E=k(function(){var n;return j(o.value,(n={},d(n,"".concat(o.value,"-").concat(e.color),v.value),d(n,"".concat(o.value,"-has-color"),e.color&&!v.value),d(n,"".concat(o.value,"-hidden"),!a.value),d(n,"".concat(o.value,"-rtl"),p.value==="rtl"),n))});return function(){var n,t,h,m=e.icon,w=m===void 0?(n=l.icon)===null||n===void 0?void 0:n.call(l):m,y=e.color,_=e.closeIcon,P=_===void 0?(t=l.closeIcon)===null||t===void 0?void 0:t.call(l):_,x=e.closable,R=x===void 0?!1:x,B=function(){return R?P?s("span",{class:"".concat(o.value,"-close-icon"),onClick:C},[P]):s(z,{class:"".concat(o.value,"-close-icon"),onClick:C},null):null},O={backgroundColor:y&&!v.value?y:void 0},I=w||null,T=(h=l.default)===null||h===void 0?void 0:h.call(l),A=I?s(V,null,[I,s("span",null,[T])]):T,D="onClick"in g,$=s("span",{class:E.value,style:O},[A,B()]);return D?s(Z,null,{default:function(){return[$]}}):$}}});f.CheckableTag=b;f.install=function(c){return c.component(f.name,f),c.component(b.name,b),c};const Y=f;export{Y as _};

View File

@ -1 +1 @@
import{d as w,bC as D,av as A,cJ as j,az as k,n as V,cK as B,cL as y,e as $,c as a,_ as M,h as r,a as P,cM as T,P as b}from"./index-632e7cf6.js";var J=["class","style"],K=function(){return{prefixCls:String,spinning:{type:Boolean,default:void 0},size:String,wrapperClassName:String,tip:b.any,delay:Number,indicator:b.any}},p=null;function L(t,n){return!!t&&!!n&&!isNaN(Number(n))}function W(t){var n=t.indicator;p=typeof n=="function"?n:function(){return a(n,null,null)}}const q=w({compatConfig:{MODE:3},name:"ASpin",inheritAttrs:!1,props:D(K(),{size:"default",spinning:!0,wrapperClassName:""}),setup:function(){return{originalUpdateSpinning:null,configProvider:A("configProvider",j)}},data:function(){var n=this.spinning,e=this.delay,i=L(n,e);return{sSpinning:n&&!i}},created:function(){this.originalUpdateSpinning=this.updateSpinning,this.debouncifyUpdateSpinning(this.$props)},mounted:function(){this.updateSpinning()},updated:function(){var n=this;k(function(){n.debouncifyUpdateSpinning(),n.updateSpinning()})},beforeUnmount:function(){this.cancelExistingSpin()},methods:{debouncifyUpdateSpinning:function(n){var e=n||this.$props,i=e.delay;i&&(this.cancelExistingSpin(),this.updateSpinning=V(this.originalUpdateSpinning,i))},updateSpinning:function(){var n=this.spinning,e=this.sSpinning;e!==n&&(this.sSpinning=n)},cancelExistingSpin:function(){var n=this.updateSpinning;n&&n.cancel&&n.cancel()},renderIndicator:function(n){var e="".concat(n,"-dot"),i=B(this,"indicator");return i===null?null:(Array.isArray(i)&&(i=i.length===1?i[0]:i),y(i)?$(i,{class:e}):p&&y(p())?$(p(),{class:e}):a("span",{class:"".concat(e," ").concat(n,"-dot-spin")},[a("i",{class:"".concat(n,"-dot-item")},null),a("i",{class:"".concat(n,"-dot-item")},null),a("i",{class:"".concat(n,"-dot-item")},null),a("i",{class:"".concat(n,"-dot-item")},null)]))}},render:function(){var n,e,i,o=this.$props,f=o.size,x=o.prefixCls,h=o.tip,d=h===void 0?(n=(e=this.$slots).tip)===null||n===void 0?void 0:n.call(e):h,N=o.wrapperClassName,l=this.$attrs,v=l.class,_=l.style,C=M(l,J),S=this.configProvider,U=S.getPrefixCls,z=S.direction,s=U("spin",x),u=this.sSpinning,E=(i={},r(i,s,!0),r(i,"".concat(s,"-sm"),f==="small"),r(i,"".concat(s,"-lg"),f==="large"),r(i,"".concat(s,"-spinning"),u),r(i,"".concat(s,"-show-text"),!!d),r(i,"".concat(s,"-rtl"),z==="rtl"),r(i,v,!!v),i),m=a("div",P(P({},C),{},{style:_,class:E}),[this.renderIndicator(s),d?a("div",{class:"".concat(s,"-text")},[d]):null]),g=T(this);if(g&&g.length){var c,I=(c={},r(c,"".concat(s,"-container"),!0),r(c,"".concat(s,"-blur"),u),c);return a("div",{class:["".concat(s,"-nested-loading"),N]},[u&&a("div",{key:"loading"},[m]),a("div",{class:I,key:"container"},[g])])}return m}});export{q as S,W as s};
import{d as w,bC as D,av as A,cK as j,az as k,n as V,cL as B,cM as y,e as $,c as a,_ as M,h as r,a as P,cN as T,P as b}from"./index-d6594e8e.js";var K=["class","style"],L=function(){return{prefixCls:String,spinning:{type:Boolean,default:void 0},size:String,wrapperClassName:String,tip:b.any,delay:Number,indicator:b.any}},p=null;function O(t,n){return!!t&&!!n&&!isNaN(Number(n))}function q(t){var n=t.indicator;p=typeof n=="function"?n:function(){return a(n,null,null)}}const F=w({compatConfig:{MODE:3},name:"ASpin",inheritAttrs:!1,props:D(L(),{size:"default",spinning:!0,wrapperClassName:""}),setup:function(){return{originalUpdateSpinning:null,configProvider:A("configProvider",j)}},data:function(){var n=this.spinning,e=this.delay,i=O(n,e);return{sSpinning:n&&!i}},created:function(){this.originalUpdateSpinning=this.updateSpinning,this.debouncifyUpdateSpinning(this.$props)},mounted:function(){this.updateSpinning()},updated:function(){var n=this;k(function(){n.debouncifyUpdateSpinning(),n.updateSpinning()})},beforeUnmount:function(){this.cancelExistingSpin()},methods:{debouncifyUpdateSpinning:function(n){var e=n||this.$props,i=e.delay;i&&(this.cancelExistingSpin(),this.updateSpinning=V(this.originalUpdateSpinning,i))},updateSpinning:function(){var n=this.spinning,e=this.sSpinning;e!==n&&(this.sSpinning=n)},cancelExistingSpin:function(){var n=this.updateSpinning;n&&n.cancel&&n.cancel()},renderIndicator:function(n){var e="".concat(n,"-dot"),i=B(this,"indicator");return i===null?null:(Array.isArray(i)&&(i=i.length===1?i[0]:i),y(i)?$(i,{class:e}):p&&y(p())?$(p(),{class:e}):a("span",{class:"".concat(e," ").concat(n,"-dot-spin")},[a("i",{class:"".concat(n,"-dot-item")},null),a("i",{class:"".concat(n,"-dot-item")},null),a("i",{class:"".concat(n,"-dot-item")},null),a("i",{class:"".concat(n,"-dot-item")},null)]))}},render:function(){var n,e,i,o=this.$props,f=o.size,N=o.prefixCls,h=o.tip,d=h===void 0?(n=(e=this.$slots).tip)===null||n===void 0?void 0:n.call(e):h,x=o.wrapperClassName,l=this.$attrs,v=l.class,_=l.style,C=M(l,K),S=this.configProvider,U=S.getPrefixCls,z=S.direction,s=U("spin",N),u=this.sSpinning,E=(i={},r(i,s,!0),r(i,"".concat(s,"-sm"),f==="small"),r(i,"".concat(s,"-lg"),f==="large"),r(i,"".concat(s,"-spinning"),u),r(i,"".concat(s,"-show-text"),!!d),r(i,"".concat(s,"-rtl"),z==="rtl"),r(i,v,!!v),i),m=a("div",P(P({},C),{},{style:_,class:E}),[this.renderIndicator(s),d?a("div",{class:"".concat(s,"-text")},[d]):null]),g=T(this);if(g&&g.length){var c,I=(c={},r(c,"".concat(s,"-container"),!0),r(c,"".concat(s,"-blur"),u),c);return a("div",{class:["".concat(s,"-nested-loading"),x]},[u&&a("div",{key:"loading"},[m]),a("div",{class:I,key:"container"},[g])])}return m}});export{F as S,q as s};

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

@ -0,0 +1 @@
import{av as M,G as l,ax as D,ay as P,d as F,u as I,r as K,o as L,cx as _,b as y,bk as T,cy as A,an as $,h as o,c as B,a as G}from"./index-d6594e8e.js";import{u as V}from"./index-154b94da.js";var E=Symbol("rowContextKey"),W=function(r){D(E,r)},k=function(){return M(E,{gutter:l(function(){}),wrap:l(function(){}),supportFlexGap:l(function(){})})};P("top","middle","bottom","stretch");P("start","end","center","space-around","space-between");var U=function(){return{align:String,justify:String,prefixCls:String,gutter:{type:[Number,Array,Object],default:0},wrap:{type:Boolean,default:void 0}}},q=F({compatConfig:{MODE:3},name:"ARow",props:U(),setup:function(r,N){var g=N.slots,v=I("row",r),d=v.prefixCls,h=v.direction,j,x=K({xs:!0,sm:!0,md:!0,lg:!0,xl:!0,xxl:!0,xxxl:!0}),w=V();L(function(){j=_.subscribe(function(e){var t=r.gutter||0;(!Array.isArray(t)&&y(t)==="object"||Array.isArray(t)&&(y(t[0])==="object"||y(t[1])==="object"))&&(x.value=e)})}),T(function(){_.unsubscribe(j)});var S=l(function(){var e=[0,0],t=r.gutter,n=t===void 0?0:t,s=Array.isArray(n)?n:[n,0];return s.forEach(function(i,b){if(y(i)==="object")for(var a=0;a<A.length;a++){var p=A[a];if(x.value[p]&&i[p]!==void 0){e[b]=i[p];break}}else e[b]=i||0}),e});W({gutter:S,supportFlexGap:w,wrap:l(function(){return r.wrap})});var R=l(function(){var e;return $(d.value,(e={},o(e,"".concat(d.value,"-no-wrap"),r.wrap===!1),o(e,"".concat(d.value,"-").concat(r.justify),r.justify),o(e,"".concat(d.value,"-").concat(r.align),r.align),o(e,"".concat(d.value,"-rtl"),h.value==="rtl"),e))}),O=l(function(){var e=S.value,t={},n=e[0]>0?"".concat(e[0]/-2,"px"):void 0,s=e[1]>0?"".concat(e[1]/-2,"px"):void 0;return n&&(t.marginLeft=n,t.marginRight=n),w.value?t.rowGap="".concat(e[1],"px"):s&&(t.marginTop=s,t.marginBottom=s),t});return function(){var e;return B("div",{class:R.value,style:O.value},[(e=g.default)===null||e===void 0?void 0:e.call(g)])}}});const Y=q;function H(c){return typeof c=="number"?"".concat(c," ").concat(c," auto"):/^\d+(\.\d+)?(px|em|rem|%)$/.test(c)?"0 0 ".concat(c):c}var J=function(){return{span:[String,Number],order:[String,Number],offset:[String,Number],push:[String,Number],pull:[String,Number],xs:{type:[String,Number,Object],default:void 0},sm:{type:[String,Number,Object],default:void 0},md:{type:[String,Number,Object],default:void 0},lg:{type:[String,Number,Object],default:void 0},xl:{type:[String,Number,Object],default:void 0},xxl:{type:[String,Number,Object],default:void 0},xxxl:{type:[String,Number,Object],default:void 0},prefixCls:String,flex:[String,Number]}};const Z=F({compatConfig:{MODE:3},name:"ACol",props:J(),setup:function(r,N){var g=N.slots,v=k(),d=v.gutter,h=v.supportFlexGap,j=v.wrap,x=I("col",r),w=x.prefixCls,S=x.direction,R=l(function(){var e,t=r.span,n=r.order,s=r.offset,i=r.push,b=r.pull,a=w.value,p={};return["xs","sm","md","lg","xl","xxl","xxxl"].forEach(function(m){var f,u={},C=r[m];typeof C=="number"?u.span=C:y(C)==="object"&&(u=C||{}),p=G(G({},p),{},(f={},o(f,"".concat(a,"-").concat(m,"-").concat(u.span),u.span!==void 0),o(f,"".concat(a,"-").concat(m,"-order-").concat(u.order),u.order||u.order===0),o(f,"".concat(a,"-").concat(m,"-offset-").concat(u.offset),u.offset||u.offset===0),o(f,"".concat(a,"-").concat(m,"-push-").concat(u.push),u.push||u.push===0),o(f,"".concat(a,"-").concat(m,"-pull-").concat(u.pull),u.pull||u.pull===0),o(f,"".concat(a,"-rtl"),S.value==="rtl"),f))}),$(a,(e={},o(e,"".concat(a,"-").concat(t),t!==void 0),o(e,"".concat(a,"-order-").concat(n),n),o(e,"".concat(a,"-offset-").concat(s),s),o(e,"".concat(a,"-push-").concat(i),i),o(e,"".concat(a,"-pull-").concat(b),b),e),p)}),O=l(function(){var e=r.flex,t=d.value,n={};if(t&&t[0]>0){var s="".concat(t[0]/2,"px");n.paddingLeft=s,n.paddingRight=s}if(t&&t[1]>0&&!h.value){var i="".concat(t[1]/2,"px");n.paddingTop=i,n.paddingBottom=i}return e&&(n.flex=H(e),j.value===!1&&!n.minWidth&&(n.minWidth=0)),n});return function(){var e;return B("div",{class:R.value,style:O.value},[(e=g.default)===null||e===void 0?void 0:e.call(g)])}}});export{Z as C,Y as R};

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

@ -0,0 +1 @@
.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}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
import{r as F,o as P,cx as K,av as L,G as i,ax as T,ay as I,d as $,u as B,cy as _,b as y,bk as V,cz as A,an as E,h as c,c as M,a as G}from"./index-632e7cf6.js";const W=function(){var o=F(!1);return P(function(){o.value=K()}),o};var D=Symbol("rowContextKey"),k=function(r){T(D,r)},U=function(){return L(D,{gutter:i(function(){}),wrap:i(function(){}),supportFlexGap:i(function(){})})};I("top","middle","bottom","stretch");I("start","end","center","space-around","space-between");var q=function(){return{align:String,justify:String,prefixCls:String,gutter:{type:[Number,Array,Object],default:0},wrap:{type:Boolean,default:void 0}}},z=$({compatConfig:{MODE:3},name:"ARow",props:q(),setup:function(r,N){var m=N.slots,v=B("row",r),d=v.prefixCls,h=v.direction,j,x=F({xs:!0,sm:!0,md:!0,lg:!0,xl:!0,xxl:!0,xxxl:!0}),S=W();P(function(){j=_.subscribe(function(e){var t=r.gutter||0;(!Array.isArray(t)&&y(t)==="object"||Array.isArray(t)&&(y(t[0])==="object"||y(t[1])==="object"))&&(x.value=e)})}),V(function(){_.unsubscribe(j)});var w=i(function(){var e=[0,0],t=r.gutter,n=t===void 0?0:t,s=Array.isArray(n)?n:[n,0];return s.forEach(function(l,b){if(y(l)==="object")for(var a=0;a<A.length;a++){var p=A[a];if(x.value[p]&&l[p]!==void 0){e[b]=l[p];break}}else e[b]=l||0}),e});k({gutter:w,supportFlexGap:S,wrap:i(function(){return r.wrap})});var R=i(function(){var e;return E(d.value,(e={},c(e,"".concat(d.value,"-no-wrap"),r.wrap===!1),c(e,"".concat(d.value,"-").concat(r.justify),r.justify),c(e,"".concat(d.value,"-").concat(r.align),r.align),c(e,"".concat(d.value,"-rtl"),h.value==="rtl"),e))}),O=i(function(){var e=w.value,t={},n=e[0]>0?"".concat(e[0]/-2,"px"):void 0,s=e[1]>0?"".concat(e[1]/-2,"px"):void 0;return n&&(t.marginLeft=n,t.marginRight=n),S.value?t.rowGap="".concat(e[1],"px"):s&&(t.marginTop=s,t.marginBottom=s),t});return function(){var e;return M("div",{class:R.value,style:O.value},[(e=m.default)===null||e===void 0?void 0:e.call(m)])}}});const X=z;function H(o){return typeof o=="number"?"".concat(o," ").concat(o," auto"):/^\d+(\.\d+)?(px|em|rem|%)$/.test(o)?"0 0 ".concat(o):o}var J=function(){return{span:[String,Number],order:[String,Number],offset:[String,Number],push:[String,Number],pull:[String,Number],xs:{type:[String,Number,Object],default:void 0},sm:{type:[String,Number,Object],default:void 0},md:{type:[String,Number,Object],default:void 0},lg:{type:[String,Number,Object],default:void 0},xl:{type:[String,Number,Object],default:void 0},xxl:{type:[String,Number,Object],default:void 0},xxxl:{type:[String,Number,Object],default:void 0},prefixCls:String,flex:[String,Number]}};const Y=$({compatConfig:{MODE:3},name:"ACol",props:J(),setup:function(r,N){var m=N.slots,v=U(),d=v.gutter,h=v.supportFlexGap,j=v.wrap,x=B("col",r),S=x.prefixCls,w=x.direction,R=i(function(){var e,t=r.span,n=r.order,s=r.offset,l=r.push,b=r.pull,a=S.value,p={};return["xs","sm","md","lg","xl","xxl","xxxl"].forEach(function(g){var f,u={},C=r[g];typeof C=="number"?u.span=C:y(C)==="object"&&(u=C||{}),p=G(G({},p),{},(f={},c(f,"".concat(a,"-").concat(g,"-").concat(u.span),u.span!==void 0),c(f,"".concat(a,"-").concat(g,"-order-").concat(u.order),u.order||u.order===0),c(f,"".concat(a,"-").concat(g,"-offset-").concat(u.offset),u.offset||u.offset===0),c(f,"".concat(a,"-").concat(g,"-push-").concat(u.push),u.push||u.push===0),c(f,"".concat(a,"-").concat(g,"-pull-").concat(u.pull),u.pull||u.pull===0),c(f,"".concat(a,"-rtl"),w.value==="rtl"),f))}),E(a,(e={},c(e,"".concat(a,"-").concat(t),t!==void 0),c(e,"".concat(a,"-order-").concat(n),n),c(e,"".concat(a,"-offset-").concat(s),s),c(e,"".concat(a,"-push-").concat(l),l),c(e,"".concat(a,"-pull-").concat(b),b),e),p)}),O=i(function(){var e=r.flex,t=d.value,n={};if(t&&t[0]>0){var s="".concat(t[0]/2,"px");n.paddingLeft=s,n.paddingRight=s}if(t&&t[1]>0&&!h.value){var l="".concat(t[1]/2,"px");n.paddingTop=l,n.paddingBottom=l}return e&&(n.flex=H(e),j.value===!1&&!n.minWidth&&(n.minWidth=0)),n});return function(){var e;return M("div",{class:R.value,style:O.value},[(e=m.default)===null||e===void 0?void 0:e.call(m)])}}});export{Y as C,X as R};

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{d as x,a1 as $,aK as g,cS as b,r as w,U as p,V as i,W as a,c as r,a3 as d,X as u,Y as n,Z as B,a8 as I,a4 as m,y as V,z as _,B as v,aj as W,ak as D,cT as N,a0 as R}from"./index-632e7cf6.js";/* empty css */const F={class:"container"},K={class:"actions"},L={class:"uni-desc"},T={class:"snapshot"},U=x({__name:"index",props:{tabIdx:{},paneIdx:{},id:{},paneKey:{}},setup(j){const h=$(),t=g(),f=e=>{h.tabList=V(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=W,l=D;return p(),i("div",F,[a("div",K,[r(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"]),r(l,{type:"primary",onClick:y},{default:d(()=>[u(n(e.$t("saveWorkspaceSnapshot")),1)]),_:1})]),a("p",L,n(e.$t("WorkspaceSnapshotDesc")),1),a("ul",T,[(p(!0),i(B,null,I(m(t).snapshots,s=>(p(),i("li",{key:s.id},[a("div",null,[a("span",null,n(s.name),1)]),a("div",null,[r(l,{onClick:S=>f(s)},{default:d(()=>[u(n(e.$t("restore")),1)]),_:2},1032,["onClick"]),r(l,{onClick:S=>m(k)(s)},{default:d(()=>[u(n(e.$t("remove")),1)]),_:2},1032,["onClick"])])]))),128))])])}}});const A=R(U,[["__scopeId","data-v-2c44013c"]]);export{A as default};
import{d as x,a1 as $,aK as g,cT as b,r as w,U as p,V as i,W as a,c as r,a3 as d,X as u,Y as n,Z as B,a8 as I,a4 as m,y as V,z as _,B as v,aj as W,ak as D,cU as N,a0 as R}from"./index-d6594e8e.js";/* empty css */const U={class:"container"},F={class:"actions"},K={class:"uni-desc"},L={class:"snapshot"},T=x({__name:"index",props:{tabIdx:{},paneIdx:{},id:{},paneKey:{}},setup(j){const h=$(),t=g(),f=e=>{h.tabList=V(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=W,l=D;return p(),i("div",U,[a("div",F,[r(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"]),r(l,{type:"primary",onClick:y},{default:d(()=>[u(n(e.$t("saveWorkspaceSnapshot")),1)]),_:1})]),a("p",K,n(e.$t("WorkspaceSnapshotDesc")),1),a("ul",L,[(p(!0),i(B,null,I(m(t).snapshots,s=>(p(),i("li",{key:s.id},[a("div",null,[a("span",null,n(s.name),1)]),a("div",null,[r(l,{onClick:S=>f(s)},{default:d(()=>[u(n(e.$t("restore")),1)]),_:2},1032,["onClick"]),r(l,{onClick:S=>m(k)(s)},{default:d(()=>[u(n(e.$t("remove")),1)]),_:2},1032,["onClick"])])]))),128))])])}}});const A=R(T,[["__scopeId","data-v-e55e3025"]]);export{A as default};

View File

@ -1 +0,0 @@
.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

@ -1 +1 @@
import{cw as j,ay as z,d as K,j as U,du as $,w as g,r as b,G as S,m as A,u as D,o as E,az as G,h as d,c as s,a as C,aw as H,bf as L,g as _,dv as W,P as u,dw as x}from"./index-632e7cf6.js";var R=z("small","default"),q=function(){return{id:String,prefixCls:String,size:u.oneOf(R),disabled:{type:Boolean,default:void 0},checkedChildren:u.any,unCheckedChildren:u.any,tabindex:u.oneOfType([u.string,u.number]),autofocus:{type:Boolean,default:void 0},loading:{type:Boolean,default:void 0},checked:u.oneOfType([u.string,u.number,u.looseBool]),checkedValue:u.oneOfType([u.string,u.number,u.looseBool]).def(!0),unCheckedValue:u.oneOfType([u.string,u.number,u.looseBool]).def(!1),onChange:{type:Function},onClick:{type:Function},onKeydown:{type:Function},onMouseup:{type:Function},"onUpdate:checked":{type:Function},onBlur:Function,onFocus:Function}},J=K({compatConfig:{MODE:3},name:"ASwitch",__ANT_SWITCH:!0,inheritAttrs:!1,props:q(),slots:["checkedChildren","unCheckedChildren"],setup:function(n,r){var o=r.attrs,y=r.slots,B=r.expose,l=r.emit,m=U();$(function(){g(!("defaultChecked"in o),"Switch","'defaultChecked' is deprecated, please use 'v-model:checked'"),g(!("value"in o),"Switch","`value` is not validate prop, do you mean `checked`?")});var h=b(n.checked!==void 0?n.checked:o.defaultChecked),f=S(function(){return h.value===n.checkedValue});A(function(){return n.checked},function(){h.value=n.checked});var v=D("switch",n),c=v.prefixCls,F=v.direction,T=v.size,i=b(),w=function(){var e;(e=i.value)===null||e===void 0||e.focus()},V=function(){var e;(e=i.value)===null||e===void 0||e.blur()};B({focus:w,blur:V}),E(function(){G(function(){n.autofocus&&!n.disabled&&i.value.focus()})});var k=function(e,t){n.disabled||(l("update:checked",e),l("change",e,t),m.onFieldChange())},I=function(e){l("blur",e)},N=function(e){w();var t=f.value?n.unCheckedValue:n.checkedValue;k(t,e),l("click",t,e)},M=function(e){e.keyCode===x.LEFT?k(n.unCheckedValue,e):e.keyCode===x.RIGHT&&k(n.checkedValue,e),l("keydown",e)},O=function(e){var t;(t=i.value)===null||t===void 0||t.blur(),l("mouseup",e)},P=S(function(){var a;return a={},d(a,"".concat(c.value,"-small"),T.value==="small"),d(a,"".concat(c.value,"-loading"),n.loading),d(a,"".concat(c.value,"-checked"),f.value),d(a,"".concat(c.value,"-disabled"),n.disabled),d(a,c.value,!0),d(a,"".concat(c.value,"-rtl"),F.value==="rtl"),a});return function(){var a;return s(W,{insertExtraNode:!0},{default:function(){return[s("button",C(C(C({},H(n,["prefixCls","checkedChildren","unCheckedChildren","checked","autofocus","checkedValue","unCheckedValue","id","onChange","onUpdate:checked"])),o),{},{id:(a=n.id)!==null&&a!==void 0?a:m.id.value,onKeydown:M,onClick:N,onBlur:I,onMouseup:O,type:"button",role:"switch","aria-checked":h.value,disabled:n.disabled||n.loading,class:[o.class,P.value],ref:i}),[s("div",{class:"".concat(c.value,"-handle")},[n.loading?s(L,{class:"".concat(c.value,"-loading-icon")},null):null]),s("span",{class:"".concat(c.value,"-inner")},[f.value?_(y,n,"checkedChildren"):_(y,n,"unCheckedChildren")])])]}})}}});const X=j(J);export{X as _};
import{cw as j,ay as z,d as K,j as U,dv as $,w as g,r as b,G as S,m as A,u as D,o as E,az as G,h as d,c as s,a as C,aw as H,bf as L,g as x,dw as W,P as c,dx as _}from"./index-d6594e8e.js";var R=z("small","default"),q=function(){return{id:String,prefixCls:String,size:c.oneOf(R),disabled:{type:Boolean,default:void 0},checkedChildren:c.any,unCheckedChildren:c.any,tabindex:c.oneOfType([c.string,c.number]),autofocus:{type:Boolean,default:void 0},loading:{type:Boolean,default:void 0},checked:c.oneOfType([c.string,c.number,c.looseBool]),checkedValue:c.oneOfType([c.string,c.number,c.looseBool]).def(!0),unCheckedValue:c.oneOfType([c.string,c.number,c.looseBool]).def(!1),onChange:{type:Function},onClick:{type:Function},onKeydown:{type:Function},onMouseup:{type:Function},"onUpdate:checked":{type:Function},onBlur:Function,onFocus:Function}},J=K({compatConfig:{MODE:3},name:"ASwitch",__ANT_SWITCH:!0,inheritAttrs:!1,props:q(),slots:["checkedChildren","unCheckedChildren"],setup:function(n,r){var o=r.attrs,y=r.slots,B=r.expose,l=r.emit,m=U();$(function(){g(!("defaultChecked"in o),"Switch","'defaultChecked' is deprecated, please use 'v-model:checked'"),g(!("value"in o),"Switch","`value` is not validate prop, do you mean `checked`?")});var h=b(n.checked!==void 0?n.checked:o.defaultChecked),f=S(function(){return h.value===n.checkedValue});A(function(){return n.checked},function(){h.value=n.checked});var v=D("switch",n),u=v.prefixCls,F=v.direction,T=v.size,i=b(),w=function(){var e;(e=i.value)===null||e===void 0||e.focus()},V=function(){var e;(e=i.value)===null||e===void 0||e.blur()};B({focus:w,blur:V}),E(function(){G(function(){n.autofocus&&!n.disabled&&i.value.focus()})});var k=function(e,t){n.disabled||(l("update:checked",e),l("change",e,t),m.onFieldChange())},I=function(e){l("blur",e)},N=function(e){w();var t=f.value?n.unCheckedValue:n.checkedValue;k(t,e),l("click",t,e)},M=function(e){e.keyCode===_.LEFT?k(n.unCheckedValue,e):e.keyCode===_.RIGHT&&k(n.checkedValue,e),l("keydown",e)},O=function(e){var t;(t=i.value)===null||t===void 0||t.blur(),l("mouseup",e)},P=S(function(){var a;return a={},d(a,"".concat(u.value,"-small"),T.value==="small"),d(a,"".concat(u.value,"-loading"),n.loading),d(a,"".concat(u.value,"-checked"),f.value),d(a,"".concat(u.value,"-disabled"),n.disabled),d(a,u.value,!0),d(a,"".concat(u.value,"-rtl"),F.value==="rtl"),a});return function(){var a;return s(W,{insertExtraNode:!0},{default:function(){return[s("button",C(C(C({},H(n,["prefixCls","checkedChildren","unCheckedChildren","checked","autofocus","checkedValue","unCheckedValue","id","onChange","onUpdate:checked"])),o),{},{id:(a=n.id)!==null&&a!==void 0?a:m.id.value,onKeydown:M,onClick:N,onBlur:I,onMouseup:O,type:"button",role:"switch","aria-checked":h.value,disabled:n.disabled||n.loading,class:[o.class,P.value],ref:i}),[s("div",{class:"".concat(u.value,"-handle")},[n.loading?s(L,{class:"".concat(u.value,"-loading-icon")},null):null]),s("span",{class:"".concat(u.value,"-inner")},[f.value?x(y,n,"checkedChildren"):x(y,n,"unCheckedChildren")])])]}})}}});const X=j(J);export{X as _};

View File

@ -0,0 +1 @@
import{cl as e,cm as i,cn as r,co as a,b1 as n}from"./index-d6594e8e.js";function c(s,t){return e(i(s,t,r),s+"")}function b(s){return a(s)&&n(s)}export{c as b,b as i};

View File

@ -1 +0,0 @@
import{cl as e,cm as i,cn as r,co as a,b1 as n}from"./index-632e7cf6.js";function c(s,t){return e(i(s,t,r),s+"")}function b(s){return a(s)&&n(s)}export{c as b,b as i};

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-55978858]{display:flex}.num-input .slide[data-v-55978858]{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-bd954eda]{display:flex}.num-input .slide[data-v-bd954eda]{flex:1;min-width:128px;max-width:256px;margin-left:8px}

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-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}
.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}

View File

@ -1 +1 @@
import{d as Z,a1 as ee,r as F,J as te,K as le,o as ie,U as v,V as N,c as i,a4 as e,W as g,a3 as n,X as k,Y as u,a5 as R,L as se,a6 as ae,af as oe,ag as $,$ as A,a2 as ne,z as w,B as re,cU as ce,cV as de,ak as ue,ai as me,T as fe,a0 as pe}from"./index-632e7cf6.js";import{u as ve,c as ge,a as ke,F as we,d as he}from"./FileItem-70868dea.js";import{a as Ce,b as Se,c as _e,M as Ie,o as z,L as ye,R as xe,f as be}from"./MultiSelectKeep-a8fa9049.js";import"./numInput.vue_vue_type_style_index_0_scoped_55978858_lang-221425da.js";/* empty css */import"./index-a2a27adc.js";import"./_isIterateeCall-582f579a.js";import"./index-9d95a206.js";import"./index-a44b2ffa.js";import"./shortcut-5c51118c.js";import"./Checkbox-2af1c4fc.js";import"./index-1bd869eb.js";const Ve={class:"refresh-button"},Me={class:"hint"},Te={key:0,class:"preview-switch"},Fe=Z({__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:re("randomImageSettingNotification"),duration:6,key:"randomImageSetting"}),h.value=!0)},f=async()=>{try{m.value=!0;const s=await ce();s.length===0&&w.warn("No data, please generate index in image search page first"),l.value=s}finally{m.value=!1,_()}},C=()=>{if(l.value.length===0){w.warn("没有图片可以浏览");return}z(l.value,o.value||0)};ie(()=>{f(),setTimeout(()=>{P()},2e3)});const{stackViewEl:U,multiSelectedIdxs:p,stack:K,scroller:L}=ve({images:l}).toRefs(),{onClearAllSelected:D,onSelectAll:E,onReverseSelect:G}=ge();Ce();const{itemSize:S,gridItems:O,cellWidth:W,onScroll:_}=ke(),{showGenInfo:c,imageGenInfo:I,q,onContextMenuClick:H,onFileItemClick:J}=Se({openNext:de}),{previewIdx:o,previewing:y,onPreviewVisibleChange:Q,previewImgMove:x,canPreview:b}=_e(),V=async(s,t,d)=>{K.value=[{curr:"",files:l.value}],await H(s,t,d)};return(s,t)=>{var M;const d=ue,X=me,Y=fe;return v(),N("div",{class:"container",ref_key:"stackViewEl",ref:U},[i(Ie,{show:!!e(p).length||e(B).keepMultiSelect,onClearAllSelected:e(D),onSelectAll:e(E),onReverseSelect:e(G)},null,8,["show","onClearAllSelected","onSelectAll","onReverseSelect"]),g("div",Ve,[i(d,{onClick:f,onTouchstart:R(f,["prevent"]),type:"primary",loading:m.value,shape:"round"},{default:n(()=>[k(u(s.$t("shuffle")),1)]),_:1},8,["onTouchstart","loading"]),i(d,{onClick:C,onTouchstart:R(C,["prevent"]),type:"default",disabled:!((M=l.value)!=null&&M.length),shape:"round"},{default:n(()=>[k(u(s.$t("tiktokView")),1)]),_:1},8,["onTouchstart","disabled"])]),i(Y,{visible:e(c),"onUpdate:visible":t[1]||(t[1]=a=>ae(c)?c.value=a:null),width:"70vw","mask-closable":"",onOk:t[2]||(t[2]=a=>c.value=!1)},{cancelText:n(()=>[]),default:n(()=>[i(X,{active:"",loading:!e(q).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]=a=>e(se)(e(I)))},[g("div",Me,u(s.$t("doubleClickToCopy")),1),k(" "+u(e(I)),1)],32)]),_:1},8,["loading"])]),_:1},8,["visible"]),i(e(he),{ref_key:"scroller",ref:L,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:a,index:T})=>[i(we,{idx:T,file:a,"cell-width":e(W),"full-screen-preview-image-url":e(r)[e(o)]?e(oe)(e(r)[e(o)]):"",onContextMenuClick:V,onPreviewVisibleChange:e(Q),"is-selected-mutil-files":e(p).length>1,selected:e(p).includes(T),onFileItemClick:e(J),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,[i(e(ye),{onClick:t[3]||(t[3]=a=>e(x)("prev")),class:$({disable:!e(b)("prev")})},null,8,["class"]),i(e(xe),{onClick:t[4]||(t[4]=a=>e(x)("next")),class:$({disable:!e(b)("next")})},null,8,["class"])])):A("",!0),e(y)&&e(r)&&e(r)[e(o)]?(v(),ne(be,{key:1,file:e(r)[e(o)],idx:e(o),onContextMenuClick:V},null,8,["file","idx"])):A("",!0)],512)}}});const We=pe(Fe,[["__scopeId","data-v-49082269"]]);export{We as default};
import{d as Z,a1 as ee,r as F,J as te,K as le,o as ie,U as v,V as N,c as i,a4 as e,W as g,a3 as n,X as k,Y as u,a5 as R,L as se,a6 as ae,af as oe,ag as $,$ as A,a2 as ne,z as w,B as re,cV as ce,cW as de,ak as ue,ai as me,T as fe,a0 as pe}from"./index-d6594e8e.js";import{u as ve,c as ge,a as ke,F as we,d as he}from"./FileItem-2eb00024.js";import{a as Ce,b as Se,c as _e,M as Ie,o as z,L as ye,R as xe,f as be}from"./MultiSelectKeep-ba3817a4.js";import"./numInput.vue_vue_type_style_index_0_scoped_bd954eda_lang-c8c8013d.js";/* empty css */import"./index-cb6a3d31.js";import"./_isIterateeCall-bfd79848.js";import"./index-30b8f89a.js";import"./index-9b184148.js";import"./shortcut-0468cdd7.js";import"./Checkbox-59427a1c.js";import"./index-e620c28b.js";const Ve={class:"refresh-button"},Me={class:"hint"},Te={key:0,class:"preview-switch"},Fe=Z({__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:re("randomImageSettingNotification"),duration:6,key:"randomImageSetting"}),h.value=!0)},f=async()=>{try{m.value=!0;const s=await ce();s.length===0&&w.warn("No data, please generate index in image search page first"),l.value=s}finally{m.value=!1,_()}},C=()=>{if(l.value.length===0){w.warn("没有图片可以浏览");return}z(l.value,o.value||0)};ie(()=>{f(),setTimeout(()=>{P()},2e3)});const{stackViewEl:K,multiSelectedIdxs:p,stack:L,scroller:U}=ve({images:l}).toRefs(),{onClearAllSelected:W,onSelectAll:D,onReverseSelect:E}=ge();Ce();const{itemSize:S,gridItems:G,cellWidth:O,onScroll:_}=ke(),{showGenInfo:c,imageGenInfo:I,q,onContextMenuClick:H,onFileItemClick:J}=Se({openNext:de}),{previewIdx:o,previewing:y,onPreviewVisibleChange:Q,previewImgMove:x,canPreview:b}=_e(),V=async(s,t,d)=>{L.value=[{curr:"",files:l.value}],await H(s,t,d)};return(s,t)=>{var M;const d=ue,X=me,Y=fe;return v(),N("div",{class:"container",ref_key:"stackViewEl",ref:K},[i(Ie,{show:!!e(p).length||e(B).keepMultiSelect,onClearAllSelected:e(W),onSelectAll:e(D),onReverseSelect:e(E)},null,8,["show","onClearAllSelected","onSelectAll","onReverseSelect"]),g("div",Ve,[i(d,{onClick:f,onTouchstart:R(f,["prevent"]),type:"primary",loading:m.value,shape:"round"},{default:n(()=>[k(u(s.$t("shuffle")),1)]),_:1},8,["onTouchstart","loading"]),i(d,{onClick:C,onTouchstart:R(C,["prevent"]),type:"default",disabled:!((M=l.value)!=null&&M.length),shape:"round"},{default:n(()=>[k(u(s.$t("tiktokView")),1)]),_:1},8,["onTouchstart","disabled"])]),i(Y,{visible:e(c),"onUpdate:visible":t[1]||(t[1]=a=>ae(c)?c.value=a:null),width:"70vw","mask-closable":"",onOk:t[2]||(t[2]=a=>c.value=!1)},{cancelText:n(()=>[]),default:n(()=>[i(X,{active:"",loading:!e(q).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]=a=>e(se)(e(I)))},[g("div",Me,u(s.$t("doubleClickToCopy")),1),k(" "+u(e(I)),1)],32)]),_:1},8,["loading"])]),_:1},8,["visible"]),i(e(he),{ref_key:"scroller",ref:U,class:"file-list",items:l.value.slice(),"item-size":e(S).first,"key-field":"fullpath","item-secondary-size":e(S).second,gridItems:e(G),onScroll:e(_)},{default:n(({item:a,index:T})=>[i(we,{idx:T,file:a,"cell-width":e(O),"full-screen-preview-image-url":e(r)[e(o)]?e(oe)(e(r)[e(o)]):"",onContextMenuClick:V,onPreviewVisibleChange:e(Q),"is-selected-mutil-files":e(p).length>1,selected:e(p).includes(T),onFileItemClick:e(J),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,[i(e(ye),{onClick:t[3]||(t[3]=a=>e(x)("prev")),class:$({disable:!e(b)("prev")})},null,8,["class"]),i(e(xe),{onClick:t[4]||(t[4]=a=>e(x)("next")),class:$({disable:!e(b)("next")})},null,8,["class"])])):A("",!0),e(y)&&e(r)&&e(r)[e(o)]?(v(),ne(be,{key:1,file:e(r)[e(o)],idx:e(o),onContextMenuClick:V},null,8,["file","idx"])):A("",!0)],512)}}});const Oe=pe(Fe,[["__scopeId","data-v-e1531e89"]]);export{Oe as default};

View File

@ -1 +1 @@
import{R as y,C as v}from"./index-ab5ead62.js";import{cw as f,c as d,A as w,d as P,U as o,V as c,W as r,Z as S,a8 as V,aG as O,a3 as R,X as u,Y as p,a4 as b,ak as $,a0 as x,R as H,J as _,K as m}from"./index-632e7cf6.js";const A=f(y),E=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 C=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){N(t,i,s[i])})}return t}function N(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(w,h({},n,{icon:C}),null)};l.displayName="PushpinFilled";l.inheritAttrs=!1;const z=l,F={class:"record-container"},k={style:{flex:"1"}},I={class:"rec-actions"},B=["onClick"],J=P({__name:"HistoryRecord",props:{records:{}},emits:["reuseRecord"],setup(t){return(e,s)=>{const n=$;return o(),c("div",null,[r("ul",F,[(o(!0),c(S,null,V(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:g=>e.$emit("reuseRecord",i),type:"primary"},{default:R(()=>[u(p(e.$t("restore")),1)]),_:2},1032,["onClick"]),r("div",{class:"pin",onClick:g=>e.records.switchPin(i)},[d(b(z)),u(" "+p(e.records.isPinned(i)?e.$t("unpin"):e.$t("pin")),1)],8,B)])]))),128))])])}}});const q=x(J,[["__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: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 G=_(`${m}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=_(`${m}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{q as H,E as _,A as a,G as f,M as t};
import{R as y,C as v}from"./index-68461333.js";import{cw as f,c as d,A as w,d as P,U as a,V as c,W as r,Z as S,a8 as V,aG as O,a3 as R,X as u,Y as p,a4 as b,ak as $,a0 as x,R as H,J as _,K as m}from"./index-d6594e8e.js";const A=f(y),E=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 C=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){N(t,i,s[i])})}return t}function N(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(w,h({},n,{icon:C}),null)};l.displayName="PushpinFilled";l.inheritAttrs=!1;const z=l,F={class:"record-container"},k={style:{flex:"1"}},I={class:"rec-actions"},B=["onClick"],J=P({__name:"HistoryRecord",props:{records:{}},emits:["reuseRecord"],setup(t){return(e,s)=>{const n=$;return a(),c("div",null,[r("ul",F,[(a(!0),c(S,null,V(e.records.getRecords(),i=>(a(),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:g=>e.$emit("reuseRecord",i),type:"primary"},{default:R(()=>[u(p(e.$t("restore")),1)]),_:2},1032,["onClick"]),r("div",{class:"pin",onClick:g=>e.records.switchPin(i)},[d(b(z)),u(" "+p(e.records.isPinned(i)?e.$t("unpin"):e.$t("pin")),1)],8,B)])]))),128))])])}}});const q=x(J,[["__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 G=_(`${m}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}}),M=_(`${m}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{q as H,E as _,A as a,G as f,M as t};

View File

@ -0,0 +1 @@
[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 @@
[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)}

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 +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-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-317d328e]{margin-left:8px}.location-act .copy[data-v-317d328e]{margin-right:4px}@media (max-width: 768px){.location-act[data-v-317d328e]{display:flex;flex-direction:column}.location-act>*[data-v-317d328e],.location-act .copy[data-v-317d328e]{margin:2px}}.breadcrumb[data-v-317d328e]{display:flex;align-items:center}.breadcrumb>*[data-v-317d328e]{margin-right:4px}@media (max-width: 768px){.breadcrumb[data-v-317d328e]{width:100%}.breadcrumb .ant-breadcrumb>*[data-v-317d328e]{display:inline-block}}.container[data-v-317d328e]{background:var(--zp-secondary-background);height:var(--pane-max-height)}.location-bar[data-v-317d328e]{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-317d328e]{flex-direction:column}.location-bar[data-v-317d328e] ::-webkit-scrollbar{height:2px;background-color:var(--zp-secondary-variant-background)}.location-bar .actions[data-v-317d328e]{padding:4px 0;width:100%;overflow:auto;display:flex;align-items:center}.location-bar .actions>*[data-v-317d328e]{flex-shrink:0}}.location-bar .actions[data-v-317d328e]{display:flex;align-items:center;flex-shrink:0}.location-bar a.opt[data-v-317d328e]{margin-left:8px}.view[data-v-317d328e]{padding:8px;height:calc(100vh - 48px)}.view .file-list[data-v-317d328e]{list-style:none;padding:8px;height:100%;overflow:auto}.hint[data-v-317d328e]{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-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-8d4bd42a]{margin-left:8px}.location-act .copy[data-v-8d4bd42a]{margin-right:4px}@media (max-width: 768px){.location-act[data-v-8d4bd42a]{display:flex;flex-direction:column}.location-act>*[data-v-8d4bd42a],.location-act .copy[data-v-8d4bd42a]{margin:2px}}.breadcrumb[data-v-8d4bd42a]{display:flex;align-items:center}.breadcrumb>*[data-v-8d4bd42a]{margin-right:4px}@media (max-width: 768px){.breadcrumb[data-v-8d4bd42a]{width:100%}.breadcrumb .ant-breadcrumb>*[data-v-8d4bd42a]{display:inline-block}}.container[data-v-8d4bd42a]{background:var(--zp-secondary-background);height:var(--pane-max-height)}.location-bar[data-v-8d4bd42a]{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-8d4bd42a]{flex-direction:column}.location-bar[data-v-8d4bd42a] ::-webkit-scrollbar{height:2px;background-color:var(--zp-secondary-variant-background)}.location-bar .actions[data-v-8d4bd42a]{padding:4px 0;width:100%;overflow:auto;display:flex;align-items:center}.location-bar .actions>*[data-v-8d4bd42a]{flex-shrink:0}}.location-bar .actions[data-v-8d4bd42a]{display:flex;align-items:center;flex-shrink:0}.location-bar a.opt[data-v-8d4bd42a]{margin-left:8px}.view[data-v-8d4bd42a]{padding:8px;height:calc(100vh - 48px)}.view .file-list[data-v-8d4bd42a]{list-style:none;padding:8px;height:100%;overflow:auto}.hint[data-v-8d4bd42a]{padding:4px;border:4px;background:var(--zp-secondary-background);border:1px solid var(--zp-border)}

View File

@ -1 +1 @@
import{u as G,g as d}from"./FileItem-70868dea.js";import{r as b,t as j,ct as m,cu as y,cv as D}from"./index-632e7cf6.js";const r=new Map,A=()=>{const{useEventListen:k,sortedFiles:f,getViewableAreaFiles:w}=G().toRefs(),c=b(d.defaultChangeIndchecked),u=b(d.defaultSeedChangeChecked),g=async()=>{if(await j(100),!c.value)return;const o=w.value().filter(e=>m(e.fullpath)&&!e.gen_info_obj);if(!o.length)return;const t=await y(o.map(e=>e.fullpath).filter(e=>!r.has(e)));o.forEach(e=>{const i=t[e.fullpath]||r.get(e.fullpath)||"";r.set(e.fullpath,i),e.gen_info_obj=D(i),e.gen_info_raw=i})};k.value("viewableAreaFilesChange",g);const F=o=>{const t=f.value;return[o,u.value,t[o-1],t[o],t[o+1]]};function I(o,t,e,i){const a={diff:{},empty:!0,ownFile:"",otherFile:""};if(t+e<0||t+e>=f.value.length||f.value[t]==null||!("gen_info_obj"in f.value[t])||!("gen_info_obj"in f.value[t+e]))return a;const l=o,s=f.value[t+e].gen_info_obj;if(s==null)return a;const h=["hashes","resources"];a.diff={},a.ownFile=i.name,a.otherFile=f.value[t+e].name,a.empty=!1,u.value||h.push("seed");for(const n in l)if(!h.includes(n)){if(!(n in s)){a.diff[n]="+";continue}if(l[n]!=s[n])if(n.includes("rompt")&&l[n]!=""&&s[n]!=""){const p=l[n].split(","),C=s[n].split(",");let _=0;for(const v in p)p[v]!=C[v]&&_++;a.diff[n]=_}else a.diff[n]=[l[n],s[n]]}return a}return{getGenDiff:I,changeIndchecked:c,seedChangeChecked:u,getRawGenParams:()=>g(),getGenDiffWatchDep:F}};export{A as u};
import{u as G,g as d}from"./FileItem-2eb00024.js";import{r as b,t as j,ct as m,cu as y,cv as D}from"./index-d6594e8e.js";const r=new Map,A=()=>{const{useEventListen:k,sortedFiles:f,getViewableAreaFiles:w}=G().toRefs(),c=b(d.defaultChangeIndchecked),u=b(d.defaultSeedChangeChecked),g=async()=>{if(await j(100),!c.value)return;const o=w.value().filter(e=>m(e.fullpath)&&!e.gen_info_obj);if(!o.length)return;const t=await y(o.map(e=>e.fullpath).filter(e=>!r.has(e)));o.forEach(e=>{const i=t[e.fullpath]||r.get(e.fullpath)||"";r.set(e.fullpath,i),e.gen_info_obj=D(i),e.gen_info_raw=i})};k.value("viewableAreaFilesChange",g);const F=o=>{const t=f.value;return[o,u.value,t[o-1],t[o],t[o+1]]};function I(o,t,e,i){const a={diff:{},empty:!0,ownFile:"",otherFile:""};if(t+e<0||t+e>=f.value.length||f.value[t]==null||!("gen_info_obj"in f.value[t])||!("gen_info_obj"in f.value[t+e]))return a;const l=o,s=f.value[t+e].gen_info_obj;if(s==null)return a;const h=["hashes","resources"];a.diff={},a.ownFile=i.name,a.otherFile=f.value[t+e].name,a.empty=!1,u.value||h.push("seed");for(const n in l)if(!h.includes(n)){if(!(n in s)){a.diff[n]="+";continue}if(l[n]!=s[n])if(n.includes("rompt")&&l[n]!=""&&s[n]!=""){const p=l[n].split(","),C=s[n].split(",");let _=0;for(const v in p)p[v]!=C[v]&&_++;a.diff[n]=_}else a.diff[n]=[l[n],s[n]]}return a}return{getGenDiff:I,changeIndchecked:c,seedChangeChecked:u,getRawGenParams:()=>g(),getGenDiffWatchDep:F}};export{A as u};

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-632e7cf6.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-d385cc4f.css">
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-d6594e8e.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-af514ea9.css">
</head>
<body>

View File

@ -310,56 +310,48 @@ export const searchIibOutputByPrompt = async (req: PromptSearchReq) => {
return resp.data as PromptSearchResp
}
// ===== Tag Relationship Graph =====
// ===== Hierarchical Tag Graph =====
export interface TagGraphReq {
folder_paths: string[]
model?: string
threshold?: number
min_cluster_size?: number
lang?: string
top_n_tags?: number
top_n_clusters?: number
weight_mode?: 'frequency' | 'tfidf' | 'hybrid'
alpha?: number
show_clusters?: boolean
detect_communities?: boolean
lang?: string
}
export interface TagGraphNode {
export interface LayerNode {
id: string
label: string
weight: number
image_count?: number
cluster_count?: number
size?: number
category: string
community?: number
size: number
metadata?: {
type: string
image_count?: number
cluster_count?: number
level?: number
}
}
export interface TagGraphLink {
export interface GraphLayer {
level: number
name: string
nodes: LayerNode[]
}
export interface GraphLink {
source: string
target: string
weight: number
image_count?: number
cluster_count?: number
}
export interface TagGraphResp {
nodes: TagGraphNode[]
links: TagGraphLink[]
communities?: Array<{
id: number
nodes: string[]
size: number
}>
layers: GraphLayer[]
links: GraphLink[]
stats: {
total_tags: number
selected_tags: number
total_clusters: number
selected_clusters: number
total_images: number
tag_links: number
cluster_links: number
total_tags: number
selected_tags: number
abstraction_layers: number
total_links: number
}
}

View File

@ -1,424 +1,346 @@
<template>
<div class="tag-graph-container">
<!-- Toolbar -->
<div class="toolbar">
<a-space>
<a-button type="primary" @click="loadGraph" :loading="loading">
{{ $t('refresh') }}
</a-button>
<a-divider type="vertical" />
<span class="label">Top Tags:</span>
<a-input-number v-model:value="topNTags" :min="10" :max="100" :step="10" style="width: 80px" />
<span class="label">Top Clusters:</span>
<a-input-number v-model:value="topNClusters" :min="5" :max="50" :step="5" style="width: 80px" />
<a-checkbox v-model:checked="showClusters">Show Clusters</a-checkbox>
<a-button @click="fitView" size="small">Fit View</a-button>
</a-space>
<div class="tag-hierarchy-graph">
<div v-if="loading" class="loading-container">
<a-spin size="large" />
<div class="loading-text">Generating hierarchical graph...</div>
</div>
<!-- Graph Canvas -->
<div ref="chartContainer" class="chart-container"></div>
<!-- Info Panel -->
<div v-if="selectedNode" class="info-panel">
<div class="info-header">
<span class="info-title">{{ selectedNode.label }}</span>
<a-button size="small" type="text" @click="selectedNode = null"></a-button>
</div>
<div class="info-body">
<div class="info-item">
<span class="info-label">Category:</span>
<span class="info-value">{{ selectedNode.category }}</span>
</div>
<div class="info-item" v-if="selectedNode.image_count">
<span class="info-label">Images:</span>
<span class="info-value">{{ selectedNode.image_count }}</span>
</div>
<div class="info-item" v-if="selectedNode.cluster_count">
<span class="info-label">Clusters:</span>
<span class="info-value">{{ selectedNode.cluster_count }}</span>
</div>
<div class="info-item" v-if="selectedNode.size">
<span class="info-label">Size:</span>
<span class="info-value">{{ selectedNode.size }}</span>
</div>
<div class="info-item" v-if="selectedNode.community !== undefined">
<span class="info-label">Community:</span>
<span class="info-value">{{ selectedNode.community }}</span>
</div>
</div>
<div class="info-actions">
<a-button size="small" type="primary" @click="searchByTag(selectedNode.label)">
Search Images
</a-button>
</div>
<div v-else-if="error" class="error-container">
<a-alert type="error" :message="error" show-icon />
</div>
<!-- Stats Panel -->
<div v-if="stats" class="stats-panel">
<div class="stat-item">
<div class="stat-value">{{ stats.selected_tags }}</div>
<div class="stat-label">Tags</div>
</div>
<div class="stat-item">
<div class="stat-value">{{ stats.selected_clusters }}</div>
<div class="stat-label">Clusters</div>
</div>
<div class="stat-item">
<div class="stat-value">{{ stats.total_images }}</div>
<div class="stat-label">Images</div>
<div v-else-if="graphData" class="graph-container">
<!-- Control Panel -->
<div class="control-panel">
<a-space>
<a-tag>Layers: {{ graphData.layers.length }}</a-tag>
<a-tag>Total Links: {{ graphData.stats.total_links }}</a-tag>
</a-space>
</div>
<!-- ECharts Container -->
<div ref="chartRef" class="chart-container"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import { getClusterTagGraph, type TagGraphReq, type TagGraphResp, type TagGraphNode } from '@/api/db'
import { ref, onMounted, watch, nextTick, onUnmounted } from 'vue'
import { getClusterTagGraph, type TagGraphReq, type TagGraphResp } from '@/api/db'
import { message } from 'ant-design-vue'
import * as echarts from 'echarts'
import type { ECharts } from 'echarts'
const props = defineProps<{
interface Props {
folders: string[]
threshold: number
minClusterSize: number
lang?: string
model?: string
}>()
}
const props = defineProps<Props>()
const emit = defineEmits<{
searchTag: [tag: string]
openCluster: [cluster: { title: string; paths: string[]; size: number }]
}>()
const loading = ref(false)
const chartContainer = ref<HTMLDivElement>()
const chart = ref<ECharts>()
const error = ref<string>('')
const graphData = ref<TagGraphResp | null>(null)
const selectedNode = ref<TagGraphNode | null>(null)
const stats = ref<TagGraphResp['stats'] | null>(null)
const chartRef = ref<HTMLDivElement>()
let chartInstance: echarts.ECharts | null = null
let _handleResize: (() => void) | null = null
// Parameters
const topNTags = ref(50)
const topNClusters = ref(20)
const showClusters = ref(true)
// Category colors
const categoryColors: Record<string, string> = {
character: '#5470c6',
style: '#91cc75',
scene: '#fac858',
object: '#ee6666',
cluster: '#73c0de',
other: '#9a60b4',
}
// Community colors (generated dynamically)
const communityColors: string[] = [
'#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de',
'#3ba272', '#fc8452', '#9a60b4', '#ea7ccc', '#5d7092'
// Layer colors (different for each layer)
const layerColors = [
'#4A90E2', // Layer 0: Clusters - Blue
'#7B68EE', // Layer 1: Tags - Purple
'#50C878', // Layer 2: Abstract-1 - Green
'#FF6B6B', // Layer 3: Abstract-2 - Red
]
const getCategoryColor = (category: string, community?: number): string => {
if (community !== undefined && community >= 0) {
return communityColors[community % communityColors.length]
}
return categoryColors[category] || categoryColors.other
const getLayerColor = (layer: number): string => {
return layerColors[layer % layerColors.length]
}
const loadGraph = async () => {
if (!props.folders.length) {
message.error('Please select folders first')
const fetchGraphData = async () => {
if (!props.folders || props.folders.length === 0) {
error.value = 'No folders selected'
return
}
loading.value = true
error.value = ''
try {
const req: TagGraphReq = {
folder_paths: props.folders,
model: props.model,
threshold: props.threshold,
min_cluster_size: props.minClusterSize,
lang: props.lang,
top_n_tags: topNTags.value,
top_n_clusters: topNClusters.value,
show_clusters: showClusters.value,
detect_communities: true,
weight_mode: 'hybrid',
alpha: 0.3,
top_n_tags: 50,
top_n_clusters: 20,
lang: props.lang || 'en',
}
const data = await getClusterTagGraph(req)
graphData.value = data
stats.value = data.stats
renderGraph(data)
message.success(`Graph loaded: ${data.stats.selected_tags} tags, ${data.stats.selected_clusters} clusters`)
} catch (e: any) {
message.error(e.message || 'Failed to load tag graph')
graphData.value = await getClusterTagGraph(req)
} catch (err: any) {
error.value = err.response?.data?.detail || err.message || 'Failed to load graph'
message.error(error.value)
} finally {
loading.value = false
}
}
const renderGraph = (data: TagGraphResp) => {
if (!chartContainer.value) return
if (!chart.value) {
chart.value = echarts.init(chartContainer.value)
const renderChart = () => {
if (!graphData.value || !chartRef.value) {
return
}
// Prepare data for ECharts
const nodes = data.nodes.map(node => ({
id: node.id,
name: node.label,
symbolSize: Math.sqrt(node.weight) * 0.5 + 10,
value: node.weight,
category: node.category,
itemStyle: {
color: getCategoryColor(node.category, node.community)
},
label: {
show: node.category === 'cluster' || node.weight > 100,
},
// Store original data
_data: node,
}))
// Dispose old instance
if (chartInstance) {
chartInstance.dispose()
}
const links = data.links.map(link => ({
chartInstance = echarts.init(chartRef.value)
const layers = graphData.value.layers
// Reverse to show abstract on left
const reversedLayers = [...layers].reverse()
// Build nodes with scattered positions for better visualization
const nodes: any[] = []
const layerSpacing = 1200 / (reversedLayers.length + 1) // Increased from 1000 to 1200
const horizontalRange = 150 // Horizontal scatter range
reversedLayers.forEach((layer, layerIdx) => {
const baseX = layerSpacing * (layerIdx + 1)
const numNodes = layer.nodes.length
const verticalSpacing = 800 / (numNodes + 1)
layer.nodes.forEach((node, nodeIdx) => {
const y = verticalSpacing * (nodeIdx + 1)
// Add horizontal offset for visual spacing
// Alternate left/right based on index, with some randomness based on node id
const hashOffset = node.id.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)
const horizontalOffset = ((hashOffset % horizontalRange) - horizontalRange / 2)
const x = baseX + horizontalOffset
// Calculate size with max 3x ratio
const minSize = 20
const maxSize = 60 // 3x ratio: 60 / 20 = 3
const normalizedSize = Math.sqrt(node.size) / 5
const clampedSize = Math.max(minSize, Math.min(maxSize, minSize + normalizedSize))
nodes.push({
id: node.id,
name: node.label,
x: x,
y: y,
fixed: true,
symbolSize: clampedSize,
value: node.size,
category: layer.level,
itemStyle: {
color: getLayerColor(layer.level)
},
label: {
show: true,
fontSize: 11,
color: '#fff',
formatter: (params: any) => {
const maxLen = 10
const name = params.name
return name.length > maxLen ? name.substring(0, maxLen) + '...' : name
}
},
metadata: node.metadata,
layerName: layer.name,
})
})
})
// Build links
const links = graphData.value.links.map(link => ({
source: link.source,
target: link.target,
value: link.weight,
lineStyle: {
width: Math.sqrt(link.weight) * 0.02 + 0.5,
opacity: 0.4,
},
width: Math.max(0.5, Math.min(2, link.weight / 100)),
opacity: 0.3,
curveness: 0.1
}
}))
// Build categories
const categories = Object.keys(categoryColors).map(name => ({ name }))
// Build categories for legend
const categories = reversedLayers.map(layer => ({
name: layer.name
}))
const option: echarts.EChartsOption = {
title: {
text: 'Tag Relationship Graph',
left: 'center',
top: 10,
},
tooltip: {
formatter: (params: any) => {
if (params.dataType === 'node') {
const node = params.data._data as TagGraphNode
let html = `<strong>${node.label}</strong><br/>`
html += `Category: ${node.category}<br/>`
if (node.image_count) html += `Images: ${node.image_count}<br/>`
if (node.cluster_count) html += `Clusters: ${node.cluster_count}<br/>`
if (node.size) html += `Size: ${node.size}<br/>`
if (node.community !== undefined) html += `Community: ${node.community}<br/>`
const node = params.data
let html = `<strong>${params.name}</strong><br/>`
html += `Layer: ${node.layerName}<br/>`
if (node.metadata?.image_count) {
html += `Images: ${node.metadata.image_count}`
}
return html
} else if (params.dataType === 'edge') {
return `Weight: ${params.value.toFixed(1)}`
}
return ''
}
},
legend: [
{
data: categories.map(c => c.name),
orient: 'vertical',
left: 10,
top: 50,
legend: [{
data: categories.map(c => c.name),
orient: 'vertical',
right: 10,
bottom: 10,
textStyle: {
color: '#fff'
}
],
series: [
{
type: 'graph',
layout: 'force',
data: nodes,
links: links,
categories: categories,
roam: true,
draggable: true,
force: {
repulsion: 200,
gravity: 0.1,
edgeLength: [50, 150],
friction: 0.6,
},
emphasis: {
focus: 'adjacency',
lineStyle: {
width: 3,
},
},
label: {
position: 'right',
formatter: '{b}',
},
labelLayout: {
hideOverlap: true,
},
scaleLimit: {
min: 0.5,
max: 5,
},
}
]
}],
series: [{
type: 'graph',
layout: 'none',
data: nodes,
links: links,
categories: categories,
roam: true, // Enable zoom and pan
scaleLimit: {
min: 0.2,
max: 5
},
draggable: false,
label: {
show: true,
position: 'inside'
},
lineStyle: {
color: 'source',
curveness: 0.1
},
emphasis: {
focus: 'adjacency',
lineStyle: {
width: 3
}
},
zoom: 1 // Initial zoom level
}]
}
chart.value.setOption(option)
chartInstance.setOption(option)
// Handle click event
chart.value.off('click')
chart.value.on('click', (params: any) => {
// Handle click events
chartInstance.on('click', (params: any) => {
if (params.dataType === 'node') {
selectedNode.value = params.data._data as TagGraphNode
const node = params.data
if (node.metadata?.type === 'tag') {
emit('searchTag', params.name)
message.info(`Searching for tag: ${params.name}`)
} else if (node.metadata?.type === 'cluster') {
// Open cluster directly with bound images
if (node.metadata.paths && node.metadata.paths.length > 0) {
emit('openCluster', {
title: params.name,
paths: node.metadata.paths,
size: node.metadata.image_count || node.metadata.paths.length
})
} else {
message.warning('No images found in this cluster')
}
} else {
message.info(`Abstract category: ${params.name}`)
}
}
})
}
const fitView = () => {
if (!chart.value) return
chart.value.resize()
}
// Watch for folder changes
watch(
() => props.folders,
() => {
fetchGraphData()
},
{ immediate: true }
)
const searchByTag = (tag: string) => {
emit('searchTag', tag)
}
const resizeHandler = () => {
chart.value?.resize()
}
// Watch for graphData and chartRef to trigger rendering
watch(
[graphData, chartRef],
() => {
if (graphData.value && chartRef.value) {
nextTick(() => {
renderChart()
})
}
}
)
onMounted(() => {
nextTick(() => {
window.addEventListener('resize', resizeHandler)
})
})
onBeforeUnmount(() => {
window.removeEventListener('resize', resizeHandler)
chart.value?.dispose()
})
// Auto load when folders change
watch(() => props.folders, () => {
if (props.folders.length) {
loadGraph()
// Rerender on window resize
_handleResize = () => {
if (chartInstance) {
chartInstance.resize()
}
}
}, { immediate: true })
window.addEventListener('resize', _handleResize)
})
// Reload when parameters change
watch([topNTags, topNClusters, showClusters], () => {
if (graphData.value) {
loadGraph()
onUnmounted(() => {
if (_handleResize) {
window.removeEventListener('resize', _handleResize)
_handleResize = null
}
if (chartInstance) {
chartInstance.dispose()
}
})
defineExpose({ loadGraph })
</script>
<style scoped>
.tag-graph-container {
<style scoped lang="scss">
.tag-hierarchy-graph {
width: 100%;
height: 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,
.error-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: #fff;
}
.loading-text {
margin-top: 16px;
font-size: 14px;
color: #aaa;
}
.graph-container {
width: 100%;
height: 100%;
position: relative;
}
.toolbar {
padding: 12px 16px;
background: #fafafa;
border-bottom: 1px solid #e8e8e8;
flex-shrink: 0;
}
.toolbar .label {
font-size: 13px;
color: #666;
.control-panel {
position: absolute;
top: 16px;
left: 16px;
z-index: 10;
background: rgba(0, 0, 0, 0.7);
padding: 8px 12px;
border-radius: 6px;
backdrop-filter: blur(10px);
}
.chart-container {
flex: 1;
width: 100%;
min-height: 500px;
}
.info-panel {
position: absolute;
top: 80px;
right: 20px;
width: 280px;
background: white;
border: 1px solid #d9d9d9;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
z-index: 1000;
}
.info-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
}
.info-title {
font-weight: 600;
font-size: 15px;
}
.info-body {
padding: 12px 16px;
}
.info-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 13px;
}
.info-label {
color: #666;
}
.info-value {
font-weight: 500;
}
.info-actions {
padding: 12px 16px;
border-top: 1px solid #f0f0f0;
}
.stats-panel {
position: absolute;
top: 80px;
left: 20px;
display: flex;
gap: 16px;
background: white;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 12px 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 24px;
font-weight: 600;
color: #1890ff;
}
.stat-label {
font-size: 12px;
color: #666;
margin-top: 4px;
height: 100%;
}
</style>

View File

@ -29,6 +29,7 @@ const threshold = ref(0.9)
const minClusterSize = ref(2)
const result = ref<ClusterIibOutputResp | null>(null)
const cacheInfo = ref<ClusterIibOutputCachedResp | null>(null)
const embeddingBuilt = ref(false) // Track if embeddings are already built
const _REQS_LS_KEY = 'iib_topic_search_hide_requirements_v1'
// true = show requirements; false = hidden
@ -262,6 +263,7 @@ const refresh = async () => {
const runQuery = async () => {
const q = (query.value || '').trim()
if (!q) return
if (qLoading.value) return
if (!scopeCount.value) {
message.warning(t('topicSearchNeedScope'))
scopeOpen.value = true
@ -272,9 +274,13 @@ const runQuery = async () => {
qResult.value = await searchIibOutputByPrompt({
query: q,
top_k: 80,
ensure_embed: true,
ensure_embed: !embeddingBuilt.value, // Only build on first search
folder_paths: scopeFolders.value
})
// Mark embeddings as built after first successful search
if (!embeddingBuilt.value) {
embeddingBuilt.value = true
}
//
openQueryResult()
} finally {
@ -313,6 +319,20 @@ const openCluster = (item: ClusterIibOutputResp['clusters'][0]) => {
tab.key = (pane as any).key
}
const openClusterFromGraph = (cluster: { title: string; paths: string[]; size: number }) => {
const pane = {
type: 'topic-search-matched-image-grid' as const,
name: `${cluster.title}${cluster.size}`,
key: Date.now() + uniqueId(),
id: uniqueId(),
title: cluster.title,
paths: cluster.paths
}
const tab = g.tabList[props.tabIdx]
tab.panes.push(pane as any)
tab.key = (pane as any).key
}
const handleSearchTag = (tag: string) => {
// Search by tag name
query.value = tag
@ -333,6 +353,11 @@ onBeforeUnmount(() => {
stopJobPoll()
})
// Reset embedding built flag when scope changes
watch(scopeFolders, () => {
embeddingBuilt.value = false
}, { deep: true })
watch(
() => scopeFolders.value,
() => {
@ -522,11 +547,9 @@ watch(
<div v-else-if="activeTab === 'graph'" style="height: calc(100vh - 300px); min-height: 600px;">
<TagRelationGraph
:folders="scopeFolders"
:threshold="threshold"
:min-cluster-size="minClusterSize"
:lang="g.lang"
:model="undefined"
@search-tag="handleSearchTag"
@open-cluster="openClusterFromGraph"
/>
</div>