dsh-rss 0.6.2 → 0.7.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/CHANGELOG.md +9 -0
- package/README.en.md +3 -0
- package/README.md +3 -0
- package/docs/VALIDATION.md +2 -0
- package/docs/validation-0.6.3.md +10 -0
- package/lib/client.js +81 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.7.0(2026-10-17)
|
|
4
|
+
|
|
5
|
+
- 设置面板顶部新增「首次使用」分步引导:一次只讲一步,支持上一步/下一步/跳过,看完记在本机不再出现。
|
|
6
|
+
- 引导内容与该插件的实际用法一致;可在开发者工具里清掉 `dsh-first-run:dsh-rss:v1` 重新查看。
|
|
7
|
+
## 0.6.3 (2026-10-17)
|
|
8
|
+
|
|
9
|
+
- 修复英文窄窗口中 RSS 标题截断;设置导航改为顶部换行排列,给订阅列表保留可读宽度。样式仅在 RSS 面板打开时生效。
|
|
10
|
+
- 在官方 DSH 0.2.1-alpha.5 中检查来源目录、分类、OPML 预览导入与导出、编辑订阅和中英文布局。
|
|
11
|
+
|
|
3
12
|
## 0.6.2 (2026-10-15)
|
|
4
13
|
|
|
5
14
|
- 加固直连、HTTP CONNECT 与 TLS 握手的套接字错误隔离,抓取结束会销毁本次请求仍持有的连接,避免错误在请求结束后逃逸。
|
package/README.en.md
CHANGED
|
@@ -10,6 +10,9 @@ Subscribe to and read RSS or Atom feeds, and manage updates from DSH.
|
|
|
10
10
|
|
|
11
11
|
Feedback and contributions are welcome: report [issues](https://github.com/STARDUSTLC666/dsh-rss/issues) or submit [pull requests](https://github.com/STARDUSTLC666/dsh-rss/pulls).
|
|
12
12
|
|
|
13
|
+
|
|
14
|
+
See the [0.6.3 validation record](docs/validation-0.6.3.md) for DSH `0.2.1-alpha.5` checks and the narrow-window layout fix.
|
|
15
|
+
|
|
13
16
|
## What it does
|
|
14
17
|
|
|
15
18
|
- Add feeds, read articles and fetch updates.
|
package/README.md
CHANGED
package/docs/VALIDATION.md
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# 0.6.3 · DSH alpha.5 网页验收
|
|
2
|
+
|
|
3
|
+
宿主为公开 npm `@deepseek-ai/dsh@0.2.1-alpha.5`,Windows Node 24,系统 Chrome 无头浏览器,使用隔离 profile。
|
|
4
|
+
|
|
5
|
+
- 119 项自动测试通过。
|
|
6
|
+
- 实际打开推荐来源目录并搜索,预览嵌套 OPML 分类后明确导入;编辑名称和导出文件均读取同一份订阅配置。导入不会自动抓取全部来源。
|
|
7
|
+
- 英文标题曾在 390px 窗口的双列设置布局中被截断;修复后 RSS 所在设置页改用顶部换行导航,正文占用完整可用列,标题、按钮和下拉框可读。关闭 RSS 面板后该导航样式不影响其他设置页。
|
|
8
|
+
- 中文与英文 UI 操作通过,原始来源名称和用户分类保持原文;错误与空列表有明确提示。
|
|
9
|
+
|
|
10
|
+
本轮不重测真实外部网站,也不声称没有 VPN 的网络验收通过;网络可用性仍由用户的实际连接决定。原生桌面窗口未操作。
|
package/lib/client.js
CHANGED
|
@@ -1,6 +1,77 @@
|
|
|
1
1
|
// This is the browser source. tsc does not rebuild it; check with node --check.
|
|
2
2
|
window.__ModuleLoader__.load({ id: 'dsh-rss', factory: require => {
|
|
3
3
|
const React = require('react');
|
|
4
|
+
var __FR_KEY = "dsh-first-run:dsh-rss:v1";
|
|
5
|
+
var __FR_TITLE = "RSS 订阅 · 首次使用(3 步)";
|
|
6
|
+
var __FR_STEPS = [{"title":"先加一个源","body":"粘贴 RSS/Atom 地址,插件会先抓一次验证可用,再存进列表。"},{"title":"增量比全量好用","body":"抓取时选「增量」,只返回上次之后的新条目,适合定时巡检。"},{"title":"跨源搜索","body":"在对话里直接问关键词最近有没有更新,比逐个源翻快得多。","tryIt":"搜一下最近一周提到 DeepSeek 的订阅条目"}];
|
|
7
|
+
|
|
8
|
+
// ---- first-run guide (shared snippet; keep in sync across plugins) ----
|
|
9
|
+
var __FR_STYLE_ID = 'dsh-first-run-css';
|
|
10
|
+
var __FR_CSS = [
|
|
11
|
+
'.fr-card{border:1px solid var(--dsw-border,rgba(0,0,0,.10));border-radius:12px;padding:14px 16px;margin:0 0 14px;background:linear-gradient(180deg,rgba(59,130,246,.06),rgba(59,130,246,.02))}',
|
|
12
|
+
'.fr-head{display:flex;align-items:center;gap:8px;margin-bottom:6px}',
|
|
13
|
+
'.fr-dot{width:6px;height:6px;border-radius:50%;background:var(--dsw-accent,#3b82f6)}',
|
|
14
|
+
'.fr-title{font-weight:650;font-size:13.5px}',
|
|
15
|
+
'.fr-count{margin-left:auto;font-size:11px;color:var(--dsw-text-weak,#6b7280);font-variant-numeric:tabular-nums}',
|
|
16
|
+
'.fr-step{font-weight:600;font-size:13px;margin:2px 0 4px}',
|
|
17
|
+
'.fr-body{color:var(--dsw-text-weak,#4b5563);font-size:12.5px;line-height:1.65}',
|
|
18
|
+
'.fr-try{margin-top:8px;padding:7px 9px;border-radius:8px;background:rgba(59,130,246,.09);font-size:12px;display:flex;gap:8px}',
|
|
19
|
+
'.fr-try code{font-family:ui-monospace,monospace;white-space:pre-wrap;word-break:break-word}',
|
|
20
|
+
'.fr-bar{display:flex;gap:8px;align-items:center;margin-top:12px}',
|
|
21
|
+
'.fr-progress{display:flex;gap:4px;margin-right:auto}',
|
|
22
|
+
'.fr-seg{width:16px;height:3px;border-radius:2px;background:var(--dsw-border,rgba(0,0,0,.12))}',
|
|
23
|
+
'.fr-seg.on{background:var(--dsw-accent,#3b82f6)}',
|
|
24
|
+
'.fr-btn{border:1px solid var(--dsw-border,rgba(0,0,0,.14));background:transparent;color:inherit;border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer}',
|
|
25
|
+
'.fr-btn:hover{background:rgba(0,0,0,.04)}',
|
|
26
|
+
'.fr-btn.primary{background:var(--dsw-accent,#3b82f6);border-color:transparent;color:#fff}',
|
|
27
|
+
'.fr-btn.ghost{border-color:transparent;color:var(--dsw-text-weak,#6b7280)}',
|
|
28
|
+
].join('\n');
|
|
29
|
+
function __frEnsureCss() {
|
|
30
|
+
if (typeof document === 'undefined' || document.getElementById(__FR_STYLE_ID)) return;
|
|
31
|
+
var el = document.createElement('style');
|
|
32
|
+
el.id = __FR_STYLE_ID;
|
|
33
|
+
el.textContent = __FR_CSS;
|
|
34
|
+
document.head.appendChild(el);
|
|
35
|
+
}
|
|
36
|
+
function __frRead() { try { return localStorage.getItem(__FR_KEY) === 'done'; } catch (e) { return false; } }
|
|
37
|
+
function __frWrite() { try { localStorage.setItem(__FR_KEY, 'done'); } catch (e) {} }
|
|
38
|
+
function __FirstRunCard() {
|
|
39
|
+
var doneState = React.useState(__frRead);
|
|
40
|
+
var done = doneState[0], setDone = doneState[1];
|
|
41
|
+
var idxState = React.useState(0);
|
|
42
|
+
var index = idxState[0], setIndex = idxState[1];
|
|
43
|
+
React.useEffect(function () { __frEnsureCss(); }, []);
|
|
44
|
+
if (done) return null;
|
|
45
|
+
var step = __FR_STEPS[index];
|
|
46
|
+
var last = index >= __FR_STEPS.length - 1;
|
|
47
|
+
var close = function () { __frWrite(); setDone(true); };
|
|
48
|
+
var kids = [
|
|
49
|
+
React.createElement('div', { className: 'fr-head', key: 'h' },
|
|
50
|
+
React.createElement('span', { className: 'fr-dot' }),
|
|
51
|
+
React.createElement('span', { className: 'fr-title' }, __FR_TITLE),
|
|
52
|
+
React.createElement('span', { className: 'fr-count' }, (index + 1) + ' / ' + __FR_STEPS.length)),
|
|
53
|
+
React.createElement('div', { className: 'fr-step', key: 't' }, step.title),
|
|
54
|
+
React.createElement('div', { className: 'fr-body', key: 'b' }, step.body),
|
|
55
|
+
];
|
|
56
|
+
if (step.tryIt) kids.push(React.createElement('div', { className: 'fr-try', key: 'y' },
|
|
57
|
+
React.createElement('span', null, '可以直接说'),
|
|
58
|
+
React.createElement('code', null, '「' + step.tryIt + '」')));
|
|
59
|
+
var buttons = [React.createElement('div', { className: 'fr-progress', key: 'p' }, __FR_STEPS.map(function (_, i) {
|
|
60
|
+
return React.createElement('span', { key: i, className: 'fr-seg' + (i <= index ? ' on' : '') });
|
|
61
|
+
}))];
|
|
62
|
+
buttons.push(React.createElement('button', { className: 'fr-btn ghost', type: 'button', onClick: close, key: 's' }, '跳过'));
|
|
63
|
+
if (index > 0) buttons.push(React.createElement('button', { className: 'fr-btn', type: 'button', key: 'prev', onClick: function () { setIndex(index - 1); } }, '上一步'));
|
|
64
|
+
buttons.push(React.createElement('button', { className: 'fr-btn primary', type: 'button', key: 'next', onClick: function () { if (last) close(); else setIndex(index + 1); } }, last ? '开始使用' : '下一步'));
|
|
65
|
+
kids.push(React.createElement('div', { className: 'fr-bar', key: 'bar' }, buttons));
|
|
66
|
+
return React.createElement('div', { className: 'fr-card' }, kids);
|
|
67
|
+
}
|
|
68
|
+
function __withFirstRun(Component) {
|
|
69
|
+
return function FirstRunWrapped(props) {
|
|
70
|
+
return React.createElement(React.Fragment, null, React.createElement(__FirstRunCard, null), React.createElement(Component, props));
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// ---- end first-run guide ----
|
|
74
|
+
|
|
4
75
|
const { useState, useEffect, useRef, useId } = React;
|
|
5
76
|
const h = React.createElement;
|
|
6
77
|
const ROUTE = '_dsh/dsh-rss/subscriptions';
|
|
@@ -50,14 +121,14 @@ window.__ModuleLoader__.load({ id: 'dsh-rss', factory: require => {
|
|
|
50
121
|
}
|
|
51
122
|
|
|
52
123
|
const CSS = `
|
|
53
|
-
.dshr{color:var(--dsw-alias-label-primary,#202737);font-size:14px;line-height:1.55;max-width:920px;min-width:0}
|
|
124
|
+
.dshr{color:var(--dsw-alias-label-primary,#202737);font-size:14px;line-height:1.55;max-width:920px;min-width:0;overflow-wrap:anywhere}
|
|
54
125
|
.dshr *,.dshr-dialog *{box-sizing:border-box}.dshr h2{font-size:22px;margin:0 0 6px}.dshr p{margin:0 0 12px}
|
|
55
126
|
.dshr-muted{color:var(--dsw-alias-label-tertiary,#657083);font-size:13px}.dshr-toolbar{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0}
|
|
56
127
|
.dshr button,.dshr-dialog button{font:inherit;min-height:38px;padding:7px 12px;border:1px solid var(--dsw-alias-border-l2,#d9dee7);border-radius:9px;color:inherit;background:var(--dsw-alias-bg-layer-1,#fff);cursor:pointer}
|
|
57
128
|
.dshr button:disabled,.dshr-dialog button:disabled{opacity:.5;cursor:not-allowed}.dshr button:hover:not(:disabled),.dshr-dialog button:hover:not(:disabled){border-color:#5685d4}
|
|
58
129
|
.dshr .primary,.dshr-dialog .primary{background:#366fd2;color:#fff;border-color:#366fd2}.dshr-dialog .danger{background:#b93838;border-color:#b93838;color:#fff}
|
|
59
130
|
.dshr button:focus-visible,.dshr-dialog button:focus-visible,.dshr input:focus-visible,.dshr select:focus-visible,.dshr-dialog input:focus-visible,.dshr-dialog textarea:focus-visible{outline:2px solid #5685d4;outline-offset:2px}
|
|
60
|
-
.dshr-filters{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:16px}.dshr-filters label:first-child{flex:1 1 220px}.dshr label,.dshr-dialog label{display:flex;flex-direction:column;gap:5px;font-size:13px}
|
|
131
|
+
.dshr-filters{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:16px}.dshr-filters label:first-child{flex:1 1 220px}.dshr label,.dshr-dialog label{display:flex;flex-direction:column;gap:5px;font-size:13px;min-width:0;max-width:100%}
|
|
61
132
|
.dshr input,.dshr select,.dshr-dialog input,.dshr-dialog textarea{font:inherit;font-size:16px;width:100%;min-width:0;min-height:40px;border:1px solid var(--dsw-alias-border-l2,#d9dee7);border-radius:8px;padding:8px 10px;background:var(--dsw-alias-bg-layer-1,#fff);color:inherit}
|
|
62
133
|
.dshr-list{display:grid;gap:10px;margin:0;padding:0;list-style:none}.dshr-card{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#d9dee7);border-radius:12px;padding:14px;display:flex;flex-wrap:wrap;gap:12px}
|
|
63
134
|
.dshr-info{flex:1 1 240px;min-width:0}.dshr-info h3{font-size:15px;margin:0 0 4px;overflow-wrap:anywhere}.dshr-url{font-size:12px;overflow-wrap:anywhere;color:var(--dsw-alias-label-tertiary,#657083)}
|
|
@@ -72,6 +143,13 @@ window.__ModuleLoader__.load({ id: 'dsh-rss', factory: require => {
|
|
|
72
143
|
.dshr-catalog{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;list-style:none;padding:0;margin:14px 0}.dshr-source{border:1px solid var(--dsw-alias-border-l2,#d9dee7);border-radius:12px;padding:14px;min-width:0;align-self:start}.dshr-source h4{font-size:16px;margin:0 0 6px;overflow-wrap:anywhere}.dshr-source p{margin:8px 0}.dshr-source .dshr-actions{margin-top:12px}.dshr-source.is-selected{border-color:#5685d4}.dshr-source details{margin-top:8px;font-size:12px}.dshr-source summary{cursor:pointer}.dshr-source .dshr-preview{margin-top:12px;font-size:13px}
|
|
73
144
|
@media(max-width:640px){.dshr-catalog{grid-template-columns:1fr}.dshr-dialog .dshr-filters label{flex-basis:100%}}
|
|
74
145
|
@media(max-width:520px){.dshr-dialog{padding:16px}.dshr-actions{width:100%}.dshr-filters label{width:100%}.dshr-toolbar button{flex:1 1 auto}.dshr-card{padding:12px}}
|
|
146
|
+
@media(max-width:640px){
|
|
147
|
+
[role=dialog]:has(.dshr){width:100%!important;max-width:100%!important;flex-direction:column!important}
|
|
148
|
+
[role=dialog]:has(.dshr)>nav+div{min-width:0;width:100%}
|
|
149
|
+
[role=dialog]:has(.dshr)>nav+div>div:has(.dshr){padding:12px!important;min-width:0}
|
|
150
|
+
[role=dialog]:has(.dshr)>nav{width:100%!important;flex-basis:auto!important;border-right:0;max-height:170px;overflow:auto}
|
|
151
|
+
[role=dialog]:has(.dshr)>nav>div:has(>button){display:flex;flex-direction:row!important;flex-wrap:wrap;gap:4px}
|
|
152
|
+
}
|
|
75
153
|
`;
|
|
76
154
|
async function request(action, args, signal) {
|
|
77
155
|
const response = await fetch(new URL(ROUTE, document.baseURI), action ? {
|
|
@@ -264,7 +342,7 @@ window.__ModuleLoader__.load({ id: 'dsh-rss', factory: require => {
|
|
|
264
342
|
let lang = language;
|
|
265
343
|
try { const service = ctx.get ? ctx.get('locale') : ctx.locale; if (service?.getSnapshot) lang = String(service.getSnapshot().active || '').startsWith('en') ? 'en' : 'zh'; } catch {}
|
|
266
344
|
return words.title[lang === 'en' ? 1 : 0];
|
|
267
|
-
}, inject: () => ({}) }, Panel));
|
|
345
|
+
}, inject: () => ({}) }, __withFirstRun(Panel)));
|
|
268
346
|
}
|
|
269
347
|
return { apply, inject: ['slots', 'locale'] };
|
|
270
348
|
} });
|