@goodandready/dsh-cron 0.1.1 → 0.1.3

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.
Files changed (2) hide show
  1. package/lib/client.js +177 -78
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -1,4 +1,3 @@
1
- // Браузерная половина @goodandready/dsh-cron
2
1
  window.__ModuleLoader__.load({
3
2
  id: '@goodandready/dsh-cron',
4
3
  factory: (require) => {
@@ -16,6 +15,7 @@ window.__ModuleLoader__.load({
16
15
  const COLUMN_SELECTOR = '[data-pane=conversation], [class*=centerCol]';
17
16
  const SESSION_ROW_SELECTOR = '[class*="sessionRow"], [class*="projectRow"], [class*="searchResultRow"], [class*="newSession"]';
18
17
 
18
+ const BACK_ICON = '<svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 3.5 5.5 8l4.5 4.5"/></svg>';
19
19
  const ICON_TIMER = '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>';
20
20
  const ICON_PLAY = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 21"></polygon></svg>';
21
21
  const ICON_PAUSE = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>';
@@ -26,12 +26,25 @@ window.__ModuleLoader__.load({
26
26
  const ICON_ACTIVITY = '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="#10b981" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>';
27
27
 
28
28
  const STYLES = `
29
- html[${ACTIVE_ATTR}] [data-dsh-cron-view] { display: flex !important; }
30
- html[${ACTIVE_ATTR}] [data-pane=conversation]>:not([data-dsh-cron-view]),
31
- html[${ACTIVE_ATTR}] [class*=centerCol]>:not([data-dsh-cron-view]) { display: none !important; }
32
-
33
- .dsh-cron-overlay { position: absolute; inset: 0; display: flex; flex-direction: column; background: var(--dsw-alias-bg-layer-2, #171717); color: var(--dsw-alias-label-primary, #ededed); overflow-y: auto; padding: 40px 48px; z-index: 100; }
29
+ [data-pane=conversation],[class*=centerCol]{position:relative;}
30
+ [data-dsh-cron-view]{z-index:60;background:var(--dsw-alias-bg-base,#171717);display:none;position:absolute;inset:0;}
31
+ html[${ACTIVE_ATTR}]:not([data-dsh-ssh-active]) [data-dsh-cron-view]{display:block !important;}
32
+ html[${ACTIVE_ATTR}]:not([data-dsh-ssh-active]) [data-pane=conversation]>:not([data-dsh-cron-view]),
33
+ html[${ACTIVE_ATTR}]:not([data-dsh-ssh-active]) [class*=centerCol]>:not([data-dsh-cron-view]){display:none !important;}
34
+
35
+ .dsh-cron-entry-clone { align-items: center; gap: 8px; display: flex; }
36
+ .dsh-cron-entry-clone[data-active] { font-weight: 600; }
37
+ .dsh-cron-entry-icon { flex: none; justify-content: center; align-items: center; display: inline-flex; }
38
+ .dsh-cron-entry-label { text-overflow: ellipsis; overflow: hidden; }
39
+ [data-dsh-frame][data-sidebar-collapsed] .dsh-cron-entry-label { display: none; }
40
+
41
+ .dsh-cron-overlay { position: absolute; inset: 0; display: flex; flex-direction: column; background: var(--dsw-alias-bg-base, #171717); color: var(--dsw-alias-label-primary, #ededed); overflow-y: auto; padding: 32px 48px; z-index: 100; box-sizing: border-box; }
34
42
  .dsh-cron-container { max-width: 860px; width: 100%; margin: 0 auto; }
43
+
44
+ .dsh-cron-top-bar { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
45
+ .dsh-cron-back-btn { appearance: none; font: inherit; cursor: pointer; color: var(--dsw-alias-label-secondary, #9ca3af); background: 0 0; border: 1px solid var(--dsw-alias-border-l2, #383838); border-radius: 8px; align-items: center; gap: 6px; padding: 4px 12px; font-size: 13px; display: inline-flex; flex: none; height: 32px; box-sizing: border-box; }
46
+ .dsh-cron-back-btn:hover { color: var(--dsw-alias-label-primary, #ededed); background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06)); }
47
+
35
48
  .dsh-cron-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 24px; }
36
49
  .dsh-cron-title { font-size: 24px; font-weight: 600; letter-spacing: -0.02em; margin-bottom: 6px; }
37
50
  .dsh-cron-subtitle { font-size: 14px; color: var(--dsw-alias-label-secondary, #9ca3af); }
@@ -41,7 +54,7 @@ window.__ModuleLoader__.load({
41
54
  .dsh-cron-dropdown-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 12px; background: transparent; border: none; border-radius: 8px; color: #ededed; font-size: 13.5px; text-align: left; cursor: pointer; }
42
55
  .dsh-cron-dropdown-item:hover { background: var(--dsw-alias-bg-layer-4, #2a2a2a); }
43
56
  .dsh-cron-search-bar { position: relative; margin-bottom: 20px; }
44
- .dsh-cron-search-input { width: 100%; background: var(--dsw-alias-bg-layer-3, #1f1f1f); border: 1px solid var(--dsw-alias-border-l2, #333); border-radius: 10px; padding: 10px 14px 10px 38px; color: inherit; font-size: 14px; outline: none; }
57
+ .dsh-cron-search-input { width: 100%; background: var(--dsw-alias-bg-layer-3, #1f1f1f); border: 1px solid var(--dsw-alias-border-l2, #333); border-radius: 10px; padding: 10px 14px 10px 38px; color: inherit; font-size: 14px; outline: none; box-sizing: border-box; }
45
58
  .dsh-cron-search-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: #666; }
46
59
  .dsh-cron-tabs { display: flex; gap: 8px; margin-bottom: 24px; }
47
60
  .dsh-cron-tab { appearance: none; background: transparent; border: none; padding: 6px 14px; border-radius: 9999px; color: #9ca3af; font-size: 13.5px; cursor: pointer; }
@@ -49,39 +62,45 @@ window.__ModuleLoader__.load({
49
62
  .dsh-cron-task-list { display: flex; flex-direction: column; gap: 12px; margin-bottom: 40px; }
50
63
  .dsh-cron-task-item { display: flex; align-items: center; gap: 16px; padding: 14px 18px; background: var(--dsw-alias-bg-layer-3, #1e1e1e); border: 1px solid var(--dsw-alias-border-l2, #2e2e2e); border-radius: 12px; }
51
64
  .dsh-cron-task-item:hover { border-color: #444; }
52
- .dsh-cron-task-status-btn { width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--dsw-alias-bg-layer-4, #2a2a2a); color: #9ca3af; border: 1px solid #333; cursor: pointer; flex-shrink: 0; }
53
- .dsh-cron-task-status-btn[data-active="true"] { color: #10b981; border-color: rgba(16,185,129,0.3); }
65
+ .dsh-cron-task-status-btn { background: none; border: 1px solid #444; border-radius: 50%; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; color: #888; cursor: pointer; flex-shrink: 0; }
66
+ .dsh-cron-task-status-btn[data-active="true"] { color: #10b981; border-color: #10b981; }
54
67
  .dsh-cron-task-info { flex: 1; min-width: 0; }
55
- .dsh-cron-task-title { font-size: 14.5px; font-weight: 500; margin-bottom: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
56
- .dsh-cron-task-sched { font-size: 12.5px; color: #9ca3af; }
57
- .dsh-cron-task-actions { display: flex; align-items: center; gap: 8px; }
58
- .dsh-cron-icon-action { appearance: none; background: transparent; border: 1px solid transparent; border-radius: 8px; padding: 6px 10px; color: #9ca3af; font-size: 12px; cursor: pointer; }
59
- .dsh-cron-icon-action:hover { background: #2a2a2a; color: #fff; }
60
- .dsh-cron-rec-section { margin-top: 32px; }
61
- .dsh-cron-rec-title { font-size: 15px; font-weight: 600; margin-bottom: 16px; }
62
- .dsh-cron-rec-list { display: flex; flex-direction: column; gap: 12px; }
63
- .dsh-cron-rec-item { display: flex; align-items: flex-start; gap: 14px; padding: 14px 18px; background: var(--dsw-alias-bg-layer-3, #1e1e1e); border: 1px solid #2e2e2e; border-radius: 12px; cursor: pointer; }
64
- .dsh-cron-rec-item:hover { border-color: #444; background: #232323; }
65
- .dsh-cron-rec-item-title { font-size: 14px; font-weight: 500; margin-bottom: 4px; }
66
- .dsh-cron-rec-item-sched { font-size: 12.5px; color: #9ca3af; margin-left: 6px; font-weight: normal; }
67
- .dsh-cron-rec-item-desc { font-size: 13px; color: #888; line-height: 1.4; }
68
- .dsh-cron-modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; z-index: 500; }
69
- .dsh-cron-modal { width: 480px; background: var(--dsw-alias-bg-layer-3, #212121); border: 1px solid #383838; border-radius: 14px; padding: 24px; box-shadow: 0 20px 40px rgba(0,0,0,0.6); }
70
- .dsh-cron-modal-title { font-size: 18px; font-weight: 600; margin-bottom: 16px; }
71
- .dsh-cron-field { margin-bottom: 14px; }
72
- .dsh-cron-label { display: block; font-size: 12.5px; color: #aaa; margin-bottom: 6px; }
73
- .dsh-cron-input, .dsh-cron-textarea { width: 100%; background: #2a2a2a; border: 1px solid #383838; border-radius: 8px; padding: 8px 12px; color: inherit; font-size: 13.5px; outline: none; }
74
- .dsh-cron-textarea { height: 80px; resize: vertical; }
68
+ .dsh-cron-task-title { font-size: 15px; font-weight: 500; margin-bottom: 3px; }
69
+ .dsh-cron-task-sched { font-size: 13px; color: #9ca3af; }
70
+ .dsh-cron-task-actions { display: flex; gap: 8px; }
71
+ .dsh-cron-icon-btn { background: transparent; border: 1px solid #333; border-radius: 8px; padding: 6px 12px; font-size: 12.5px; color: #aaa; cursor: pointer; }
72
+ .dsh-cron-icon-btn:hover { background: #2a2a2a; color: #fff; }
73
+ .dsh-cron-recs-title { font-size: 17px; font-weight: 600; margin-bottom: 14px; }
74
+ .dsh-cron-recs-list { display: flex; flex-direction: column; gap: 10px; }
75
+ .dsh-cron-rec-card { display: flex; align-items: center; gap: 16px; padding: 16px 20px; background: var(--dsw-alias-bg-layer-3, #1c1c1c); border: 1px solid var(--dsw-alias-border-l1, #282828); border-radius: 12px; cursor: pointer; transition: background 0.15s; }
76
+ .dsh-cron-rec-card:hover { background: var(--dsw-alias-bg-layer-4, #242424); border-color: #383838; }
77
+ .dsh-cron-rec-icon { width: 36px; height: 36px; border-radius: 10px; background: rgba(255,255,255,0.03); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
78
+ .dsh-cron-rec-content { flex: 1; min-width: 0; }
79
+ .dsh-cron-rec-title { font-size: 14.5px; font-weight: 500; margin-bottom: 4px; }
80
+ .dsh-cron-rec-time { font-size: 13px; color: #9ca3af; font-weight: normal; margin-left: 8px; }
81
+ .dsh-cron-rec-desc { font-size: 13px; color: #71717a; }
82
+
83
+ .dsh-cron-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; z-index: 1000; }
84
+ .dsh-cron-modal { background: var(--dsw-alias-bg-layer-2, #1f1f1f); border: 1px solid var(--dsw-alias-border-l2, #333); border-radius: 16px; width: 100%; max-width: 520px; padding: 24px; box-shadow: 0 20px 40px rgba(0,0,0,0.6); }
85
+ .dsh-cron-modal-title { font-size: 18px; font-weight: 600; margin-bottom: 18px; }
86
+ .dsh-cron-form-group { margin-bottom: 16px; }
87
+ .dsh-cron-form-group label { display: block; font-size: 13px; color: #aaa; margin-bottom: 6px; }
88
+ .dsh-cron-form-group input, .dsh-cron-form-group textarea { width: 100%; background: #141414; border: 1px solid #333; border-radius: 8px; padding: 9px 12px; color: #fff; font-size: 13.5px; outline: none; box-sizing: border-box; }
89
+ .dsh-cron-form-group textarea { min-height: 80px; resize: vertical; }
75
90
  .dsh-cron-modal-foot { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
76
91
  .dsh-cron-btn-primary { background: #ededed; color: #111; border: none; border-radius: 8px; padding: 7px 16px; font-size: 13.5px; font-weight: 500; cursor: pointer; }
77
92
  .dsh-cron-btn-secondary { background: transparent; color: #999; border: 1px solid #383838; border-radius: 8px; padding: 7px 16px; font-size: 13.5px; cursor: pointer; }
78
93
  `;
79
94
 
80
- if (typeof document !== 'undefined' && !document.getElementById('dsh-cron-styles')) {
81
- const styleEl = document.createElement('style');
82
- styleEl.id = 'dsh-cron-styles';
83
- styleEl.textContent = STYLES;
84
- document.head.appendChild(styleEl);
95
+ function ensureStyles() {
96
+ if (typeof document === 'undefined') return;
97
+ let el = document.getElementById('dsh-cron-styles');
98
+ if (!el) {
99
+ el = document.createElement('style');
100
+ el.id = 'dsh-cron-styles';
101
+ document.head.appendChild(el);
102
+ }
103
+ el.textContent = STYLES;
85
104
  }
86
105
 
87
106
  function createToggle() {
@@ -89,14 +108,24 @@ window.__ModuleLoader__.load({
89
108
  const listeners = new Set();
90
109
  return {
91
110
  isOpen: () => open,
92
- set: (v) => { open = !!v; for (const fn of listeners) fn(open); },
93
- toggle: () => { open = !open; for (const fn of listeners) fn(open); },
94
- subscribe: (fn) => { listeners.add(fn); return () => listeners.delete(fn); }
111
+ set: (v) => {
112
+ if (open === v) return;
113
+ open = !!v;
114
+ for (const fn of listeners) fn(open);
115
+ },
116
+ toggle: () => {
117
+ open = !open;
118
+ for (const fn of listeners) fn(open);
119
+ },
120
+ subscribe: (fn) => {
121
+ listeners.add(fn);
122
+ return () => listeners.delete(fn);
123
+ }
95
124
  };
96
125
  }
97
126
 
98
127
  function CronScreen(props) {
99
- const { toggle } = props;
128
+ const { toggle, onClose } = props;
100
129
  const [tab, setTab] = React.useState('all');
101
130
  const [query, setQuery] = React.useState('');
102
131
  const [tasks, setTasks] = React.useState([]);
@@ -107,6 +136,17 @@ window.__ModuleLoader__.load({
107
136
  const [formSchedule, setFormSchedule] = React.useState('');
108
137
  const [formPrompt, setFormPrompt] = React.useState('');
109
138
 
139
+ React.useEffect(() => {
140
+ const onKey = (e) => {
141
+ if (e.key !== 'Escape') return;
142
+ if (modalOpen) { setModalOpen(false); return; }
143
+ if (dropdownOpen) { setDropdownOpen(false); return; }
144
+ if (onClose) onClose();
145
+ };
146
+ document.addEventListener('keydown', onKey);
147
+ return () => document.removeEventListener('keydown', onKey);
148
+ }, [modalOpen, dropdownOpen, onClose]);
149
+
110
150
  const fetchTasks = async () => {
111
151
  try {
112
152
  const res = await fetch('/dsh-cron/tasks?status=' + tab + '&query=' + encodeURIComponent(query));
@@ -138,7 +178,7 @@ window.__ModuleLoader__.load({
138
178
  const handleDelete = async (id) => {
139
179
  if (!confirm('Удалить эту задачу?')) return;
140
180
  try {
141
- await fetch('/dsh-cron/action/' + id + '/delete', { method: 'DELETE' });
181
+ await fetch('/dsh-cron/action/' + id + '/delete', { method: 'POST' });
142
182
  fetchTasks();
143
183
  } catch (err) {}
144
184
  };
@@ -160,11 +200,14 @@ window.__ModuleLoader__.load({
160
200
  const handleSave = async (e) => {
161
201
  e.preventDefault();
162
202
  try {
163
- const payload = { title: formTitle, schedule: formSchedule, prompt: formPrompt };
164
203
  const res = await fetch('/dsh-cron/tasks', {
165
204
  method: 'POST',
166
205
  headers: { 'Content-Type': 'application/json' },
167
- body: JSON.stringify(payload)
206
+ body: JSON.stringify({
207
+ title: formTitle,
208
+ schedule: formSchedule,
209
+ prompt: formPrompt
210
+ })
168
211
  });
169
212
  const data = await res.json();
170
213
  if (!data.ok) { alert(data.error || 'Ошибка'); return; }
@@ -175,7 +218,8 @@ window.__ModuleLoader__.load({
175
218
 
176
219
  const handleCreateWithDSH = () => {
177
220
  setDropdownOpen(false);
178
- toggle.set(false);
221
+ if (onClose) onClose();
222
+ else toggle.set(false);
179
223
  setTimeout(() => {
180
224
  const textarea = document.querySelector('textarea[placeholder*="message"], textarea[class*="prompt"]');
181
225
  if (textarea) {
@@ -187,6 +231,20 @@ window.__ModuleLoader__.load({
187
231
 
188
232
  return React.createElement('div', { className: 'dsh-cron-overlay' },
189
233
  React.createElement('div', { className: 'dsh-cron-container' },
234
+ React.createElement('div', { className: 'dsh-cron-top-bar' },
235
+ React.createElement('button', {
236
+ type: 'button',
237
+ className: 'dsh-cron-back-btn',
238
+ title: 'Назад к чату',
239
+ onClick: () => { if (onClose) onClose(); else toggle.set(false); }
240
+ },
241
+ React.createElement('span', {
242
+ style: { display: 'flex', alignItems: 'center' },
243
+ dangerouslySetInnerHTML: { __html: BACK_ICON }
244
+ }),
245
+ React.createElement('span', null, 'Назад к чату')
246
+ )
247
+ ),
190
248
  React.createElement('div', { className: 'dsh-cron-header' },
191
249
  React.createElement('div', null,
192
250
  React.createElement('div', { className: 'dsh-cron-title' }, 'Запланированные задачи'),
@@ -239,49 +297,78 @@ window.__ModuleLoader__.load({
239
297
  React.createElement('div', { className: 'dsh-cron-task-sched' }, task.scheduleText || task.schedule)
240
298
  ),
241
299
  React.createElement('div', { className: 'dsh-cron-task-actions' },
242
- React.createElement('button', { className: 'dsh-cron-icon-action', onClick: () => handleRunNow(task.id) }, 'Запустить сейчас'),
243
- React.createElement('button', { className: 'dsh-cron-icon-action', onClick: () => handleDelete(task.id) }, 'Удалить')
300
+ React.createElement('button', { className: 'dsh-cron-icon-btn', onClick: () => handleRunNow(task.id) }, 'Запустить'),
301
+ React.createElement('button', { className: 'dsh-cron-icon-btn', onClick: () => handleDelete(task.id) }, 'Удалить')
244
302
  )
245
303
  )
246
304
  )
247
305
  ),
248
- React.createElement('div', { className: 'dsh-cron-rec-section' },
249
- React.createElement('div', { className: 'dsh-cron-rec-title' }, 'Рекомендации'),
250
- React.createElement('div', { className: 'dsh-cron-rec-list' },
251
- recs.map((rec) =>
252
- React.createElement('div', { key: rec.id, className: 'dsh-cron-rec-item', onClick: () => openCreateModal(rec) },
253
- React.createElement('div', {
254
- className: 'dsh-cron-rec-icon',
255
- dangerouslySetInnerHTML: { __html: rec.icon === 'bell' ? ICON_BELL : rec.icon === 'clipboard' ? ICON_CLIPBOARD : ICON_ACTIVITY }
256
- }),
257
- React.createElement('div', null,
258
- React.createElement('div', { className: 'dsh-cron-rec-item-title' }, rec.title, React.createElement('span', { className: 'dsh-cron-rec-item-sched' }, rec.scheduleText)),
259
- React.createElement('div', { className: 'dsh-cron-rec-item-desc' }, rec.description)
260
- )
306
+ React.createElement('div', { className: 'dsh-cron-recs-title' }, 'Рекомендации'),
307
+ React.createElement('div', { className: 'dsh-cron-recs-list' },
308
+ recs.map((rec) =>
309
+ React.createElement('div', {
310
+ key: rec.id,
311
+ className: 'dsh-cron-rec-card',
312
+ onClick: () => openCreateModal(rec)
313
+ },
314
+ React.createElement('div', {
315
+ className: 'dsh-cron-rec-icon',
316
+ dangerouslySetInnerHTML: {
317
+ __html: rec.icon === 'bell' ? ICON_BELL : rec.icon === 'clipboard' ? ICON_CLIPBOARD : ICON_ACTIVITY
318
+ }
319
+ }),
320
+ React.createElement('div', { className: 'dsh-cron-rec-content' },
321
+ React.createElement('div', { className: 'dsh-cron-rec-title' },
322
+ rec.title,
323
+ React.createElement('span', { className: 'dsh-cron-rec-time' }, rec.scheduleText)
324
+ ),
325
+ React.createElement('div', { className: 'dsh-cron-rec-desc' }, rec.description)
261
326
  )
262
327
  )
263
328
  )
264
329
  )
265
330
  ),
266
- modalOpen && React.createElement('div', { className: 'dsh-cron-modal-backdrop', onClick: () => setModalOpen(false) },
331
+ modalOpen && React.createElement('div', { className: 'dsh-cron-modal-overlay', onClick: () => setModalOpen(false) },
267
332
  React.createElement('div', { className: 'dsh-cron-modal', onClick: (e) => e.stopPropagation() },
268
- React.createElement('div', { className: 'dsh-cron-modal-title' }, 'Настройка задачи вручную'),
333
+ React.createElement('div', { className: 'dsh-cron-modal-title' }, 'Настройка запланированной задачи'),
269
334
  React.createElement('form', { onSubmit: handleSave },
270
- React.createElement('div', { className: 'dsh-cron-field' },
271
- React.createElement('label', { className: 'dsh-cron-label' }, 'Название задачи'),
272
- React.createElement('input', { className: 'dsh-cron-input', required: true, value: formTitle, onChange: (e) => setFormTitle(e.target.value) })
335
+ React.createElement('div', { className: 'dsh-cron-form-group' },
336
+ React.createElement('label', null, 'Название задачи'),
337
+ React.createElement('input', {
338
+ required: true,
339
+ value: formTitle,
340
+ placeholder: 'Например: Ежедневный утренний отчет',
341
+ onChange: (e) => setFormTitle(e.target.value)
342
+ })
273
343
  ),
274
- React.createElement('div', { className: 'dsh-cron-field' },
275
- React.createElement('label', { className: 'dsh-cron-label' }, 'Расписание (cron или interval)'),
276
- React.createElement('input', { className: 'dsh-cron-input', required: true, value: formSchedule, onChange: (e) => setFormSchedule(e.target.value) })
344
+ React.createElement('div', { className: 'dsh-cron-form-group' },
345
+ React.createElement('label', null, 'Расписание (cron или "every 30m")'),
346
+ React.createElement('input', {
347
+ required: true,
348
+ value: formSchedule,
349
+ placeholder: '0 9 * * 1-5 или every 2h',
350
+ onChange: (e) => setFormSchedule(e.target.value)
351
+ })
277
352
  ),
278
- React.createElement('div', { className: 'dsh-cron-field' },
279
- React.createElement('label', { className: 'dsh-cron-label' }, 'Инструкция для агента (промпт)'),
280
- React.createElement('textarea', { className: 'dsh-cron-textarea', required: true, value: formPrompt, onChange: (e) => setFormPrompt(e.target.value) })
353
+ React.createElement('div', { className: 'dsh-cron-form-group' },
354
+ React.createElement('label', null, 'Промпт / Инструкция для агента'),
355
+ React.createElement('textarea', {
356
+ required: true,
357
+ value: formPrompt,
358
+ placeholder: 'Опишите, что именно агент должен сделать...',
359
+ onChange: (e) => setFormPrompt(e.target.value)
360
+ })
281
361
  ),
282
362
  React.createElement('div', { className: 'dsh-cron-modal-foot' },
283
- React.createElement('button', { type: 'button', className: 'dsh-cron-btn-secondary', onClick: () => setModalOpen(false) }, 'Отмена'),
284
- React.createElement('button', { type: 'submit', className: 'dsh-cron-btn-primary' }, 'Сохранить')
363
+ React.createElement('button', {
364
+ type: 'button',
365
+ className: 'dsh-cron-btn-secondary',
366
+ onClick: () => setModalOpen(false)
367
+ }, 'Отмена'),
368
+ React.createElement('button', {
369
+ type: 'submit',
370
+ className: 'dsh-cron-btn-primary'
371
+ }, 'Сохранить задачу')
285
372
  )
286
373
  )
287
374
  )
@@ -290,9 +377,21 @@ window.__ModuleLoader__.load({
290
377
  }
291
378
 
292
379
  function nativeButton() {
293
- const nodes = document.querySelectorAll('button[class*="newSession"]');
294
- for (const node of nodes) {
295
- if (String(node.className).indexOf('newSessionLabel') === -1) return node;
380
+ const candidates = [
381
+ 'nav button[class*="sidebar-entry"]',
382
+ 'aside button[class*="sidebar-entry"]',
383
+ '[class*="sidebar"] button[class*="sidebar-entry"]',
384
+ 'nav button',
385
+ 'aside button',
386
+ '[class*="sidebar"] button'
387
+ ];
388
+ for (const sel of candidates) {
389
+ const found = document.querySelectorAll(sel);
390
+ for (const el of found) {
391
+ if (el.hasAttribute(ENTRY_ATTR)) continue;
392
+ if (el.dataset && el.dataset.dshPart === 'sidebar-entry') continue;
393
+ if (el.offsetWidth > 0 || el.offsetHeight > 0) return el;
394
+ }
296
395
  }
297
396
  return undefined;
298
397
  }
@@ -309,7 +408,6 @@ window.__ModuleLoader__.load({
309
408
 
310
409
  const icon = document.createElement('span');
311
410
  icon.className = 'dsh-cron-entry-icon';
312
- icon.style.marginRight = '8px';
313
411
  icon.innerHTML = ICON_TIMER;
314
412
  entry.appendChild(icon);
315
413
 
@@ -329,12 +427,12 @@ window.__ModuleLoader__.load({
329
427
  function placeEntry(target, entry) {
330
428
  let anchor = target;
331
429
  let next = target.nextElementSibling;
332
- while (next !== null && next.tagName === 'BUTTON' && String(next.className).indexOf('sidebar-entry') >= 0) {
430
+ while (next !== null && next.tagName === 'BUTTON'
431
+ && (String(next.className).indexOf('sidebar-entry') >= 0 || next.hasAttribute('data-dsh-part'))) {
333
432
  anchor = next;
334
433
  next = next.nextElementSibling;
335
434
  }
336
435
  anchor.insertAdjacentElement('afterend', entry);
337
- return true;
338
436
  }
339
437
 
340
438
  function mountSidebarEntry(toggle, label) {
@@ -375,7 +473,7 @@ window.__ModuleLoader__.load({
375
473
  let container;
376
474
 
377
475
  const ensure = () => {
378
- if (container !== undefined) return;
476
+ if (container !== undefined && container.isConnected) return;
379
477
  const column = document.querySelector(COLUMN_SELECTOR);
380
478
  if (column === null) return;
381
479
  container = document.createElement('div');
@@ -426,6 +524,7 @@ window.__ModuleLoader__.load({
426
524
  }
427
525
 
428
526
  function apply(ctx) {
527
+ ensureStyles();
429
528
  const toggle = createToggle();
430
529
  ctx.effect(() => {
431
530
  const off = [
@@ -439,4 +538,4 @@ window.__ModuleLoader__.load({
439
538
  module.exports = { apply, inject: ['slots', 'locale', 'settingsScope'] };
440
539
  return module.exports;
441
540
  }
442
- });
541
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-cron",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Scheduled cron tasks, background automation and agent execution for DeepSeek Harness.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",