dsh-workspace-session-filter 1.0.4 → 1.0.6
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 +9 -4
- package/client.js +132 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
1. **侧边栏底部的筛选条** —— 设置按钮上方单独一行,`运行中` / `待确认` / `未读` 三个
|
|
8
8
|
chip(各自带命中数)。点一下向上弹出按工作区分组的会话列表,点行直接进会话,是日常主入口;
|
|
9
|
-
2.
|
|
9
|
+
2. 侧边栏面板列表里的「会话筛选」图标(**默认不显示**)—— 在
|
|
10
|
+
`设置 → 插件 → 会话筛选` 里打开开关后出现,指向中间栏的完整视图,顶部是
|
|
10
11
|
`全部 / 运行中 / 等待确认 / 未读 / 更多` 筛选器,`更多` 里是 `最近一天 / 最近三天 /
|
|
11
12
|
最近一周 / 最近一个月`(外加 `不限时间`)。
|
|
12
13
|
|
|
@@ -80,11 +81,12 @@ dsh plugin --profile desktop remove dsh-workspace-session-filter
|
|
|
80
81
|
装好后:
|
|
81
82
|
|
|
82
83
|
- 侧边栏**底部**(设置按钮上方、单独一行)出现 `运行中 N` / `待确认 N` / `未读 N` 筛选条;
|
|
83
|
-
-
|
|
84
|
+
- 面板列表里**不会**自动多出图标;需要完整视图时,到
|
|
85
|
+
`设置 → 插件 → 会话筛选` 打开「在左侧导航显示」,图标才出现(开关状态存本地,刷新后保留)。
|
|
84
86
|
|
|
85
87
|
卸载:`plugin_manager({ action: "remove_bundle", target: "dsh-workspace-session-filter" })`。
|
|
86
88
|
|
|
87
|
-
> 包名 `dsh-workspace-session-filter` 与仓库同名。截至 1.0.
|
|
89
|
+
> 包名 `dsh-workspace-session-filter` 与仓库同名。截至 1.0.6,npm registry、
|
|
88
90
|
> GitHub、release tarball、本地目录四种来源都能装;registry 上是 `npm publish` 发出的
|
|
89
91
|
> 正式版本,`repository` 字段指回本仓库。
|
|
90
92
|
|
|
@@ -92,7 +94,7 @@ dsh plugin --profile desktop remove dsh-workspace-session-filter
|
|
|
92
94
|
|
|
93
95
|
| 文件 | 作用 |
|
|
94
96
|
| --- | --- |
|
|
95
|
-
| `client.js` | 全部界面逻辑:字典、图标、底部筛选条
|
|
97
|
+
| `client.js` | 全部界面逻辑:字典、图标、底部筛选条 / 面板 / 设置页三处注册 + 按开关动态注册的侧边栏图标、内联样式 |
|
|
96
98
|
| `index.js` | Host 半边(本插件不需要 Host 能力,留空) |
|
|
97
99
|
| `cordis.patch.yml` | 把 `session-filter` 条目插入 profile 组合 |
|
|
98
100
|
| `package.json` | 包元信息 + `dsh.client` / `dsh.bundle` 声明 |
|
|
@@ -104,6 +106,9 @@ dsh plugin --profile desktop remove dsh-workspace-session-filter
|
|
|
104
106
|
- **面板落位**:`sidebar.panellist` 的 `id` 与 `main` 的 `key` 复用同一个字符串
|
|
105
107
|
`session-filter`,侧边栏图标因此直接指向本面板;`order: 30` 排在插件、定时任务、
|
|
106
108
|
上下文之后。
|
|
109
|
+
- **导航图标可关**:注册在 `设置 → 插件` 的 `settings.plugins.tab` 页面只放一个开关;
|
|
110
|
+
偏好存 `localStorage`(键 `dsh-workspace-session-filter:nav.v1`,默认关),
|
|
111
|
+
开关变化时**注销/重建** `sidebar.panellist` 注册,因此关掉时不会留下一个空图标位。
|
|
107
112
|
- **筛选条落位**:注册在 `sidebar.footer.action`(设置按钮上方的一行槽位),整条占满宽度;
|
|
108
113
|
弹层用 `position: fixed` 贴在 chip 上方(`bottom: innerHeight - rect.top + 8`),因为侧边栏
|
|
109
114
|
容器 `overflow` 会裁掉绝对定位的浮层。宽版渲染三个 chip,窄条(`wide: false`)渲染一个
|
package/client.js
CHANGED
|
@@ -31,6 +31,8 @@ window.__ModuleLoader__.load({
|
|
|
31
31
|
'filter.awaiting': '等待确认',
|
|
32
32
|
'filter.awaitingShort': '待确认',
|
|
33
33
|
'filter.unread': '未读',
|
|
34
|
+
'settings.nav': '在左侧导航显示',
|
|
35
|
+
'settings.navHint': '打开后左侧导航多一个「会话筛选」入口,可打开完整筛选面板;关闭时侧边栏底部的筛选条照常可用。',
|
|
34
36
|
'filter.more': '更多',
|
|
35
37
|
'filter.reset': '清除筛选',
|
|
36
38
|
'filter.aria': '会话筛选条件',
|
|
@@ -70,6 +72,8 @@ window.__ModuleLoader__.load({
|
|
|
70
72
|
'filter.awaiting': 'Awaiting confirmation',
|
|
71
73
|
'filter.awaitingShort': 'Awaiting',
|
|
72
74
|
'filter.unread': 'Unread',
|
|
75
|
+
'settings.nav': 'Show in the left navigation',
|
|
76
|
+
'settings.navHint': 'Adds a Session filter entry to the left navigation that opens the full panel. The filter bar at the foot of the sidebar keeps working either way.',
|
|
73
77
|
'filter.more': 'More',
|
|
74
78
|
'filter.reset': 'Clear filters',
|
|
75
79
|
'filter.aria': 'Session filters',
|
|
@@ -232,6 +236,55 @@ window.__ModuleLoader__.load({
|
|
|
232
236
|
}
|
|
233
237
|
}
|
|
234
238
|
|
|
239
|
+
/* ---------------------------- navigation toggle --------------------------- *
|
|
240
|
+
* The left-nav icon is opt-in: the sidebar already carries four panel cells and
|
|
241
|
+
* most visits start at the sidebar-foot bar. The preference shares the same
|
|
242
|
+
* localStorage store as the seen stamps, and the registration follows it, so a
|
|
243
|
+
* hidden entry leaves no empty cell behind the panel list.
|
|
244
|
+
* ------------------------------------------------------------------ */
|
|
245
|
+
const NAV_KEY = 'dsh-workspace-session-filter:nav.v1'
|
|
246
|
+
const navState = { value: null, listeners: new Set() }
|
|
247
|
+
|
|
248
|
+
function navStorage() {
|
|
249
|
+
try {
|
|
250
|
+
return typeof localStorage === 'undefined' ? null : localStorage
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return null
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function navVisible() {
|
|
257
|
+
if (navState.value !== null) return navState.value
|
|
258
|
+
const store = navStorage()
|
|
259
|
+
let value = false
|
|
260
|
+
if (store) {
|
|
261
|
+
try { value = store.getItem(NAV_KEY) === '1' } catch (error) { value = false }
|
|
262
|
+
}
|
|
263
|
+
navState.value = value
|
|
264
|
+
return value
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function setNavVisible(next) {
|
|
268
|
+
const value = next === true
|
|
269
|
+
if (navVisible() === value) return
|
|
270
|
+
navState.value = value
|
|
271
|
+
const store = navStorage()
|
|
272
|
+
if (store) {
|
|
273
|
+
try {
|
|
274
|
+
if (value) store.setItem(NAV_KEY, '1')
|
|
275
|
+
else store.removeItem(NAV_KEY)
|
|
276
|
+
} catch (error) { /* storage may be full or blocked */ }
|
|
277
|
+
}
|
|
278
|
+
for (const listener of Array.from(navState.listeners)) {
|
|
279
|
+
try { listener(value) } catch (error) { console.error('[workspace-session-filter] nav listener failed', error) }
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function subscribeNav(listener) {
|
|
284
|
+
navState.listeners.add(listener)
|
|
285
|
+
return () => { navState.listeners.delete(listener) }
|
|
286
|
+
}
|
|
287
|
+
|
|
235
288
|
function loadSeen() {
|
|
236
289
|
if (seenCache.loaded) return seenCache.map
|
|
237
290
|
seenCache.loaded = true
|
|
@@ -811,6 +864,39 @@ window.__ModuleLoader__.load({
|
|
|
811
864
|
return h('div', { ref: rootRef, className: 'dwsf-foot' }, popover, filters)
|
|
812
865
|
}
|
|
813
866
|
|
|
867
|
+
/**
|
|
868
|
+
* The plugin's own page inside Settings → Plugins. One switch owns the
|
|
869
|
+
* optional left-nav entry; the sidebar-foot filter bar is unaffected.
|
|
870
|
+
*/
|
|
871
|
+
function SettingsPage(props) {
|
|
872
|
+
const t = props.t
|
|
873
|
+
const [on, setOn] = useState(navVisible)
|
|
874
|
+
|
|
875
|
+
useEffect(() => subscribeNav((value) => setOn(value)), [])
|
|
876
|
+
|
|
877
|
+
return h('div', { className: 'dwsf-settings' },
|
|
878
|
+
h('div', { className: 'dwsf-settingRow' },
|
|
879
|
+
h('div', { className: 'dwsf-settingCopy' },
|
|
880
|
+
h('span', { className: 'dwsf-settingLabel' }, t('settings.nav')),
|
|
881
|
+
h('span', { className: 'dwsf-settingHint' }, t('settings.navHint'))
|
|
882
|
+
),
|
|
883
|
+
h('button', {
|
|
884
|
+
type: 'button',
|
|
885
|
+
role: 'switch',
|
|
886
|
+
className: 'dwsf-switch',
|
|
887
|
+
'aria-checked': on ? 'true' : 'false',
|
|
888
|
+
'aria-label': t('settings.nav'),
|
|
889
|
+
'data-on': on ? 'true' : 'false',
|
|
890
|
+
onClick: () => setNavVisible(!on),
|
|
891
|
+
},
|
|
892
|
+
h('span', { className: 'dwsf-switchTrack', 'data-on': on ? 'true' : 'false', 'aria-hidden': 'true' },
|
|
893
|
+
h('span', { className: 'dwsf-switchThumb' })
|
|
894
|
+
)
|
|
895
|
+
)
|
|
896
|
+
)
|
|
897
|
+
)
|
|
898
|
+
}
|
|
899
|
+
|
|
814
900
|
/** Sidebar entry: guards the standard hooks and keeps a crash out of the sidebar. */
|
|
815
901
|
function FooterEntry(props) {
|
|
816
902
|
const ready = typeof props.useSessions === 'function'
|
|
@@ -906,7 +992,7 @@ window.__ModuleLoader__.load({
|
|
|
906
992
|
.dwsf-emptyHint{color:var(--dsw-alias-label-caption);font-size:13px;line-height:20px}
|
|
907
993
|
[data-slot="sidebar.footer.action"]{display:flex !important;flex-wrap:wrap;width:100%;min-width:0;align-items:flex-start;column-gap:0;row-gap:0}
|
|
908
994
|
.dwsf-foot{flex:1 0 100%;display:flex;align-items:center;min-width:0;max-width:100%;margin:8px 0 2px;padding:0 2px}
|
|
909
|
-
.dwsf-footChips{display:flex;flex-wrap:wrap;align-items:center;justify-content:
|
|
995
|
+
.dwsf-footChips{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:4px 2px;min-width:0;width:100%}
|
|
910
996
|
.dwsf-footChip{flex:0 1 auto;min-width:0;max-width:100%;height:28px;display:inline-flex;align-items:center;justify-content:center;gap:3px;padding:0 7px;border:0;border-radius:999px;background:0 0;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:12px;line-height:18px;white-space:nowrap;cursor:pointer;overflow:hidden}
|
|
911
997
|
.dwsf-footChip:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
912
998
|
.dwsf-footChip[data-active="true"]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
@@ -919,6 +1005,17 @@ window.__ModuleLoader__.load({
|
|
|
919
1005
|
.dwsf-footRailButton:hover,.dwsf-footRailButton[data-active="true"]{background:var(--dsw-alias-interactive-bg-hover)}
|
|
920
1006
|
.dwsf-footRailButton:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:-2px}
|
|
921
1007
|
.dwsf-footBadge{position:absolute;top:4px;right:2px;min-width:15px;height:15px;padding:0 3px;border-radius:999px;background:var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-label-primary-inverted);font-size:9px;line-height:15px;text-align:center;font-variant-numeric:tabular-nums}
|
|
1008
|
+
.dwsf-settings{display:flex;flex-direction:column;gap:8px;color:var(--dsw-alias-label-primary)}
|
|
1009
|
+
.dwsf-settingRow{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:12px 14px;border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1)}
|
|
1010
|
+
.dwsf-settingCopy{display:flex;flex-direction:column;gap:2px;min-width:0}
|
|
1011
|
+
.dwsf-settingLabel{font-size:13px;line-height:20px}
|
|
1012
|
+
.dwsf-settingHint{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
|
|
1013
|
+
.dwsf-switch{flex:none;margin-top:1px;padding:0;border:0;background:0 0;cursor:pointer;border-radius:999px;line-height:0}
|
|
1014
|
+
.dwsf-switch:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}
|
|
1015
|
+
.dwsf-switchTrack{display:inline-flex;align-items:center;width:34px;height:20px;padding:2px;border-radius:999px;background:var(--dsw-alias-bg-layer-2);box-shadow:inset 0 0 0 .5px var(--dsw-alias-border-l2);transition:background .16s ease,box-shadow .16s ease}
|
|
1016
|
+
.dwsf-switchTrack[data-on="true"]{background:var(--dsw-alias-brand-primary);box-shadow:none}
|
|
1017
|
+
.dwsf-switchThumb{width:16px;height:16px;border-radius:999px;background:var(--dsw-alias-bg-base);transition:transform .16s ease}
|
|
1018
|
+
.dwsf-switchTrack[data-on="true"] .dwsf-switchThumb{transform:translateX(14px)}
|
|
922
1019
|
.dwsf-pop{position:fixed;z-index:60;display:flex;flex-direction:column;width:min(340px,calc(100vw - 24px));max-height:60vh;border-radius:12px;background:var(--dsw-specific-menu);backdrop-filter:var(--dsw-menu-backdrop-filter);--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);box-shadow:var(--dsw-elevation-prominent);overflow:hidden}
|
|
923
1020
|
.dwsf-popHead{flex:none;display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:44px;padding:10px 12px 4px}
|
|
924
1021
|
.dwsf-popTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:20px}
|
|
@@ -986,12 +1083,43 @@ window.__ModuleLoader__.load({
|
|
|
986
1083
|
inject: () => ({ t, onOpen: openSession }),
|
|
987
1084
|
}, FooterEntry)), 'workspace-session-filter: sidebar footer')
|
|
988
1085
|
|
|
989
|
-
ctx.effect(() => ctx.slots.inject('
|
|
990
|
-
name: '
|
|
1086
|
+
ctx.effect(() => ctx.slots.inject('settings.plugins.tab', () => ctx.slots.register({
|
|
1087
|
+
name: 'settings.plugins.tab',
|
|
991
1088
|
id: PANEL_ID,
|
|
992
1089
|
order: 30,
|
|
993
1090
|
label: () => t('panel.label'),
|
|
994
|
-
|
|
1091
|
+
inject: () => ({ t }),
|
|
1092
|
+
}, SettingsPage)), 'workspace-session-filter: settings page')
|
|
1093
|
+
|
|
1094
|
+
/* The nav entry is opt-in, so its registration is created and dropped while
|
|
1095
|
+
* the page runs instead of being decided once at apply time. */
|
|
1096
|
+
ctx.effect(() => {
|
|
1097
|
+
let dispose = null
|
|
1098
|
+
const sync = () => {
|
|
1099
|
+
if (navVisible() && !dispose) {
|
|
1100
|
+
dispose = ctx.slots.inject('sidebar.panellist', () => ctx.slots.register({
|
|
1101
|
+
name: 'sidebar.panellist',
|
|
1102
|
+
id: PANEL_ID,
|
|
1103
|
+
order: 30,
|
|
1104
|
+
label: () => t('panel.label'),
|
|
1105
|
+
}, PanelGlyph))
|
|
1106
|
+
return
|
|
1107
|
+
}
|
|
1108
|
+
if (!navVisible() && dispose) {
|
|
1109
|
+
dispose()
|
|
1110
|
+
dispose = null
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
const off = subscribeNav(sync)
|
|
1114
|
+
sync()
|
|
1115
|
+
return () => {
|
|
1116
|
+
off()
|
|
1117
|
+
if (dispose) {
|
|
1118
|
+
dispose()
|
|
1119
|
+
dispose = null
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}, 'workspace-session-filter: nav icon')
|
|
995
1123
|
},
|
|
996
1124
|
}
|
|
997
1125
|
},
|