dsh-reference-frames 0.0.0-stage → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ - insert:
2
+ - id: reference-frames
3
+ name: 'dsh-reference-frames'
4
+ config:
5
+ apiToken: ''
6
+ # 「保存为插件预设」后自动 git push(用本机 git 凭据,无需在这里放 token)
7
+ autoPush: true
8
+ # 推送用这对远端 / 分支
9
+ pushRemote: origin
10
+ pushBranch: main
package/docs/GUIDE.md ADDED
@@ -0,0 +1,119 @@
1
+ # Guide
2
+
3
+ [English](GUIDE.md) · [简体中文](GUIDE.zh-CN.md)
4
+
5
+ ## Search
6
+
7
+ One input box. Type a brief, or paste / drag / pick an image — the panel decides: text becomes a frame search; an image goes to FrameThrower as a visual-similarity search and, when the web source is on, through the vision model into web image keywords.
8
+
9
+ - **AI search** (switch in the composer, left of the search button; used by every source, and shown even when only the web source is on):
10
+ - On — the host's default model returns two things in one call: one English single-frame description for the FrameThrower libraries (title / genre / per-title filters apply to the animation library), and 2–3 short image-search phrases for the web source (keywords, not sentences; they may stay in the brief's language or use an official English name). The model detects the brief's language first and translates the meaning into English for FrameThrower; Chinese, Japanese, Korean, French, Spanish and other languages can be typed directly. The exact terms are shown per source above the grid. With an attached image, a separate vision call reads the image (using your note as context) and produces the web phrases; FrameThrower still gets the raw image.
11
+ - Off — your words go upstream untouched: FrameThrower needs English, the web source takes the brief as written. For an attached image, no vision call happens: the web source uses your typed note as its query, and explains that the image itself cannot be translated when the note is empty.
12
+ - The helper asks for the model's lowest thinking level. A thinking model whose provider maps `reasoningEfforts.off` to nothing will still think for 10s+; mapping it to `off: off` makes the call return in ~2s, otherwise it times out and falls back to the raw brief.
13
+ - **Non-English briefs and image-based web search need AI.** FrameThrower matches English descriptions; a brief in Chinese, Japanese, French or any other language falls back to the default page. The AI step detects the input language and translates the meaning into English queries while keeping web phrases in the brief's language. Web image search needs a vision-capable model to turn an attached image into queries. With AI off the panel says so instead of sending a placeholder request.
14
+ - **Results always belong to the current search.** The grid is cleared first, the query is always visible, and failures (no token, credits, rate limit, unreachable, upstream status) are stated plainly.
15
+ - **Browse more** appends and de-duplicates the next page; “More frames” replaces the set instead. At the end it says “no more frames”; a failed page keeps the button and shows why.
16
+ - **Clear** sits left of “More frames” in the results header. One click — no confirmation, no toast — empties the grid, drops the persisted view cache (so a reload stays empty) and wipes the host-side search / image / interpret caches, so the next search starts fresh. The emptied grid keeps a quiet placeholder instead of leaving the middle blank.
17
+
18
+ ## Sources
19
+
20
+ “Sources” sits in the composer toolbar next to “Keywords”. Clicking it opens per-source switches. The three FrameThrower libraries sit together under one group; the web source sits below them. Settings shows one token card, entered once, and its switch turns the whole FrameThrower family on or off. The choice lives in `anistyle:sources:v1`. A search only calls the sources that are on, and one source failing never hides the others' results — the reason is listed above the grid.
21
+
22
+ - **Animation frames** — animated series and films matched against English single-frame descriptions; needs a token; supports similar frames, image search and reshuffling.
23
+ - **Film stills** — live-action frames from FrameThrower's film library (`POST /search`); needs a token. Supports similar frames and reshuffling, but not image search: that endpoint only accepts a publicly reachable image URL, so an attached image is skipped with a note.
24
+ - **Commercial shots** — individual shots from ads (`POST /commercials/shots/search`), tagged with brand, spot, year, shot size and light; needs a token. Supports similar shots and image search.
25
+ - **Chrome web images** — Bing Images' server-rendered result page for every language, with DuckDuckGo Images as a fallback. No token. With AI on it searches the model's short phrases, and falls back to your wording when AI is off or fails. Attach an image and the vision model reads it first, then searches with the phrases it describes (AI off means the image cannot be translated — a typed note is used as the query instead). No similar frames. These are public web endpoints, not official APIs: they can throttle, answer a scripted request with a human check, break on redesign, or return dead image links. Images go through the local `/anistyle/img` proxy to dodge hotlink protection and mixed content.
26
+ - **Where a tile came from** — web tiles carry a “Web” badge, film tiles “Film” and commercial shots “Ad”; ↗ opens the source page. Results from every enabled source are interleaved and de-duplicated.
27
+ - Similar (≈) appears on every FrameThrower tile (animation, film and commercials). An attached image goes to the animation and commercial libraries as a visual-similarity search, and to the web source through the vision model; film stills are skipped because their API needs a public image URL. With several sources on, the result sets are interleaved.
28
+ - Film and commercials are **off on first run** so the default search stays one request at the old cost; turn them on under Sources. The animation / film / commercial switches are independent.
29
+ ### Why not Pinterest
30
+
31
+ Pinterest's only public keyword search is `GET /v5/search/partner/pins`: a closed beta for invited/approved partners, capped at the top 10 results; `GET /v5/search/pins` only searches your own account's Pins. Access requires a business account, application review (Trial is rate-limited per day and tokens expire in 24h), Standard access, and OAuth 2. Pinterest's ToS also forbids automated scraping without permission. So this release does not integrate Pinterest; the only viable routes are partner + official API, or letting users paste board links themselves.
32
+
33
+ ## Keywords
34
+
35
+ “Keywords” under the input opens chips grouped by category (era, rendering, lighting …).
36
+
37
+ - Clicking a chip inserts it into the brief — it does not search. Click again to remove it.
38
+ - Manage mode: rename or delete a term, add a term, rename a category, add a category, delete a category. Built-in categories come back with “restore defaults”.
39
+ - Everything is stored locally in `anistyle:keywords:v1`.
40
+
41
+ ## Frames
42
+
43
+ Each frame shows title / year, its caption, shot tags and its own palette. Three buttons appear on hover:
44
+
45
+ | Button | Action |
46
+ | --- | --- |
47
+ | + | Open the board picker. Click a board to add it — the picker stays open for more boards; the trailing + takes a name and creates a board on the spot |
48
+ | ≈ | Replace **this tile only** with a visually similar frame (FrameThrower tiles only) |
49
+ | ↗ | Open the frame's source page |
50
+
51
+ Click an image to open the lightbox: `←` `→` to move, `Esc` to close, and **+ Add to board** at the right end of the caption row. Hover that button to unfold your boards; click one to add the frame and the lightbox stays open, ready for the next image. The bottom of the list creates a board on the spot. Opened from inside a board, the button reads **Add to this board** and drops the frame straight in. Right-click (or two-finger click on a trackpad) an image in either the frame lightbox or the attached-image preview to get a **Copy image** menu: it writes a PNG to the system clipboard, scaling down past 2400px, and says so explicitly when the browser cannot copy images.
52
+
53
+ ## Moodboards
54
+
55
+ Boards start empty and are stored in this browser.
56
+
57
+ - Card actions appear on hover: pin ↑ (pinned boards sort first) and delete ×.
58
+ - Inside a board, ≈ on a frame returns recommendations in a strip above the grid. Click + on a recommendation to add it; nothing is added otherwise.
59
+ - **Similar picks** (board header) samples three random frames from the board and searches with all of them at once. “Reshuffle” samples again.
60
+ - Rename, remove frames, and export Markdown (per-frame caption, palette, deep link).
61
+
62
+ ## Preset moodboards
63
+
64
+ Your boards are saved to `~/.dsh/reference-frames/moodboards.json` — outside the installed package, so a plugin update never overwrites them. The first run seeds that file from the boards the package ships in `presets/moodboards.json`.
65
+
66
+ - Users: when the package's file has content, a **Community boards** button appears on the moodboards page. Same-name boards are skipped and ids are regenerated; ignoring the button changes nothing.
67
+ - Author: in a checkout, the upload icon at the right of the moodboards header (tooltip “Sync to GitHub”) writes the current boards back into `presets/moodboards.json`, then commits and pushes with your local git credentials; the icon spins while it runs. An npm install has no repo to push into, so the icon is hidden there and saving just writes the local file.
68
+
69
+ ```yaml
70
+ # cordis.patch.yml
71
+ config:
72
+ autoPush: true # false = write the file only
73
+ pushRemote: origin
74
+ pushBranch: main
75
+ ```
76
+
77
+ ## Updates
78
+
79
+ Updates belong to Harness's **Plugins** page: the plugin is published on npm, and its bundle page lists newer versions with an **Update** button that replaces the installed copy in place. Nothing inside the panel talks to GitHub.
80
+
81
+ - Install from npm (Plugins page → 添加插件) and the version list appears on its own; a `link:` checkout gets no list, so update that with `git pull` and a restart.
82
+ - The gear menu ends with the version this copy came from — a label, no button.
83
+
84
+ ## Token & cost
85
+
86
+ Gear menu → paste the `ft_…` token. It stays in browser localStorage, travels as a request header to the local route, and is forwarded host-side; it never enters the chat or the bundle. The token only affects FrameThrower; the web-image source needs none.
87
+
88
+ Animation frames cost about 4 credits per search, film stills and commercial shots about 2. A similar-frame search costs 2, or 10 when searching by image.
89
+
90
+ ## Files & routes
91
+
92
+ | File | Role |
93
+ | --- | --- |
94
+ | `index.js` | Host half: proxies the three FrameThrower libraries (animation / film / commercials) and web images (DuckDuckGo / Bing fallback), image proxy, caches searches per source, caches the style vocabulary for 24h, rewrites the brief and reads attached images for web queries with the default model, reads/writes presets |
95
+ | `client.js` | Browser half: panel UI, source switches, AI switch, moodboards in localStorage |
96
+ | `cordis.patch.yml` | Plugin row and config |
97
+ | `presets/moodboards.json` | Shared moodboards shipped with the package (the live copy is `~/.dsh/reference-frames/moodboards.json`) |
98
+ | `locale/*.json` | Title and description shown in the plugin manager |
99
+
100
+ - `POST /anistyle/interpret` — `{ text }` → `{ source: 'model'|'none', params }`
101
+ - `POST /anistyle/interpret-image` — `{ image, text?, count? }` → `{ source: 'model'|'none', params: { web } }` (vision model; stores the image as a host attachment)
102
+ - `GET /anistyle/vocabulary` — style vocabulary (proxied and cached)
103
+ - `POST /anistyle/frames` — `{ source?: 'animation' | 'film' | 'commercial', query, limit, offset, ... }`; animation also takes `style` / `title` / `genre` / `perTitle`, film `film_title` / `genre`, commercial `perSpot`
104
+ - `POST /anistyle/web` — `{ query, limit, offset }` → `{ source: 'chrome', frames }` (market inferred from the query script)
105
+ - `GET /anistyle/img?u=` — image proxy (same-origin, cached, private hosts rejected, size/type limited)
106
+ - `POST /anistyle/similar` — `{ source?, frameId | frameIds | image, by?, limit }` (film needs ids; commercial maps ids to shots and an image to `/commercials/shots/image`)
107
+ - `GET /anistyle/presets` — `{ boards, checkout }`; `checkout` says whether this install can push boards back to the repo
108
+ - `POST /anistyle/presets` — write the boards to the user's data file; pushes only from a checkout
109
+ - `POST /anistyle/clear-cache` — clear all in-memory caches (frame / web / image / interpret / vocabulary)
110
+ - `GET /anistyle/last` — last upstream response (diagnostics)
111
+ - `GET /anistyle/version` — the installed version, shown at the foot of the gear menu
112
+
113
+ ## Maintenance
114
+
115
+ - Two ways in: the npm copy under `~/.dsh/profiles/<profile>/node_modules/dsh-reference-frames` (what users run), or a `link:`ed checkout for development.
116
+ - In a checkout: `client.js` → refresh the page; `index.js` / `cordis.patch.yml` → restart the app. In an npm install, edit the checkout and publish — the change arrives through the Plugins page.
117
+ - Check syntax with `node --check client.js && node --check index.js`.
118
+ - Renaming requires four matching values: package `name`, `cordis.patch.yml` `name`, `client.js` `id`, and the profile entries.
119
+ - Release: bump `package.json`, add a `CHANGELOG.md` entry, commit, then push a `vX.Y.Z` tag. `.github/workflows/release.yml` publishes to npm (trusted publishing, no OTP) and opens the GitHub release titled `vX.Y.Z`.
@@ -0,0 +1,120 @@
1
+ # 使用与维护
2
+
3
+ [English](GUIDE.md) · [简体中文](GUIDE.zh-CN.md)
4
+
5
+ ## 检索
6
+
7
+ 只有一个输入框。可以写一句需求,也可以粘贴 / 拖入 / 选择一张图——面板自己判断:文字走帧检索;有图时动画库和广告库按图找相似帧,网页源开着则先让多模态模型读图、生成网页检索词。
8
+
9
+ - **AI检索**(输入框内、搜索按钮左侧的开关,所有源都用;只开网页源时也会出现):
10
+ - 开——宿主用默认模型一次给出两组词:FrameThrower 三个库共用的英文「单帧画面描述」(作品名 / 类型 / 每部取几张只供动画库),以及网页图片用的 2-3 条短检索词(关键词而不是句子,可以保留原文语言或官方英文名)。模型会先识别输入语言,再把内容翻译成英文供 FrameThrower 使用;中文、日文、韩文、法文、西班牙文等都可以直接输入。结果区上方按源逐行显示实际发出的词。贴图时会另起一次多模态调用读图(输入的文字作为补充语境)生成网页检索词;FrameThrower 仍然直接拿原图找相似帧。
11
+ - 关——原文直接发给各源:FrameThrower 需要英文,网页图片则是你写的原话。贴图时不会调用模型:网页源改用你输入的文字检索;没有文字时直接说明图片无法转成检索词。
12
+ - 选的是模型的「最低思考档」。如果默认模型是思考型、且 provider 里 `reasoningEfforts.off` 映射为空,它仍会先思考十几秒;把该映射改成 `off: off` 后,生词约 2 秒返回,否则会超时并退回原文。
13
+ - **非英文需求和网页以图检索都需要 AI。** FrameThrower 按英文描述匹配;中文、日文、法文等任何非英文原文都会退回默认结果。AI 会先识别输入语言,把语义翻译成英文检索词,同时让网页检索词保持原文语言。网页源要把图片变成检索词,必须有能读图的多模态模型。关着 AI 时面板会直接说明,不发占位请求。
14
+ - **结果永远属于这一次检索。** 每次先清空网格,检索词始终可见;失败时直接说明原因(无 token / 额度 / 限流 / 连不上 / 上游状态码)。
15
+ - **浏览更多**追加下一页并去重,「换一批」则是换一组。翻到底显示「没有更多了」;某一页失败会保留按钮并说明原因。
16
+ - 结果区标题右侧、「换一批」左边有「**清空**」:点一下即可(没有确认框、没有提示),清空当前参考帧、浏览器里保存的结果缓存(刷新也不会恢复),以及宿主侧的检索 / 图片 / 改写缓存,让下一次检索从零开始。清空后结果区留有一段克制的占位提示,中间不会变成一片空白。
17
+
18
+ ## 检索源
19
+
20
+ 「检索源」在输入框工具栏、关键词右侧,点开可以逐个开关;三个 FrameThrower 库同属一组,网页源在下面单独一组。状态存在本机 `anistyle:sources:v1`。设置里按凭据只放一张卡:token 填一次,卡片上的开关会同时开关动画 / 电影 / 广告三个库。每次检索只打开发着的源,一个源失败不会挡住其他源的结果,失败原因会作为一行提示列在结果上方。
21
+
22
+ - **动画帧** — 动画剧集与动画电影,按英文单帧描述匹配,需要 token;支持相似帧、按图检索、换一批。
23
+ - **电影剧照** — FrameThrower 真人电影库(`POST /search`),需要 token;支持相似帧和换一批,但不支持按图检索:该接口只收公开可访问的图片 URL,贴图时会跳过并说明原因。
24
+ - **广告镜头** — 广告里的单个镜头(`POST /commercials/shots/search`),带品牌、片名、年份、景别和光线标签;需要 token;支持相似镜头和按图检索。
25
+ - **Chrome 网页图片** — 所有语言都读 Bing 图片的完整结果页,DuckDuckGo 图片兜底;不需要 token。开 AI 时用模型给的短检索词,关 AI 或模型失败时退回原文;没有相似帧。贴图时先让多模态模型读图、生成网页检索词再搜;关掉 AI 时图片无法转成检索词,只会拿你输入的文字去搜。这些都是网页端公开结果,属于非官方接口:可能限流、对脚本请求回人机验证、改版失效,图片也可能 404。图片统一经本机 `/anistyle/img` 代理,避免防盗链和混合内容。
26
+ - **结果的来源** — 网页帧卡片标「网页」,电影标「电影」,广告镜头标「广告」,↗ 打开原始页面;所有开着的结果交替混排并去重。
27
+ - 相似帧(≈)对动画、电影、广告三种 FrameThrower 帧都可用。贴图会同时走多条路:动画库和广告库做视觉相似帧,网页源用多模态模型把图片转成检索词再搜网页图片;电影剧照因为接口只收公开图片链接而跳过。多个源都开时结果交替混排。
28
+ - 电影和广告默认**关闭**,第一次用时在「检索源」里打开;这样默认检索仍是原来的一次请求、原价。三个库的开关彼此独立。
29
+
30
+ ### 为什么没有 Pinterest
31
+
32
+ Pinterest 官方 API 的公开关键词搜索只有 `GET /v5/search/partner/pins`,属于 closed beta,只对受邀/申请通过的 partner 开放,且只回前 10 条;`GET /v5/search/pins` 只能搜自己账号的 Pin。接入还要商业账号 + 应用审核(Trial 按天限流、token 24 小时过期)→ Standard access,以及 OAuth 2 授权。同时 Pinterest ToS 明确禁止未经许可的自动化抓取。所以这一版不接 Pinterest;要做只能走「申请 partner + 官方 API」或「用户主动粘贴板链接」。
33
+
34
+ ## 关键词
35
+
36
+ 输入框下方的「关键词」展开是按类别分组的词(年代、渲染、光线……)。
37
+
38
+ - 点词是**写进需求**,不会触发检索;再点一次取消。
39
+ - 管理模式:改词名、删词、加词、改类别名、新建类别、删除类别。内置类别可用「恢复默认」找回。
40
+ - 全部存在本机 `anistyle:keywords:v1`。
41
+
42
+ ## 帧
43
+
44
+ 每帧显示作品 / 年份、画面描述、镜头标签和它自己的色板。悬停出现三个按钮:
45
+
46
+ | 按钮 | 作用 |
47
+ | --- | --- |
48
+ | + | 打开选板器。点一个板就加入,选板器不会关,可以连续加;末尾的 + 现场输名字新建 |
49
+ | ≈ | 只把**这一张**换成视觉上相似的帧,其他帧不动(仅 FrameThrower 帧) |
50
+ | ↗ | 打开这一帧所在的来源页 |
51
+
52
+ 点图片进入灯箱:`←` `→` 翻页,`Esc` 关闭,说明行右端有「+ 加入情绪板」。悬停这个按钮会展开情绪板列表,点一个就加入,灯箱不关,可以接着翻下一张;列表底部可以现场新建一个板。从情绪板里打开的灯箱,按钮变成「加入这个情绪板」,直接加进当前板。在图片上点右键或触控板双指点按,会弹出「复制图片」菜单:把当前图转成 PNG 写进系统剪贴板,超过 2400px 会等比缩小;浏览器不支持 Clipboard 图片写入时会明确提示。
53
+
54
+ ## 情绪板
55
+
56
+ 情绪板从空开始,存在本机浏览器。
57
+
58
+ - 卡片悬停出现:置顶 ↑(置顶排最前)、删除 ×。
59
+ - 进入情绪板后,帧上的 ≈ 会在网格上方给出「相似推荐」,点 + 才加入,不点什么都不发生。
60
+ - 板头「相似推荐」从板里随机抽 3 帧作为一次多图参考,「换一组」重新抽样。
61
+ - 支持重命名、移除帧、导出 Markdown(逐帧描述、色板、deep link)。
62
+
63
+ ## 预设情绪板
64
+
65
+ 你自己的情绪板保存在 `~/.dsh/reference-frames/moodboards.json` —— 在安装目录之外,插件更新不会覆盖它。首次运行会从包内 `presets/moodboards.json` 播种。
66
+
67
+ - 用户:包内预设文件有内容时,情绪板页会出现「社区情绪板」。同名跳过、id 重新生成;不点则完全不受影响。
68
+ - 作者:在 git 检出里,情绪板页右上角的上传图标(悬停提示「同步至 GitHub」)会把当前情绪板写回 `presets/moodboards.json`,默认用本机 git 凭据提交并推送,上传过程中图标变成转圈。npm 安装没有可推送的仓库,这个图标会隐藏,保存只写本地文件。
69
+
70
+ ```yaml
71
+ # cordis.patch.yml
72
+ config:
73
+ autoPush: true # false = 只写文件
74
+ pushRemote: origin
75
+ pushBranch: main
76
+ ```
77
+
78
+ ## 版本更新
79
+
80
+ 更新交给 Harness 的 **插件** 页:插件发布在 npm 上,bundle 详情页会列出更新的版本,点 **更新** 就地替换已装的副本。面板本身不再联系 GitHub。
81
+
82
+ - 从 npm 安装(插件页 → 添加插件)后版本列表自然出现;`link:` 链接安装不会显示版本列表,那种装法改完 `git pull` 再重启。
83
+ - 齿轮菜单最底下一行只显示当前版本号,没有按钮。
84
+
85
+ ## Token 与费用
86
+
87
+ 齿轮菜单里粘贴 `ft_…`。token 只存在浏览器 localStorage,随请求头发给本机路由,由宿主转发;不进对话,也不进打包产物。token 只影响 FrameThrower 源;网页图片源不需要 token。
88
+
89
+ 动画帧检索约 4 credits,电影 / 广告检索约 2 credits;相似帧检索 2 credits(以图检索 10)。
90
+
91
+ ## 文件与路由
92
+
93
+ | 文件 | 作用 |
94
+ | --- | --- |
95
+ | `index.js` | 宿主半:代理 FrameThrower 三个库(动画 / 电影 / 广告)、网页图片(DuckDuckGo / Bing 兜底)与图片代理、按源缓存检索、缓存 24h 画风词表、用默认模型改写需求、读图生成网页检索词、读写预设 |
96
+ | `client.js` | 浏览器半:面板 UI、检索源开关、AI 开关、情绪板(localStorage) |
97
+ | `cordis.patch.yml` | 插件行与配置 |
98
+ | `presets/moodboards.json` | 随包发布的分享情绪板(实际使用的一份在 `~/.dsh/reference-frames/moodboards.json`) |
99
+ | `locale/*.json` | 插件管理页显示的标题与描述 |
100
+
101
+ - `POST /anistyle/interpret` — `{ text }` → `{ source: 'model'|'none', params }`
102
+ - `POST /anistyle/interpret-image` — `{ image, text?, count? }` → `{ source: 'model'|'none', params: { web } }`(多模态模型;图片会先存入宿主附件库)
103
+ - `GET /anistyle/vocabulary` — 画风词表(转发并缓存)
104
+ - `POST /anistyle/frames` — `{ source?: 'animation' | 'film' | 'commercial', query, limit, offset, ... }`;动画另有 `style` / `title` / `genre` / `perTitle`,电影用 `film_title` / `genre`,广告用 `perSpot`
105
+ - `POST /anistyle/web` — `{ query, limit, offset }` → `{ source: 'chrome', frames }`(检索市场按查询文本的语种判断)
106
+ - `GET /anistyle/img?u=` — 图片代理(同源、带缓存、拒绝内网地址、限制大小与类型)
107
+ - `POST /anistyle/similar` — `{ source?, frameId | frameIds | image, by?, limit }`(电影只能用 id;广告把 id 转成 shot_ids,图片转给 `/commercials/shots/image`)
108
+ - `GET /anistyle/presets` — `{ boards, checkout }`;`checkout` 表示这份安装能不能把情绪板推回仓库
109
+ - `POST /anistyle/presets` — 把情绪板写入用户数据目录;只有 git 检出才会推送
110
+ - `POST /anistyle/clear-cache` — 清空全部内存缓存(帧 / 网页 / 图片 / 改写 / 画风词表)
111
+ - `GET /anistyle/last` — 最近一次上游响应(诊断)
112
+ - `GET /anistyle/version` — 当前安装的版本号,齿轮菜单底部显示它
113
+
114
+ ## 维护
115
+
116
+ - 两种装法:npm 副本在 `~/.dsh/profiles/<profile>/node_modules/dsh-reference-frames`(用户跑的就是它),开发时用 `link:` 链接检出。
117
+ - 检出里:改 `client.js` 刷新页面;改 `index.js` / `cordis.patch.yml` 重启应用。npm 安装里要先在检出改好再发版,改动经插件页更新下来。
118
+ - 语法自检:`node --check client.js && node --check index.js`。
119
+ - 改名要同步四处:`package.json` 的 `name`、`cordis.patch.yml` 的 `name`、`client.js` 的 `id`,以及 profile 里的登记。
120
+ - 发布:改 `package.json` 版本、写 `CHANGELOG.md`、提交,然后推 `vX.Y.Z` 标签。`.github/workflows/release.yml` 用 trusted publishing 发 npm(不需要 OTP),并以同名标题 `vX.Y.Z` 建 GitHub release。
@@ -0,0 +1,47 @@
1
+ # 参考帧 · DSH 插件
2
+
3
+ [English](../README.md) · [简体中文](README.zh-CN.md)
4
+
5
+ 说一句你想要的感觉,它会找出对味的真实参考帧,供你收进情绪板。
6
+
7
+ ## 安装
8
+
9
+ 前提:DeepSeek Harness。[FrameThrower](https://framethrower.ai) token(`ft_…`,免费注册)用于动画帧源;网页图片源不需要 token,所以没有 token 也能用。
10
+
11
+ `dsh-reference-frames` 已发布到 npm。在 **插件** 页安装,并由该页负责更新。
12
+
13
+ **从 npm 安装。** 插件页 → 添加插件 → `dsh-reference-frames`,或在 `~/.dsh/profiles/<profile>/package.json` 里写:
14
+
15
+ ```json
16
+ {
17
+ "dependencies": { "dsh-reference-frames": "^1.6.0" },
18
+ "dsh": { "profile": { "bundles": ["…", "dsh-reference-frames"] } }
19
+ }
20
+ ```
21
+
22
+ 装好后依赖保存为精确版本,插件详情页的「版本」区块会列出更新的版本,并带 **更新** 按钮。
23
+
24
+ **从源码安装。** 改为链接本机目录:
25
+
26
+ ```json
27
+ {
28
+ "dependencies": { "dsh-reference-frames": "link:/绝对路径/dsh-reference-frames" },
29
+ "dsh": { "profile": { "bundles": ["…", "dsh-reference-frames"] } }
30
+ }
31
+ ```
32
+
33
+ ```bash
34
+ cd ~/.dsh/profiles/<profile> && pnpm install
35
+ ```
36
+
37
+ 链接安装不会在插件页显示版本列表 —— 改完 `git pull` 再重启 Harness 即可。
38
+
39
+ 两种情况都要重启 Harness,侧边栏才会出现 **参考帧**。首次使用在齿轮菜单里填入 token。
40
+
41
+ > `package.json` 的 `name`、`cordis.patch.yml` 的 `name`、`client.js` 顶部的 `id` 必须一致,
42
+ > 漏掉任何一个插件都会静默不加载。
43
+
44
+ ## 文档
45
+
46
+ - [使用与维护](GUIDE.zh-CN.md) — 检索、关键词、情绪板、预设、路由、维护
47
+ - [更新日志](../CHANGELOG.md)
package/icon.svg ADDED
@@ -0,0 +1,25 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="Art Style Explorer">
2
+ <defs>
3
+ <linearGradient id="ase-g" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0" stop-color="#ff8a5c"/>
5
+ <stop offset="0.52" stop-color="#e0547a"/>
6
+ <stop offset="1" stop-color="#5b5bd6"/>
7
+ </linearGradient>
8
+ </defs>
9
+ <rect x="2" y="2" width="60" height="60" rx="15" fill="#141320"/>
10
+ <rect x="9" y="14" width="46" height="30" rx="4" fill="url(#ase-g)"/>
11
+ <circle cx="24" cy="29" r="7" fill="#141320" opacity="0.55"/>
12
+ <path d="M38 22.5 50 29l-12 6.5z" fill="#141320" opacity="0.75"/>
13
+ <g fill="#f7f3ee">
14
+ <rect x="13" y="38" width="7" height="3" rx="1.5"/>
15
+ <rect x="23" y="38" width="7" height="3" rx="1.5"/>
16
+ <rect x="33" y="38" width="7" height="3" rx="1.5"/>
17
+ <rect x="43" y="38" width="7" height="3" rx="1.5"/>
18
+ </g>
19
+ <g>
20
+ <rect x="9" y="49" width="9" height="5" rx="2.5" fill="#ff8a5c"/>
21
+ <rect x="20.5" y="49" width="9" height="5" rx="2.5" fill="#f4c46a"/>
22
+ <rect x="32" y="49" width="9" height="5" rx="2.5" fill="#8fd3c1"/>
23
+ <rect x="43.5" y="49" width="9" height="5" rx="2.5" fill="#9a8cf0"/>
24
+ </g>
25
+ </svg>