@restregular/dsh-notepad 0.4.0 → 0.6.0

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.
package/index.js CHANGED
@@ -38,7 +38,7 @@ import {
38
38
  const ROUTE = '/dsh-notepad/api';
39
39
 
40
40
  /** Plugin version mirrored in package.json. */
41
- const VERSION = '0.4.0';
41
+ const VERSION = '0.6.0';
42
42
 
43
43
  /** Request body cap for note writes. */
44
44
  const BODY_LIMIT = 1024 * 1024;
@@ -55,6 +55,12 @@ const OPTIMIZE_TIMEOUT_MS = 120_000;
55
55
  /** Default output cap for one optimized segment. */
56
56
  const OPTIMIZE_MAX_TOKENS = 4096;
57
57
 
58
+ /** Entries kept in the in-process optimizer cache. */
59
+ const OPTIMIZE_CACHE_LIMIT = 64;
60
+
61
+ /** Maximum key + value characters kept in the optimizer cache. */
62
+ const OPTIMIZE_CACHE_CHARS = 2_000_000;
63
+
58
64
  /** Shared instruction for the optional writing assistant. */
59
65
  const OPTIMIZE_SYSTEM = [
60
66
  'You are a writing assistant for a developer notepad.',
@@ -238,6 +244,41 @@ function cleanOptimizedText(value) {
238
244
  return text;
239
245
  }
240
246
 
247
+ /**
248
+ * Results of earlier optimizer calls. Re-optimizing content that has not
249
+ * changed returns the previous answer instead of billing another model call.
250
+ * The cache is per host process and bounded by entry count and characters.
251
+ * @type {Map<string, string>}
252
+ */
253
+ const optimizeCache = new Map();
254
+ let optimizeCacheChars = 0;
255
+
256
+ /**
257
+ * @param {string} key
258
+ * @param {string} text
259
+ */
260
+ function cacheOptimizedText(key, text) {
261
+ const previous = optimizeCache.get(key);
262
+ if (previous !== undefined) {
263
+ optimizeCacheChars -= key.length + previous.length;
264
+ optimizeCache.delete(key);
265
+ }
266
+ optimizeCache.set(key, text);
267
+ optimizeCacheChars += key.length + text.length;
268
+ while (optimizeCache.size > OPTIMIZE_CACHE_LIMIT || optimizeCacheChars > OPTIMIZE_CACHE_CHARS) {
269
+ const oldest = optimizeCache.keys().next();
270
+ if (oldest.done === true) break;
271
+ const value = optimizeCache.get(oldest.value) ?? '';
272
+ optimizeCacheChars -= oldest.value.length + value.length;
273
+ optimizeCache.delete(oldest.value);
274
+ }
275
+ }
276
+
277
+ /** Workspace + title + text: everything the optimizer prompt depends on. */
278
+ function optimizeCacheKey(root, title, text) {
279
+ return `${root}\u0000${title}\u0000${text}`;
280
+ }
281
+
241
282
  /**
242
283
  * Ask the currently selected default model to improve one note segment.
243
284
  * The LLM service is optional: a deployment without one reports a stable
@@ -425,8 +466,16 @@ async function dispatchRequest(ctx, req, res) {
425
466
  `Segment is too large to optimize (${text.length} characters; limit ${MAX_OPTIMIZE_CHARS}).`,
426
467
  );
427
468
  }
428
- const optimized = await optimizeText(ctx, text, typeof body.title === 'string' ? body.title : '');
429
- sendJson(res, { ok: true, root, text: optimized });
469
+ const title = typeof body.title === 'string' ? body.title : '';
470
+ const cacheKey = optimizeCacheKey(root, title, text);
471
+ const cached = optimizeCache.get(cacheKey);
472
+ if (typeof cached === 'string' && cached !== '') {
473
+ sendJson(res, { ok: true, root, text: cached, cached: true });
474
+ return;
475
+ }
476
+ const optimized = await optimizeText(ctx, text, title);
477
+ cacheOptimizedText(cacheKey, optimized);
478
+ sendJson(res, { ok: true, root, text: optimized, cached: false });
430
479
  return;
431
480
  }
432
481
  if (action !== 'note') {
package/locale/en.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "Notepad",
4
- "description": "A per-workspace floating notepad in the session view. New segment cards lead the list, each card has an editable title; click an index to select, drag to reorder, merge a selection, and improve one or all cards with AI. Paste or pick images as path-based chips; removing a chip cleans up plugin-owned files. Pending counts appear on the hidden launcher and the sidebar workspace row. Insert, replace, or send one or many; sending archives automatically. Archived segments can be shown, hidden, restored, and deleted. Resizable and dark-mode aware."
4
+ "description": "A per-workspace floating notepad in the session view. New segment cards lead the list; an empty title is just a blank click target until clicked. Click an index to select, drag to reorder, merge a selection, move between cards with the arrow keys, and improve only the selected cards with AI — the button turns into undo and identical content reuses the previous answer. Insert and send buttons expand on hover into current-session / new-session groups, while replace stays a single button. Image chips open an in-panel preview; active segments can be deleted directly after a confirmation that warns the action cannot be undone. The footer shows one shortcut tip at a time and rotates on click; copy-all skips archived segments; sending follows the General Settings busy-send policy. Pending counts appear on the hidden launcher and the sidebar workspace row, auto refresh keeps the list scroll position, Ctrl+Shift+0 (or the header reset button) resets the panel geometry, and the panel never covers the host title bar."
5
5
  }
6
6
  }
package/locale/zh.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "记事本",
4
- "description": "在会话界面显示一个按工作区共享的分段便签浮窗:新分段置顶,标题可编辑,点击序号选中,可拖拽排序、合并多段并用 AI 优化单段或全部;支持粘贴或选择图片并以路径附件形式引入,删除图片芯片会清理插件目录里的文件;隐藏按钮和侧边栏工作区显示未发送数量角标;支持单段/多段插入、替换与发送,发送后自动归档,归档可显示、隐藏、恢复与删除,浮窗可调整尺寸并适配深色模式。"
4
+ "description": "在会话界面显示一个按工作区共享的分段便签浮窗:新分段置顶,空标题点击后才显示输入框,点击序号选中,可拖拽排序、合并多段、用上下方向键切换分段;AI 优化只作用于选中的分段,优化后按钮变为撤销,相同内容直接复用上次结果;插入 / 发送按钮 hover 时横向展开为「当前会话 / 新会话」按钮组,替换按钮保持单键;图片支持面板内预览;可直接删除分段,但需弹窗二次确认并提示无法恢复;底部只显示一条快捷键提示,点击轮换;复制全部不包含归档分段;发送行为跟随「设置 > 通用设置」中的繁忙发送策略;支持粘贴或选择图片并以路径附件形式引入;隐藏按钮和侧边栏工作区显示未发送数量角标;自动刷新保持滚动位置,浮窗可调整尺寸,Ctrl+Shift+0 或标题栏复位按钮可复位位置和尺寸,且不会越过窗口标题栏。"
5
5
  }
6
6
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@restregular/dsh-notepad",
3
- "version": "0.4.0",
4
- "description": "Notepad — a per-workspace floating notepad in the DeepSeek Harness session view: newest segment cards first with editable titles, drag to reorder, merge selected segments, AI improve one or all, click an index to select, image chips with automatic file cleanup, pending-count badges on the hidden button and the sidebar workspace, batch insert / replace / send, archive management, resizable panel and dark-mode styling. · 记事本 — 在 DeepSeek Harness 会话界面显示一个按工作区共享的分段便签浮窗:新分段置顶且标题可编辑,可拖拽排序、合并选中分段、AI 优化单段或全部、点击序号选中,图片芯片删除时自动清理磁盘文件,隐藏按钮与侧边栏工作区显示未发送数量角标,支持单段/多段插入、替换与发送,发送自动归档,归档可显示隐藏和恢复删除,可调整尺寸并适配深色模式。",
3
+ "version": "0.6.0",
4
+ "description": "Notepad — a per-workspace floating notepad in the DeepSeek Harness session view: newest segment cards first with click-to-edit titles, drag to reorder, merge selected segments, arrow-key navigation, AI improve limited to selected segments with undo and cached results, hover insert / send groups that also target a fresh session, in-panel image previews, direct delete with a confirmation, rotating shortcut tips, copy-all that skips archived segments, the General Settings busy-send policy, image chips with automatic file cleanup, pending-count badges on the hidden button and sidebar workspace, scroll-safe auto refresh, resizable title-bar-safe panel, Ctrl+Shift+0 geometry reset, and dark-mode styling. · 记事本 — 在 DeepSeek Harness 会话界面显示一个按工作区共享的分段便签浮窗:新分段置顶且空标题只在点击后显示输入框,可拖拽排序、合并选中分段、上下方向键切换分段、只对选中分段做 AI 优化,优化后按钮变为撤销并复用缓存结果;插入 / 发送按钮 hover 时展开为「当前会话 / 新会话」按钮组,图片支持面板内预览,可直接删除分段但需二次确认,底部快捷键提示可点击轮换,复制全部不再包含归档分段,发送行为跟随「设置 > 通用设置」中的繁忙发送策略;图片芯片删除时自动清理磁盘文件;隐藏按钮与侧边栏工作区显示未发送数量角标;自动刷新保持列表滚动位置;可调整尺寸,Ctrl+Shift+0 复位位置和尺寸,浮窗不会越过窗口标题栏,并适配深色模式。",
5
5
  "icon": "./icon.svg",
6
6
  "type": "module",
7
7
  "main": "index.js",
@@ -34,7 +34,9 @@
34
34
  "@deepseek-ai/dsh-client-ui-layout",
35
35
  "@deepseek-ai/dsh-api-session-controller",
36
36
  "@deepseek-ai/dsh-client-ui-session",
37
- "@deepseek-ai/dsh-client-ui-conversation"
37
+ "@deepseek-ai/dsh-client-ui-conversation",
38
+ "@deepseek-ai/dsh-client-ui-workspace",
39
+ "@deepseek-ai/dsh-client-shortcuts"
38
40
  ]
39
41
  }
40
42
  },