dsh-prompt-rewriter 1.4.1 → 1.5.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.
package/README.md CHANGED
@@ -97,6 +97,7 @@ Only images **currently sitting in the composer** — **never images from earlie
97
97
  - The model dropdown lists **the providers and models you actually have** — you can't type in one that doesn't exist.
98
98
  - Turning the model switch off does not clear your selection; flipping it back on shows the same choice.
99
99
  - Images still uploading are skipped, and the status area tells you how many were missed. A rewrite never fails because of an image.
100
+ - The **current version** sits in the top-right of the settings page; clicking it opens the project home page. Check it before reporting a problem — it makes the issue much easier to pin down.
100
101
 
101
102
  ## Install
102
103
 
package/README.zh.md CHANGED
@@ -98,6 +98,7 @@
98
98
  - 模型下拉列出你**实际拥有**的渠道与模型,填不进不存在的模型。
99
99
  - 关掉模型开关不会清空你的选择,重新打开还是同一个。
100
100
  - 上传中的图片会被跳过,状态区会告诉你少读了几张。不会因为一张图让整次改写失败。
101
+ - 设置页右上角显示**当前版本号**,点一下会打开项目主页。遇到问题时先看一眼版本,反馈时也更好定位。
101
102
 
102
103
  ## 安装
103
104
 
package/client.js CHANGED
@@ -111,8 +111,21 @@ window.__ModuleLoader__.load({
111
111
  // ---- Settings page -------------------------------------------------
112
112
  // Host theme tokens only, so the page follows light/dark automatically.
113
113
  '.dsh-pe-settings{display:flex;flex-direction:column;gap:4px;max-width:640px}',
114
- '.dsh-pe-settingsIntro{margin:0 0 8px;color:var(--dsw-alias-label-secondary);',
114
+ // Header line: intro on the left, version on the right, the way the plugin
115
+ // market pairs a description with its package version.
116
+ '.dsh-pe-settingsHead{display:flex;flex-wrap:wrap;align-items:baseline;',
117
+ 'justify-content:space-between;gap:4px 12px;margin:0 0 8px}',
118
+ '.dsh-pe-settingsIntro{margin:0;color:var(--dsw-alias-label-secondary);',
115
119
  'font-size:13px;line-height:20px}',
120
+ // Same weight and colour as the host's own package version labels: muted,
121
+ // monospace, and unobtrusive until hovered.
122
+ '.dsh-pe-version{flex:none;font-size:12px;line-height:20px;color:var(--dsw-alias-label-tertiary);',
123
+ 'font-family:var(--dsw-font-mono,ui-monospace,SFMono-Regular,Menlo,monospace);',
124
+ 'text-decoration:none;border-radius:var(--dsw-radius-sm)}',
125
+ 'a.dsh-pe-version{cursor:pointer}',
126
+ 'a.dsh-pe-version:hover{color:var(--dsw-alias-label-secondary);text-decoration:underline}',
127
+ 'a.dsh-pe-version:focus-visible{outline:none;color:var(--dsw-alias-label-secondary);',
128
+ 'box-shadow:0 0 0 2px var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary))}',
116
129
  // Each row is a two-column flex that WRAPS. Wrapping is what makes the
117
130
  // narrow case work: `@container` cannot be used here because the host
118
131
  // settings panel owns the ancestor chain and declares no container-type,
@@ -789,6 +802,9 @@ window.__ModuleLoader__.load({
789
802
  */
790
803
  function SettingsSection({ t, getRemote }) {
791
804
  const [values, setValues] = useState(null);
805
+ // `{ version, url }` reported by the Host from the package manifest. Never
806
+ // hard-coded here: a release only bumps package.json.
807
+ const [about, setAbout] = useState(null);
792
808
  const [catalog, setCatalog] = useState({ status: 'loading', groups: [] });
793
809
  const [error, setError] = useState(null);
794
810
  const [saving, setSaving] = useState(false);
@@ -854,6 +870,7 @@ window.__ModuleLoader__.load({
854
870
  if (!alive.current) return;
855
871
  if (body?.ok !== true) throw new Error(body?.message || t('settings.loadFailed', { status: response.status }));
856
872
  setValues(body.values);
873
+ if (body.about !== undefined) setAbout(body.about);
857
874
  } catch (failure) {
858
875
  if (alive.current && !controller.signal.aborted) setError(failure instanceof Error ? failure.message : String(failure));
859
876
  }
@@ -929,7 +946,21 @@ window.__ModuleLoader__.load({
929
946
  return h(
930
947
  'div',
931
948
  { className: 'dsh-pe-settings' },
932
- h('p', { className: 'dsh-pe-settingsIntro' }, t('settings.intro')),
949
+ // Intro line with the version at the right, the way the plugin market shows a
950
+ // package version beside its description. The version links to the project
951
+ // page and opens in a new tab, so the current session is left untouched.
952
+ h('div', { className: 'dsh-pe-settingsHead' },
953
+ h('p', { className: 'dsh-pe-settingsIntro' }, t('settings.intro')),
954
+ about?.version !== undefined && (about?.url === undefined
955
+ ? h('span', { className: 'dsh-pe-version' }, `v${about.version}`)
956
+ : h('a', {
957
+ className: 'dsh-pe-version',
958
+ href: about.url,
959
+ target: '_blank',
960
+ rel: 'noopener noreferrer',
961
+ title: about.url,
962
+ }, `v${about.version}`)),
963
+ ),
933
964
 
934
965
  row(
935
966
  'model',
@@ -987,7 +1018,10 @@ window.__ModuleLoader__.load({
987
1018
  row(
988
1019
  'context',
989
1020
  t('settings.context.title'),
990
- values.referenceContext ? t('settings.context.on') : t('settings.context.off'),
1021
+ // The dot's meaning rides along in the same hint, separated by a space,
1022
+ // so the row stays one comfortable block instead of growing a second
1023
+ // paragraph.
1024
+ `${values.referenceContext ? t('settings.context.on') : t('settings.context.off')} ${t('settings.context.mark')}`,
991
1025
  toggle('settings.context', values.referenceContext, (next) => save({ referenceContext: next }, 'settings.context')),
992
1026
  ),
993
1027
 
@@ -1109,8 +1143,11 @@ window.__ModuleLoader__.load({
1109
1143
  'settings.model.loadError': '读取模型列表失败,请重试。',
1110
1144
  'settings.model.placeholder': '还没选过模型',
1111
1145
  'settings.context.title': '参考之前的对话',
1112
- 'settings.context.on': '开启:改写时会参考前面聊过的目标和已排除的方案,更贴近你的真实意图。会多耗 token,长对话也更慢。',
1113
- 'settings.context.off': '关闭:只看当前输入,不看之前的对话。',
1146
+ 'settings.context.on': '开启:参考前文的目标与已排除方案,更耗 token。',
1147
+ 'settings.context.off': '关闭:只看当前输入。',
1148
+ // One short line, merged into the hint above rather than added as its
1149
+ // own row: the dot's meaning is a detail of this switch.
1150
+ 'settings.context.mark': '蓝点=本次会参考上下文。',
1114
1151
  'settings.images.title': '参考输入框里的图片',
1115
1152
  'settings.images.on': '开启:输入框里的图片一并参考(历史图片始终不读)。会多耗 token。',
1116
1153
  'settings.images.off': '关闭:只处理文字。',
@@ -1140,8 +1177,9 @@ window.__ModuleLoader__.load({
1140
1177
  'settings.model.loadError': 'Could not load the model list. Please retry.',
1141
1178
  'settings.model.placeholder': 'No model chosen yet',
1142
1179
  'settings.context.title': 'Use earlier messages',
1143
- 'settings.context.on': 'On: the rewrite also considers goals and ruled-out options from earlier in this conversation. Costs more tokens and is slower in long chats.',
1144
- 'settings.context.off': 'Off: looks only at what you typed, not at earlier messages.',
1180
+ 'settings.context.on': 'On: uses earlier goals and ruled-out options; costs more tokens.',
1181
+ 'settings.context.off': 'Off: uses only what you typed.',
1182
+ 'settings.context.mark': 'Blue dot = this run uses context.',
1145
1183
  'settings.images.title': 'Use images in the composer',
1146
1184
  'settings.images.on': 'On: images already attached to your draft are included. Earlier images never are. Costs more tokens.',
1147
1185
  'settings.images.off': 'Off: text only.',
package/index.js CHANGED
@@ -18,6 +18,7 @@
18
18
  import fs from 'node:fs';
19
19
  import os from 'node:os';
20
20
  import path from 'node:path';
21
+ import { fileURLToPath } from 'node:url';
21
22
  import { zstdDecompressSync } from 'node:zlib';
22
23
  import z from '@deepseek-ai/schemastery';
23
24
 
@@ -1102,6 +1103,58 @@ function settingsNamespace(ctx) {
1102
1103
  return undefined;
1103
1104
  }
1104
1105
 
1106
+ /**
1107
+ * Package facts the settings page shows: version and project URL.
1108
+ *
1109
+ * Read from this package's own `package.json` at runtime rather than hard-coded,
1110
+ * so bumping the manifest is the only step a release needs. Cached after the first
1111
+ * read: the file cannot change while the plugin is loaded.
1112
+ *
1113
+ * @returns `{ version, url }`; fields are undefined when the manifest is absent.
1114
+ */
1115
+ function readAbout() {
1116
+ if (aboutCache !== undefined) return aboutCache;
1117
+ let version;
1118
+ let url;
1119
+ try {
1120
+ const manifest = JSON.parse(fs.readFileSync(path.join(packageRoot(), 'package.json'), 'utf8'));
1121
+ version = typeof manifest.version === 'string' ? manifest.version : undefined;
1122
+ // `repository` may be a string or `{ url }`; strip the git+ prefix and .git
1123
+ // suffix so it is directly openable in a browser.
1124
+ const raw = typeof manifest.repository === 'string' ? manifest.repository : manifest.repository?.url;
1125
+ if (typeof raw === 'string') {
1126
+ url = raw
1127
+ .replace(/^git\+/u, '')
1128
+ .replace(/\.git$/u, '')
1129
+ .replace(/^git:\/\//u, 'https://');
1130
+ }
1131
+ } catch {
1132
+ // A missing manifest simply hides the version line; it must not break the page.
1133
+ }
1134
+ aboutCache = { version, url };
1135
+ return aboutCache;
1136
+ }
1137
+
1138
+ /**
1139
+ * Directory holding this package's manifest.
1140
+ *
1141
+ * `fileURLToPath` is required rather than reading `url.pathname`: a pathname keeps
1142
+ * percent-encoding, so an install directory containing a space ("DSH project")
1143
+ * decoded to a literal `%20` and the manifest read failed silently.
1144
+ *
1145
+ * @returns the package root path.
1146
+ */
1147
+ function packageRoot() {
1148
+ try {
1149
+ return path.dirname(fileURLToPath(import.meta.url));
1150
+ } catch {
1151
+ return process.cwd();
1152
+ }
1153
+ }
1154
+
1155
+ /** Memoized result of {@link readAbout}. */
1156
+ let aboutCache;
1157
+
1105
1158
  /**
1106
1159
  * Read the live preference values.
1107
1160
  *
@@ -1147,7 +1200,7 @@ async function handleSettings(ctx, config, request) {
1147
1200
  if (ns === undefined) return fail('SETTINGS_UNAVAILABLE', '无法定位插件配置项,无法保存选项。');
1148
1201
 
1149
1202
  if (request.method === 'GET') {
1150
- return Response.json({ ok: true, values: readPreferences(ctx, config) });
1203
+ return Response.json({ ok: true, values: readPreferences(ctx, config), about: readAbout() });
1151
1204
  }
1152
1205
 
1153
1206
  let payload;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-prompt-rewriter",
3
- "version": "1.4.1",
3
+ "version": "1.5.1",
4
4
  "description": "One-click prompt enhancement beside the DeepSeek Harness composer: rewrites a draft in place without sending it.",
5
5
  "type": "module",
6
6
  "icon": "icon.svg",