dsh-sticky-notes
A sticky-note wall inside DSH: one icon in the left sidebar, one panel to capture ideas anytime, and a file on disk so nothing is lost when the page reloads or the Harness restarts.
Install
The bundle is self-contained: no dependencies, no build step, no configuration
required. It targets a DSH build that ships the sidebar.panellist slot
(engines.dsh: ^0.2.1-alpha.2).
From a terminal:
dsh plugin add github:LoveStudyXin/dsh-sticky-notes
Or from the GUI: open DSH → Plugins / 插件 → Add / 添加, and paste the same address. To install from a local checkout instead (edits apply on reload):
dsh plugin add link:/absolute/path/dsh-sticky-notes
Keep the bundle enabled and refresh the page once (or restart DSH if the plugin manager asks for a restart). A sticky-note icon appears in the left rail; click it to open the wall.
The wall lives on disk; see Storage for the paths, backups and export. A
Chinese install guide ships as INSTALL.md.
What it does
- Sidebar entry — a panel icon in the left rail; clicking it opens the wall.
- Quick capture — type in the box at the top and press
↵to add a note (⇧↵for a new line).⌘/Ctrl + ↵also adds. - Images in the input — paste a screenshot (
⌘V/Ctrl+V), drag an image anywhere onto the wall, or pick files with the attach button. A note can carry up to 6 PNG/JPG/GIF/WebP images (≤8 MB each), shows thumbnails on the paper, and opens a full-size preview when a thumbnail is clicked (Esccloses). - Six paper colors, a slight paper tilt, and a masonry wall.
- Edit in place — click a note's text, change it, click away (or
⌘/Ctrl + ↵) to save;Esccancels. - Pin a note to the top, delete it with a 6-second undo toast.
- Search filters the wall; the footer shows saving state.
- 本地备份 / export / import — the wall is copied to disk before every overwrite (last 30 kept), restorable from the header, and exportable or importable as a JSON file.
- Texts follow the active DSH locale (Chinese/English).
Agent tool: sticky_notes_add
The Host half also registers a model-facing tool, so a Session can put a note on the wall without a browser — including a Session started by an IM bot (Feishu, WeChat, …), where the human just says “把这张图存到便利贴里”.
| Argument | Meaning |
|---|---|
text |
note text; optional when images are given |
color |
amber (default), rose, sky, mint, lilac, tangerine |
pinned |
pin the note to the top (default false) |
imagePaths |
image file paths: absolute, or relative to the calling Session's working directory (max 6) |
It reads the bytes itself and enforces the same rules as the panel (PNG/JPG/GIF/
WebP, ≤8 MB each, at most 6 images per note), appends one note to the same
notes.json, takes the same pre-overwrite backup, and returns
{ noteId, noteCount, images }.
The model does not need to see the picture: an IM bot stages a received image into the workspace and hands the model a path, so even a text-only model can save a screenshot it never “saw”.
The tool is registered when the plugin loads, so a linked development install needs the plugin row to reload (disable then enable, or restart DSH) before a Session can call it.
Zero-token IM command: /sticky (optional patch)
An IM message still costs a model turn — even when it only says “save this to a
sticky note”. scripts/patch-dsh-im-sticky.mjs patches the installed
@xmanrui/dsh-im bundle so a message that starts with /sticky is answered
without a Session turn: it writes the text and any staged images straight into
the wall and replies with the note id.
# from this package directory
node scripts/patch-dsh-im-sticky.mjs --check # report only
node scripts/patch-dsh-im-sticky.mjs # apply (keeps lib/index.js.dsh-sticky.orig)
node scripts/patch-dsh-im-sticky.mjs --revert # restore the unpatched bundle
Then disable and re-enable the “IM机器人” plugin (or restart DSH) and send
/sticky 测试测试小肥鱼 in Feishu, with an image attached if you want one saved
too. /sticky is not in the plugin’s command panel or /help — type it.
Notes:
- The patch edits a third-party plugin in place. A dsh-im upgrade overwrites it — re-run the script afterwards (it verifies its anchors and refuses to guess on an unknown bundle version).
- The patched path skips the bot’s command-permission check, so keep the bot’s allowlist tight.
- The wall is a single JSON file with last-write-wins writes: an open sticky panel that saves a stale wall can overwrite a note saved from IM. Reload the panel after an IM save if you keep it open.
Feishu 多维表格 mirror (optional)
Everything is local-first; the wall can additionally push each note to a Feishu Bitable (多维表格) table. It is one-way and additive: local files remain the source of truth, editing or deleting a note locally does not change its row, and Feishu being unreachable never blocks a local save (failures are logged and retried on the next tick).
Setup:
The Feishu app needs
bitable:app(orbase:app:create) anddrive:drive. Open the authorization link the setup script prints, have an admin approve it and publish a version.Run the setup script — it reuses the bot’s app credentials from DSH, creates “DSH 便利贴” with the fields
文字 / 颜色 / 置顶 / 创建时间 / 图片, and writes$DSH_HOME/state/sticky-notes/feishu.json:node scripts/setup-feishu-bitable.mjs --check # token + what is missing node scripts/setup-feishu-bitable.mjs # create and configureReload the sticky-notes plugin (or restart DSH). New notes are mirrored within a second or two; the wall is also polled every 30 s as a fallback.
How it works:
feishu.json(in the data dir) holdsenabled,appId,secretRef,appToken,tableId,baseUrlandbackfillNotes. Delete it or setenabled: falseto stop mirroring.feishu-sync.jsonremembers which note ids were pushed, so a restart never duplicates rows. Pointing at a different Bitable resets that history.- With
backfillNotes: true(the setup default) the existing wall is pushed once; set it tofalseto start from the next new note only. - Each image is uploaded as a Bitable attachment (
drive/v1/medias/upload_all,parent_type=bitable_image); a failed image upload still pushes the row without that image.
Storage
Everything is a local file; nothing lives only in the browser.
Paths below are under $DSH_HOME (default ~/.dsh):
$DSH_HOME/state/sticky-notes/notes.json— the wall itself.$DSH_HOME/state/sticky-notes/images/<id>.<ext>— pasted image bytes.$DSH_HOME/state/sticky-notes/backups/notes-<timestamp>-<count>.json— the wall before an overwrite (up to 30 kept; at most one per 30 s of editing). Images a backup still references are kept too, so a restored backup never shows broken pictures.
config.dataDir in cordis.patch.yml moves the whole directory. It is a plain
string (no schema, no ~ expansion): give an absolute path.
Limits: one wall holds at most 5000 notes and 64 MB of JSON per save; notes beyond that are dropped on write. Two windows on the same wall use last-write-wins.
Three ways to get data back or copy it out:
- 备份 in the panel header lists the local backups with their note count and restore time; one click restores that wall.
- 导出 downloads the current wall as
sticky-notes-<date>.json. - 导入 replaces the whole wall from a previously exported (or hand-edited) JSON file; entries that cannot be rendered are skipped, and the server backs up the current wall first.
The backup popover also shows the absolute notes.json path. Only requests from
the GUI itself (same-origin browser fetches) or from local tools are served;
uploads must carry the same-origin request marker, and image bytes — not the
declared content-type — decide the accepted formats.
A note references images by { id, type, size }; the bytes are served at
/sticky-notes/images/<id>.<ext>, and bytes no note or backup references are
collected after a 10-minute grace period on the next save.
Layout
| File | Role |
|---|---|
index.js |
Host half: notes file, normalization, local HTTP API, sticky_notes_add tool |
feishu-sync.js |
Optional one-way mirror: new notes → Feishu 多维表格 |
scripts/setup-feishu-bitable.mjs |
Optional: create the Bitable table and write feishu.json |
scripts/patch-dsh-im-sticky.mjs |
Optional: patch dsh-im so an IM /sticky message skips the model |
client.js |
Client half: sidebar panellist icon + main panel |
cordis.patch.yml |
Bundle patch inserting the plugin row |
locale/en.json, locale/zh.json |
Display title/description for the plugin manager |
icon.svg |
Original icon |
No comments yet. Be the first to write one.