@goodandready/dsh-task-tracker 0.2.13 → 0.2.14

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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.14] - 2026-11-15
4
+
5
+ ### Changed
6
+ - **Navigation Panel Integration**: moved Tasks entry from the top new session bar (`.hHd-Xa_newSessionRow`) into the standard left navigation panel list (`<nav class="hHd-Xa_panelList">`) via slot `sidebar.panellist` with order 25 and `TaskPanelIcon`, matching sibling items («Плагины», «Задачи автоматизации») (#82).
7
+ - **Central Main View Slot**: Tasks view is now mounted into the central content area via slot `main` (`key: "dsh-task-tracker"`), allowing seamless switching between tasks and chat sessions without state loss (#82).
8
+
9
+ ### Fixed
10
+ - **Cordis Context Injection**: layout service synchronization resolved asynchronously with `ctx.inject([ layout], ...)` and safe property fallbacks, preventing undeclared service proxy exceptions (#82).
11
+
3
12
  ## [0.2.13] - 2026-11-14
4
13
 
5
14
  ### Changed
package/README.md CHANGED
File without changes
package/README.ru.md CHANGED
File without changes
package/README.zh.md CHANGED
File without changes
package/cordis.patch.yml CHANGED
File without changes
package/lib/client.js CHANGED
@@ -86,6 +86,7 @@ function notifyDataChanged() {
86
86
  'html[data-dsh-task-tracker-open] [data-dsh-task-tracker-view]{display:flex!important}' +
87
87
  'html[data-dsh-task-tracker-open] [data-pane=conversation]>:not([data-dsh-task-tracker-view]){display:none!important}' +
88
88
  'html[data-dsh-task-tracker-open] [class*=centerCol]>:not([data-dsh-task-tracker-view]){display:none!important}' +
89
+ '.tt-main-page{width:100%;height:100%;min-height:0;display:flex;flex-direction:column;flex:1;background:var(--dsw-alias-bg-base);position:relative;overflow:hidden}' +
89
90
  '.tt-screen-container{display:flex;flex-direction:column;width:100%;height:100%;min-height:0;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);position:relative;overflow:hidden}' +
90
91
  '.tt-screen-head{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);flex:none}' +
91
92
  '.tt-screen-title{font-size:16px;font-weight:600;display:flex;align-items:center;gap:8px}' +
@@ -1214,6 +1215,7 @@ function notifyDataChanged() {
1214
1215
 
1215
1216
  const ensure = () => {
1216
1217
  if (container !== null) return
1218
+ if (document.querySelector('.tt-main-page')) return
1217
1219
  const column = document.querySelector(COLUMN_SELECTOR)
1218
1220
  if (column === null) return
1219
1221
 
@@ -1278,71 +1280,41 @@ function notifyDataChanged() {
1278
1280
  }
1279
1281
  }
1280
1282
 
1281
- function mountSidebarEntry(toggle, label) {
1282
- if (typeof document === 'undefined') return () => {}
1283
-
1284
- let entry = null
1285
- let observer = null
1286
-
1287
- const tryPlace = () => {
1288
- if (document.querySelector('[data-dsh-part="task-tracker-sidebar"]')) {
1289
- if (observer) {
1290
- observer.disconnect()
1291
- observer = null
1292
- }
1293
- return
1294
- }
1295
- const nodes = document.querySelectorAll('button[class*="newSession"]')
1296
- let target = null
1297
- for (const n of nodes) {
1298
- if (String(n.className).indexOf('newSessionLabel') === -1) {
1299
- target = n
1300
- break
1301
- }
1302
- }
1303
- if (!target) return
1304
-
1305
- entry = target.cloneNode(true)
1306
- entry.className = String(target.className) + ' tt-sidebar-entry'
1307
- entry.setAttribute('data-dsh-plugin', 'dsh-task-tracker')
1308
- entry.setAttribute('data-dsh-part', 'task-tracker-sidebar')
1309
- entry.setAttribute('aria-label', label)
1310
- entry.removeAttribute('id')
1311
- entry.innerHTML = ''
1312
-
1313
- const icon = document.createElement('span')
1314
- icon.innerHTML = ICON_TASK
1315
- entry.appendChild(icon)
1316
-
1317
- const text = document.createElement('span')
1318
- text.style.marginLeft = '8px'
1319
- text.textContent = label
1320
- entry.appendChild(text)
1321
-
1322
- entry.addEventListener('click', (e) => {
1323
- e.preventDefault()
1324
- e.stopPropagation()
1325
- toggle.toggle()
1326
- })
1327
-
1328
- target.insertAdjacentElement('afterend', entry)
1329
- if (observer) {
1330
- observer.disconnect()
1331
- observer = null
1332
- }
1333
- }
1334
-
1335
- observer = new MutationObserver(tryPlace)
1336
- observer.observe(document.body, { childList: true, subtree: true })
1337
- tryPlace()
1283
+ function TaskPanelIcon(props) {
1284
+ const size = (props && props.size) || 16
1285
+ return h(
1286
+ 'svg',
1287
+ {
1288
+ 'data-dsh-panel-entry': PANEL_ID,
1289
+ viewBox: '0 0 24 24',
1290
+ width: size,
1291
+ height: size,
1292
+ fill: 'none',
1293
+ stroke: 'currentColor',
1294
+ strokeWidth: 2,
1295
+ strokeLinecap: 'round',
1296
+ strokeLinejoin: 'round',
1297
+ 'aria-hidden': 'true',
1298
+ },
1299
+ h('polyline', { points: '9 11 12 14 22 4' }),
1300
+ h('path', { d: 'M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11' }),
1301
+ )
1302
+ }
1338
1303
 
1339
- return () => {
1340
- if (observer) {
1341
- observer.disconnect()
1342
- observer = null
1343
- }
1344
- if (entry && entry.remove) entry.remove()
1345
- }
1304
+ function TasksMainPage(props) {
1305
+ return h(
1306
+ 'div',
1307
+ {
1308
+ className: 'tt-main-page',
1309
+ 'data-dsh-task-tracker-view': '',
1310
+ 'data-dsh-plugin': 'dsh-task-tracker',
1311
+ },
1312
+ h(TasksScreen, {
1313
+ ctx: props.ctx,
1314
+ toggle: props.toggle,
1315
+ isMainPage: true,
1316
+ }),
1317
+ )
1346
1318
  }
1347
1319
 
1348
1320
  function PluginCard(props) {
@@ -1623,6 +1595,7 @@ function notifyDataChanged() {
1623
1595
 
1624
1596
  module.exports.inject = ['slots', 'locale', 'configForms']
1625
1597
  module.exports.apply = function apply(ctx) {
1598
+ try {
1626
1599
  if (ctx.locale && typeof ctx.locale.register === 'function') {
1627
1600
  const registerLocale = () =>
1628
1601
  ctx.locale.register(NS, {
@@ -1852,8 +1825,129 @@ function notifyDataChanged() {
1852
1825
  }
1853
1826
 
1854
1827
  const toggle = createToggle()
1828
+ const t = (ctx.locale && typeof ctx.locale.bind === 'function') ? ctx.locale.bind(NS) : (k, fb) => fb || k
1829
+
1830
+ let layoutService = null
1831
+ const bindLayout = () => {
1832
+ if (!layoutService) return
1833
+ if (layoutService.panelInfo && typeof layoutService.panelInfo.subscribe === 'function') {
1834
+ const syncFromLayout = () => {
1835
+ const activeId = layoutService.panelInfo.getSnapshot?.()?.activePanelId
1836
+ toggle.set(activeId === PANEL_ID)
1837
+ }
1838
+ if (typeof ctx.effect === 'function') {
1839
+ ctx.effect(() => layoutService.panelInfo.subscribe(syncFromLayout), 'dsh-task-tracker: layout sync')
1840
+ } else {
1841
+ layoutService.panelInfo.subscribe(syncFromLayout)
1842
+ }
1843
+ }
1844
+ }
1845
+
1846
+ if (typeof ctx.inject === 'function') {
1847
+ try {
1848
+ ctx.inject(['layout'], (scoped) => {
1849
+ if (scoped && scoped.layout) {
1850
+ layoutService = scoped.layout
1851
+ bindLayout()
1852
+ }
1853
+ })
1854
+ } catch (_) {
1855
+ /* bestEffort: optional layout service */
1856
+ }
1857
+ } else if (typeof ctx.get === 'function') {
1858
+ try {
1859
+ layoutService = ctx.get('layout') || null
1860
+ if (layoutService) bindLayout()
1861
+ } catch (_) {
1862
+ /* bestEffort: optional layout service */
1863
+ }
1864
+ } else {
1865
+ try {
1866
+ layoutService = ctx.layout || null
1867
+ if (layoutService) bindLayout()
1868
+ } catch (_) {
1869
+ /* bestEffort: optional layout service */
1870
+ }
1871
+ }
1872
+
1873
+ toggle.subscribe((open) => {
1874
+ if (layoutService && typeof layoutService.selectPanel === 'function') {
1875
+ const activeId = layoutService.panelInfo?.getSnapshot?.()?.activePanelId
1876
+ if (open && activeId !== PANEL_ID) {
1877
+ layoutService.selectPanel(PANEL_ID)
1878
+ } else if (!open && activeId === PANEL_ID) {
1879
+ layoutService.selectPanel(null)
1880
+ }
1881
+ }
1882
+ })
1883
+
1884
+ if (ctx.slots && (typeof ctx.slots.register === 'function' || typeof ctx.slots.inject === 'function')) {
1885
+ const injectNavRow = () => {
1886
+ if (typeof ctx.slots.inject === 'function') {
1887
+ return ctx.slots.inject('sidebar.panellist', () => {
1888
+ if (typeof ctx.slots.register === 'function') {
1889
+ const off = ctx.slots.register(
1890
+ {
1891
+ name: 'sidebar.panellist',
1892
+ id: PANEL_ID,
1893
+ order: 25,
1894
+ label: () => (t ? t('tasks.title', 'Tasks') : 'Tasks'),
1895
+ locale: NS,
1896
+ },
1897
+ TaskPanelIcon,
1898
+ )
1899
+ return off
1900
+ }
1901
+ return () => {}
1902
+ })
1903
+ }
1904
+ if (typeof ctx.slots.register === 'function') {
1905
+ return ctx.slots.register(
1906
+ {
1907
+ name: 'sidebar.panellist',
1908
+ id: PANEL_ID,
1909
+ order: 25,
1910
+ label: () => (t ? t('tasks.title', 'Tasks') : 'Tasks'),
1911
+ locale: NS,
1912
+ },
1913
+ TaskPanelIcon,
1914
+ )
1915
+ }
1916
+ return () => {}
1917
+ }
1918
+
1919
+ const injectMainPage = () => {
1920
+ if (typeof ctx.slots.inject === 'function') {
1921
+ return ctx.slots.inject('main', () => {
1922
+ if (typeof ctx.slots.register === 'function') {
1923
+ const off = ctx.slots.register(
1924
+ {
1925
+ name: 'main',
1926
+ key: PANEL_ID,
1927
+ locale: NS,
1928
+ inject: () => ({ ctx, toggle }),
1929
+ },
1930
+ TasksMainPage,
1931
+ )
1932
+ return off
1933
+ }
1934
+ return () => {}
1935
+ })
1936
+ }
1937
+ if (typeof ctx.slots.register === 'function') {
1938
+ return ctx.slots.register(
1939
+ {
1940
+ name: 'main',
1941
+ key: PANEL_ID,
1942
+ locale: NS,
1943
+ inject: () => ({ ctx, toggle }),
1944
+ },
1945
+ TasksMainPage,
1946
+ )
1947
+ }
1948
+ return () => {}
1949
+ }
1855
1950
 
1856
- if (ctx.slots && typeof ctx.slots.inject === 'function') {
1857
1951
  const injectChip = () =>
1858
1952
  ctx.slots.inject('conversation.session.header.utilities', () =>
1859
1953
  ctx.slots.register(
@@ -1869,15 +1963,18 @@ function notifyDataChanged() {
1869
1963
  )
1870
1964
 
1871
1965
  if (typeof ctx.effect === 'function') {
1966
+ ctx.effect(injectNavRow, 'dsh-task-tracker: sidebar nav entry')
1967
+ ctx.effect(injectMainPage, 'dsh-task-tracker: main page')
1872
1968
  ctx.effect(injectChip, 'dsh-task-tracker: chip')
1873
1969
  } else {
1970
+ injectNavRow()
1971
+ injectMainPage()
1874
1972
  injectChip()
1875
1973
  }
1876
1974
  }
1877
1975
 
1878
1976
  const mountOverlay = () => {
1879
1977
  const off = [
1880
- mountSidebarEntry(toggle, 'Tasks'),
1881
1978
  mountTasksScreen(ctx, toggle),
1882
1979
  ]
1883
1980
  return () => {
@@ -1894,6 +1991,10 @@ function notifyDataChanged() {
1894
1991
  } else {
1895
1992
  mountOverlay()
1896
1993
  }
1994
+ } catch (applyErr) {
1995
+ console.error('[dsh-task-tracker] Apply error:', applyErr)
1996
+ throw applyErr
1997
+ }
1897
1998
  }
1898
1999
 
1899
2000
  return module.exports
package/lib/index.js CHANGED
File without changes
package/lib/routes.js CHANGED
File without changes
package/lib/storage.js CHANGED
File without changes
package/lib/updater.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-task-tracker",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "Автономный встроенный Todo-инбокс для Ди: локальное JSON-хранилище tasks.json, тулы todo_add / todo_list / todo_done, REST API webServer и полноэкранный интерфейс TasksScreen.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -27,7 +27,9 @@
27
27
  "inject": [
28
28
  "@deepseek-ai/dsh-client-ui-slots",
29
29
  "@deepseek-ai/dsh-client-locale",
30
- "@deepseek-ai/dsh-client-ui-settings"
30
+ "@deepseek-ai/dsh-client-ui-settings",
31
+ "@deepseek-ai/dsh-client-ui-sidebar",
32
+ "@deepseek-ai/dsh-client-ui-layout"
31
33
  ]
32
34
  }
33
35
  },