@linxin666/dsh-ssh 0.3.9 → 0.3.11
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/lib/client.js +59 -11
- package/lib/client.js.map +1 -1
- package/lib/types/client/mount.d.ts +4 -1
- package/lib/types/client/panel/helpers.d.ts +2 -0
- package/lib/types/client/sidebar-entry-core.d.ts +10 -0
- package/lib/types/client/sidebar-entry.d.ts +7 -1
- package/package.json +14 -14
- package/src/client/index.ts +8 -2
- package/src/client/mount.tsx +11 -1
- package/src/client/panel/helpers.ts +14 -0
- package/src/client/panel/panel.module.css +4 -2
- package/src/client/sidebar-entry-core.ts +33 -7
- package/src/client/sidebar-entry.ts +7 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SshApi } from './api.ts';
|
|
2
2
|
import type { PanelController } from './panel/controller.ts';
|
|
3
3
|
import type { TerminalFontSource } from './panel/helpers.ts';
|
|
4
|
+
import type { LocaleRefreshSource } from './sidebar-entry.ts';
|
|
4
5
|
/** The injected panel container (kept in the DOM, hidden when inactive). */
|
|
5
6
|
export declare const PANEL_VIEW_SELECTOR = "[data-dsh-ssh-view]";
|
|
6
7
|
/**
|
|
@@ -9,6 +10,8 @@ export declare const PANEL_VIEW_SELECTOR = "[data-dsh-ssh-view]";
|
|
|
9
10
|
* @param controller - the panel controller driving the view.
|
|
10
11
|
* @param api - the SSH API client the tabs operate through.
|
|
11
12
|
* @param terminalFont - live terminal-font setting source (issue #577).
|
|
13
|
+
* @param locale - locale-change source; when given, re-renders an open panel
|
|
14
|
+
* on a Language switch.
|
|
12
15
|
* @returns disposer unmounting the tree and restoring the column.
|
|
13
16
|
*/
|
|
14
|
-
export declare function mountPanel(controller: PanelController, api: SshApi, terminalFont?: TerminalFontSource): () => void;
|
|
17
|
+
export declare function mountPanel(controller: PanelController, api: SshApi, terminalFont?: TerminalFontSource, locale?: LocaleRefreshSource): () => void;
|
|
@@ -8,6 +8,8 @@ import { type SshKey } from '../locales.ts';
|
|
|
8
8
|
export type TranslateValues = Record<string, string | number>;
|
|
9
9
|
/** Active dictionary, picked by the document language at call time. */
|
|
10
10
|
export declare function dictionary(): Record<string, string>;
|
|
11
|
+
/** Wire the SDK translate seat; pass undefined to restore the document-language pick. */
|
|
12
|
+
export declare function setRuntimeTranslate(t: ((key: SshKey, values?: TranslateValues) => string) | undefined): void;
|
|
11
13
|
/** Translate a key with optional {name} template params (current language). */
|
|
12
14
|
export declare function tt(key: SshKey, values?: TranslateValues): string;
|
|
13
15
|
/** Human-readable error text from an unknown thrown value. */
|
|
@@ -34,6 +34,16 @@ export interface SidebarEntryOptions {
|
|
|
34
34
|
label(): string;
|
|
35
35
|
/** Optional localized tooltip (title attribute). */
|
|
36
36
|
tooltip?(): string;
|
|
37
|
+
/**
|
|
38
|
+
* Optional locale-change subscription: re-applies label / aria-label /
|
|
39
|
+
* tooltip whenever the active locale changes. Plain-DOM rows would
|
|
40
|
+
* otherwise keep the label captured at mount; pass the SDK locale runtime
|
|
41
|
+
* subscription (ctx.locale.subscribe) so the row follows the language
|
|
42
|
+
* switch without a reload.
|
|
43
|
+
*/
|
|
44
|
+
refresh?: {
|
|
45
|
+
subscribe(listener: () => void): () => void;
|
|
46
|
+
};
|
|
37
47
|
/** Click action (open/toggle the owning panel). */
|
|
38
48
|
onToggle(): void;
|
|
39
49
|
/** Family-block position: 'before' inserts ahead of sibling plugin rows, 'after' behind them. */
|
|
@@ -11,10 +11,16 @@
|
|
|
11
11
|
import type { PanelController } from './panel/controller.ts';
|
|
12
12
|
/** Stable data attribute identifying the injected entry row. */
|
|
13
13
|
export declare const ENTRY_SELECTOR = "[data-dsh-ssh-entry]";
|
|
14
|
+
/** Locale-change subscription the shared core asks for (ctx.locale.subscribe shape). */
|
|
15
|
+
export interface LocaleRefreshSource {
|
|
16
|
+
subscribe(listener: () => void): () => void;
|
|
17
|
+
}
|
|
14
18
|
/**
|
|
15
19
|
* Mount the sidebar entry, waiting for the shell to render and self-healing
|
|
16
20
|
* on later React re-renders.
|
|
17
21
|
* @param controller - the panel controller the entry toggles.
|
|
22
|
+
* @param locale - locale-change source; when given, re-applies the label on
|
|
23
|
+
* a Language switch (the plain-DOM row otherwise keeps the mount-time copy).
|
|
18
24
|
* @returns disposer removing the entry and its observers.
|
|
19
25
|
*/
|
|
20
|
-
export declare function mountSidebarEntry(controller: PanelController): () => void;
|
|
26
|
+
export declare function mountSidebarEntry(controller: PanelController, locale?: LocaleRefreshSource): () => void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxin666/dsh-ssh",
|
|
3
3
|
"description": "Remote SSH operations for the dsh web GUI: host config store (~/.dsh/dsh-ssh.json, import from ~/.ssh/config), a persistent ssh2 connection pool with jump-host support, exec / PTY web terminal / SFTP transfer / local port-forward tunnels / cluster execution, plus agent tools (ssh_list, ssh_exec, ssh_upload, ssh_download, ssh_tunnel, ssh_cluster). Hot-pluggable — mounted via ~/.dsh/cordis.patch.yml + a profile node_modules symlink, no dsh source changes.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.11",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dsh": {
|
|
28
28
|
"engines": {
|
|
29
|
-
"dsh": ">=0.1.2-alpha.
|
|
29
|
+
"dsh": ">=0.1.2-alpha.3"
|
|
30
30
|
},
|
|
31
31
|
"bundle": {
|
|
32
32
|
"patch": "./cordis.patch.yml"
|
|
@@ -52,18 +52,18 @@
|
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
55
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.
|
|
56
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.
|
|
57
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.
|
|
58
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.
|
|
59
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.
|
|
60
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.
|
|
61
|
-
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.
|
|
62
|
-
"@deepseek-ai/dsh-scope": "^0.1.2-alpha.
|
|
63
|
-
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.
|
|
64
|
-
"@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.
|
|
65
|
-
"@deepseek-ai/dsh-timeout": "^0.1.2-alpha.
|
|
66
|
-
"@deepseek-ai/dsh-tools": "^0.1.2-alpha.
|
|
55
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.3",
|
|
56
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.3",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.3",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.3",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.3",
|
|
60
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.3",
|
|
61
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.3",
|
|
62
|
+
"@deepseek-ai/dsh-scope": "^0.1.2-alpha.3",
|
|
63
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.3",
|
|
64
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.3",
|
|
65
|
+
"@deepseek-ai/dsh-timeout": "^0.1.2-alpha.3",
|
|
66
|
+
"@deepseek-ai/dsh-tools": "^0.1.2-alpha.3",
|
|
67
67
|
"@types/node": "^22.20.0",
|
|
68
68
|
"@types/react": "~18.3.1",
|
|
69
69
|
"@types/react-dom": "^18.3.5",
|
package/src/client/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { en, zh, type SshKey } from './locales.ts'
|
|
|
25
25
|
import { mountPanel } from './mount.tsx'
|
|
26
26
|
import { PanelController } from './panel/controller.ts'
|
|
27
27
|
import type { TerminalFontSource } from './panel/helpers.ts'
|
|
28
|
+
import { setRuntimeTranslate } from './panel/helpers.ts'
|
|
28
29
|
import { mountSidebarEntry } from './sidebar-entry.ts'
|
|
29
30
|
import { reportDailyHeartbeat } from './telemetry.ts'
|
|
30
31
|
|
|
@@ -89,6 +90,11 @@ export function apply(ctx: ClientContext): void {
|
|
|
89
90
|
}
|
|
90
91
|
}, 'dsh-ssh: dictionaries')
|
|
91
92
|
|
|
93
|
+
// Wire the SDK translate seat into the module-level tt (sidebar row and
|
|
94
|
+
// other plain-DOM callers): reads the active locale at call time, so they
|
|
95
|
+
// follow the Language setting without a reload.
|
|
96
|
+
try { setRuntimeTranslate(ctx.locale.bind(NS)) } catch { /* locale missing: document-language fallback stays */ }
|
|
97
|
+
|
|
92
98
|
const controller = new PanelController()
|
|
93
99
|
const api = new SshApi()
|
|
94
100
|
// Live terminal-font preference (issue #577): the settings namespace is
|
|
@@ -105,8 +111,8 @@ export function apply(ctx: ClientContext): void {
|
|
|
105
111
|
}
|
|
106
112
|
const disposers: Array<() => void> = []
|
|
107
113
|
try {
|
|
108
|
-
disposers.push(mountSidebarEntry(controller))
|
|
109
|
-
disposers.push(mountPanel(controller, api, terminalFont))
|
|
114
|
+
disposers.push(mountSidebarEntry(controller, ctx.locale))
|
|
115
|
+
disposers.push(mountPanel(controller, api, terminalFont, ctx.locale))
|
|
110
116
|
} catch (error) {
|
|
111
117
|
// DOM failures degrade the panel, never the GUI.
|
|
112
118
|
console.warn('[dsh-ssh] mount failed:', error)
|
package/src/client/mount.tsx
CHANGED
|
@@ -16,6 +16,7 @@ import type { SshApi } from './api.ts'
|
|
|
16
16
|
import type { PanelController } from './panel/controller.ts'
|
|
17
17
|
import type { TerminalFontSource } from './panel/helpers.ts'
|
|
18
18
|
import { SshPanel } from './panel/SshPanel.tsx'
|
|
19
|
+
import type { LocaleRefreshSource } from './sidebar-entry.ts'
|
|
19
20
|
import css from './panel/panel.module.css'
|
|
20
21
|
|
|
21
22
|
/** The injected panel container (kept in the DOM, hidden when inactive). */
|
|
@@ -40,11 +41,19 @@ function conversationColumn(): HTMLElement | undefined {
|
|
|
40
41
|
* @param controller - the panel controller driving the view.
|
|
41
42
|
* @param api - the SSH API client the tabs operate through.
|
|
42
43
|
* @param terminalFont - live terminal-font setting source (issue #577).
|
|
44
|
+
* @param locale - locale-change source; when given, re-renders an open panel
|
|
45
|
+
* on a Language switch.
|
|
43
46
|
* @returns disposer unmounting the tree and restoring the column.
|
|
44
47
|
*/
|
|
45
|
-
export function mountPanel(controller: PanelController, api: SshApi, terminalFont?: TerminalFontSource): () => void {
|
|
48
|
+
export function mountPanel(controller: PanelController, api: SshApi, terminalFont?: TerminalFontSource, locale?: LocaleRefreshSource): () => void {
|
|
46
49
|
let root: Root | undefined
|
|
47
50
|
let container: HTMLDivElement | undefined
|
|
51
|
+
let unsubscribeLocale: (() => void) | undefined
|
|
52
|
+
try {
|
|
53
|
+
unsubscribeLocale = locale?.subscribe(() => {
|
|
54
|
+
if (root !== undefined) root.render(<SshPanel controller={controller} api={api} terminalFont={terminalFont} />)
|
|
55
|
+
})
|
|
56
|
+
} catch { /* locale service absent: the panel follows its next natural re-render */ }
|
|
48
57
|
|
|
49
58
|
const ensure = (): void => {
|
|
50
59
|
if (container !== undefined) {
|
|
@@ -110,6 +119,7 @@ export function mountPanel(controller: PanelController, api: SshApi, terminalFon
|
|
|
110
119
|
document.removeEventListener(ACTIVATE_EVENT, onOtherActivate)
|
|
111
120
|
waitObserver.disconnect()
|
|
112
121
|
unsubscribe()
|
|
122
|
+
unsubscribeLocale?.()
|
|
113
123
|
document.documentElement.removeAttribute(ACTIVE_ATTR)
|
|
114
124
|
root?.unmount()
|
|
115
125
|
root = undefined
|
|
@@ -14,8 +14,22 @@ export function dictionary(): Record<string, string> {
|
|
|
14
14
|
return lang.toLowerCase().startsWith('en') ? { ...en } : { ...zh }
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* SDK translate seat wired by the browser apply() once ctx.locale is bound
|
|
19
|
+
* (setRuntimeTranslate): reads the ACTIVE locale at call time, so plain-DOM
|
|
20
|
+
* surfaces follow a runtime language switch. The document-language pick stays
|
|
21
|
+
* as the unwired fallback (locale service absent, module-scope early callers).
|
|
22
|
+
*/
|
|
23
|
+
let runtimeT: ((key: SshKey, values?: TranslateValues) => string) | undefined
|
|
24
|
+
|
|
25
|
+
/** Wire the SDK translate seat; pass undefined to restore the document-language pick. */
|
|
26
|
+
export function setRuntimeTranslate(t: ((key: SshKey, values?: TranslateValues) => string) | undefined): void {
|
|
27
|
+
runtimeT = t
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
/** Translate a key with optional {name} template params (current language). */
|
|
18
31
|
export function tt(key: SshKey, values?: TranslateValues): string {
|
|
32
|
+
if (runtimeT !== undefined) return runtimeT(key, values)
|
|
19
33
|
return t(dictionary(), key, values)
|
|
20
34
|
}
|
|
21
35
|
|
|
@@ -97,7 +97,8 @@ html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [class*='centerCol']
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/* Collapsed rail: icon-only, centered, matching the shell's 56px rail. */
|
|
100
|
-
[data-dsh-frame][data-sidebar-collapsed] .entry
|
|
100
|
+
:global([data-dsh-frame][data-sidebar-collapsed]) .entry,
|
|
101
|
+
:global([data-sidebar-collapsed]) .entry {
|
|
101
102
|
justify-content: center;
|
|
102
103
|
padding: 0;
|
|
103
104
|
width: 36px;
|
|
@@ -106,7 +107,8 @@ html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [class*='centerCol']
|
|
|
106
107
|
border-radius: 50%;
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
[data-dsh-frame][data-sidebar-collapsed] .entryLabel
|
|
110
|
+
:global([data-dsh-frame][data-sidebar-collapsed]) .entryLabel,
|
|
111
|
+
:global([data-sidebar-collapsed]) .entryLabel {
|
|
110
112
|
display: none;
|
|
111
113
|
}
|
|
112
114
|
|
|
@@ -36,6 +36,14 @@ export interface SidebarEntryOptions {
|
|
|
36
36
|
label(): string
|
|
37
37
|
/** Optional localized tooltip (title attribute). */
|
|
38
38
|
tooltip?(): string
|
|
39
|
+
/**
|
|
40
|
+
* Optional locale-change subscription: re-applies label / aria-label /
|
|
41
|
+
* tooltip whenever the active locale changes. Plain-DOM rows would
|
|
42
|
+
* otherwise keep the label captured at mount; pass the SDK locale runtime
|
|
43
|
+
* subscription (ctx.locale.subscribe) so the row follows the language
|
|
44
|
+
* switch without a reload.
|
|
45
|
+
*/
|
|
46
|
+
refresh?: { subscribe(listener: () => void): () => void }
|
|
39
47
|
/** Click action (open/toggle the owning panel). */
|
|
40
48
|
onToggle(): void
|
|
41
49
|
/** Family-block position: 'before' inserts ahead of sibling plugin rows, 'after' behind them. */
|
|
@@ -76,7 +84,7 @@ function newSessionButton(root: HTMLElement): HTMLButtonElement | undefined {
|
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
/** Build the entry row (a detached button; insert once the shell is up). */
|
|
79
|
-
function createEntry(options: SidebarEntryOptions): HTMLButtonElement {
|
|
87
|
+
function createEntry(options: SidebarEntryOptions): { entry: HTMLButtonElement; applyLabel: () => void } {
|
|
80
88
|
const entry = document.createElement('button')
|
|
81
89
|
entry.type = 'button'
|
|
82
90
|
entry.setAttribute(options.rowAttribute, '')
|
|
@@ -85,12 +93,20 @@ function createEntry(options: SidebarEntryOptions): HTMLButtonElement {
|
|
|
85
93
|
entry.setAttribute('data-dsh-part', 'sidebar-entry')
|
|
86
94
|
}
|
|
87
95
|
entry.className = options.css['entry'] ?? ''
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
const labelSpan = document.createElement('span')
|
|
97
|
+
labelSpan.className = options.css['entryLabel'] ?? ''
|
|
98
|
+
const iconSpan = document.createElement('span')
|
|
99
|
+
iconSpan.className = options.css['entryIcon'] ?? ''
|
|
100
|
+
iconSpan.innerHTML = options.icon
|
|
101
|
+
entry.append(iconSpan, labelSpan)
|
|
102
|
+
const applyLabel = (): void => {
|
|
103
|
+
entry.setAttribute('aria-label', options.label())
|
|
104
|
+
if (options.tooltip !== undefined) entry.setAttribute('title', options.tooltip())
|
|
105
|
+
labelSpan.textContent = options.label()
|
|
106
|
+
}
|
|
107
|
+
applyLabel()
|
|
92
108
|
entry.addEventListener('click', options.onToggle)
|
|
93
|
-
return entry
|
|
109
|
+
return { entry, applyLabel }
|
|
94
110
|
}
|
|
95
111
|
|
|
96
112
|
/** Re-insert the entry after the New Session row (before the browser region). */
|
|
@@ -132,9 +148,18 @@ export function mountSidebarEntry(options: SidebarEntryOptions): () => void {
|
|
|
132
148
|
if (typeof document !== 'undefined' && document.querySelector(options.rowSelector) !== null) {
|
|
133
149
|
return () => {}
|
|
134
150
|
}
|
|
135
|
-
const entry = createEntry(options)
|
|
151
|
+
const { entry, applyLabel } = createEntry(options)
|
|
136
152
|
let root: HTMLElement | undefined
|
|
137
153
|
let placed = false
|
|
154
|
+
let unsubscribeRefresh: (() => void) | undefined
|
|
155
|
+
if (options.refresh !== undefined) {
|
|
156
|
+
try {
|
|
157
|
+
unsubscribeRefresh = options.refresh.subscribe(applyLabel)
|
|
158
|
+
} catch {
|
|
159
|
+
// A throwing subscription must not break the mount; the label stays at
|
|
160
|
+
// its initial value and the next reload resolves it again.
|
|
161
|
+
}
|
|
162
|
+
}
|
|
138
163
|
|
|
139
164
|
const tryPlace = (): void => {
|
|
140
165
|
if (root !== undefined && !root.isConnected) {
|
|
@@ -202,6 +227,7 @@ export function mountSidebarEntry(options: SidebarEntryOptions): () => void {
|
|
|
202
227
|
return () => {
|
|
203
228
|
waitObserver.disconnect()
|
|
204
229
|
rootObserver.disconnect()
|
|
230
|
+
unsubscribeRefresh?.()
|
|
205
231
|
unsubscribeActive?.()
|
|
206
232
|
entry.remove()
|
|
207
233
|
}
|
|
@@ -19,13 +19,18 @@ export const ENTRY_SELECTOR = '[data-dsh-ssh-entry]'
|
|
|
19
19
|
/** Inline terminal glyph sized to the shell's current sidebar navigation icons. */
|
|
20
20
|
const ICON = '<svg viewBox="0 0 16 16" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="1.75" y="2.25" width="12.5" height="11.5" rx="1.75"/><path d="M4.25 5.25l2.75 2.75-2.75 2.75"/><path d="M8.5 10.75h3.25"/></svg>'
|
|
21
21
|
|
|
22
|
+
/** Locale-change subscription the shared core asks for (ctx.locale.subscribe shape). */
|
|
23
|
+
export interface LocaleRefreshSource { subscribe(listener: () => void): () => void }
|
|
24
|
+
|
|
22
25
|
/**
|
|
23
26
|
* Mount the sidebar entry, waiting for the shell to render and self-healing
|
|
24
27
|
* on later React re-renders.
|
|
25
28
|
* @param controller - the panel controller the entry toggles.
|
|
29
|
+
* @param locale - locale-change source; when given, re-applies the label on
|
|
30
|
+
* a Language switch (the plain-DOM row otherwise keeps the mount-time copy).
|
|
26
31
|
* @returns disposer removing the entry and its observers.
|
|
27
32
|
*/
|
|
28
|
-
export function mountSidebarEntry(controller: PanelController): () => void {
|
|
33
|
+
export function mountSidebarEntry(controller: PanelController, locale?: LocaleRefreshSource): () => void {
|
|
29
34
|
return mountSharedSidebarEntry({
|
|
30
35
|
rowAttribute: 'data-dsh-ssh-entry',
|
|
31
36
|
rowSelector: ENTRY_SELECTOR,
|
|
@@ -34,6 +39,7 @@ export function mountSidebarEntry(controller: PanelController): () => void {
|
|
|
34
39
|
css,
|
|
35
40
|
label: () => tt('entry.label'),
|
|
36
41
|
tooltip: () => tt('entry.tooltip'),
|
|
42
|
+
refresh: locale === undefined ? undefined : { subscribe: (listener) => locale.subscribe(listener) },
|
|
37
43
|
onToggle: () => { controller.toggle() },
|
|
38
44
|
position: 'after',
|
|
39
45
|
familySelectors: ['[data-dsh-taskboard-entry]', '[data-dsh-ssh-entry]'],
|