goto-assistant 0.3.0 → 0.4.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/package.json +1 -1
- package/public/index.html +35 -6
- package/public/setup.html +4 -4
- package/public/style.css +115 -17
- package/public/task-chat.js +10 -0
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
6
6
|
<meta name="color-scheme" content="light dark">
|
|
7
7
|
<title>goto-assistant</title>
|
|
8
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
|
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2.1.1/css/pico.min.css">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&family=Instrument+Serif:ital@0;1&display=swap" rel="stylesheet">
|
|
12
12
|
<link rel="stylesheet" href="/style.css">
|
|
13
|
-
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
14
|
-
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
|
13
|
+
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.12/marked.min.js"></script>
|
|
14
|
+
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.3.1/dist/purify.min.js"></script>
|
|
15
|
+
<script src="https://cdn.jsdelivr.net/npm/cronstrue@2.61.0/dist/cronstrue.min.js"></script>
|
|
15
16
|
<script src="/chat-core.js"></script>
|
|
16
17
|
<script src="/task-chat.js"></script>
|
|
17
18
|
</head>
|
|
@@ -72,6 +73,7 @@
|
|
|
72
73
|
<button class="btn-icon" id="taskToggleBtn" title="Enable/Disable"></button>
|
|
73
74
|
<button class="btn-icon" id="taskDeleteBtn" title="Delete">🗑</button>
|
|
74
75
|
</div>
|
|
76
|
+
<button class="task-chat-toggle" id="taskChatToggle" title="Chat">💬</button>
|
|
75
77
|
</div>
|
|
76
78
|
<div class="task-detail-body">
|
|
77
79
|
<div class="task-info" id="taskInfo">
|
|
@@ -79,7 +81,15 @@
|
|
|
79
81
|
<div class="task-content" id="taskContent"></div>
|
|
80
82
|
<div class="task-results" id="taskResults"></div>
|
|
81
83
|
</div>
|
|
82
|
-
<div class="task-
|
|
84
|
+
<div class="task-create-empty" id="taskCreateEmpty">
|
|
85
|
+
<p>Describe the task you'd like to create</p>
|
|
86
|
+
<button class="task-create-empty-btn" id="taskCreateOpenChat">Start chatting →</button>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="task-chat-panel mobile-slide-overlay" id="taskChatPanel">
|
|
89
|
+
<div class="task-chat-header">
|
|
90
|
+
<h3>Task Chat</h3>
|
|
91
|
+
<button class="task-chat-close" id="taskChatClose">×</button>
|
|
92
|
+
</div>
|
|
83
93
|
<div class="task-chat-messages" id="taskChatMessages"></div>
|
|
84
94
|
<div class="task-chat-input">
|
|
85
95
|
<div class="setup-chat-input-row">
|
|
@@ -414,6 +424,7 @@
|
|
|
414
424
|
function showChatMain() {
|
|
415
425
|
document.querySelector('.chat-main').style.display = '';
|
|
416
426
|
document.getElementById('taskDetail').style.display = 'none';
|
|
427
|
+
document.getElementById('taskChatPanel').classList.remove('open');
|
|
417
428
|
disconnectTaskChat();
|
|
418
429
|
}
|
|
419
430
|
|
|
@@ -507,14 +518,16 @@
|
|
|
507
518
|
document.getElementById('taskDetailTitle').textContent = task.name || task.id;
|
|
508
519
|
document.getElementById('taskDetailActions').style.display = '';
|
|
509
520
|
document.getElementById('taskInfo').style.display = '';
|
|
521
|
+
document.querySelector('.task-detail-body').classList.remove('create-mode');
|
|
510
522
|
|
|
511
523
|
// Meta
|
|
512
524
|
const meta = document.getElementById('taskMeta');
|
|
525
|
+
const humanSchedule = task.schedule ? cronToHuman(task.schedule) : null;
|
|
513
526
|
meta.innerHTML = `
|
|
514
527
|
<div class="task-meta-row"><span class="task-meta-label">ID</span><span class="task-meta-value">${escapeHtml(task.id || '')}</span></div>
|
|
515
528
|
<div class="task-meta-row"><span class="task-meta-label">Type</span><span class="task-meta-value"><span class="task-type-badge">${task.type === 'AI' ? 'AI' : 'CMD'}</span></span></div>
|
|
516
529
|
<div class="task-meta-row"><span class="task-meta-label">Status</span><span class="task-meta-value">${task.enabled ? '<span class="task-enabled-badge">Enabled</span>' : '<span class="task-disabled-badge">Disabled</span>'}</span></div>
|
|
517
|
-
${task.schedule ? `<div class="task-meta-row"><span class="task-meta-label">Schedule</span><span class="task-meta-value"
|
|
530
|
+
${task.schedule ? `<div class="task-meta-row"><span class="task-meta-label">Schedule</span><span class="task-meta-value">${humanSchedule ? `<span class="task-schedule-human">${escapeHtml(humanSchedule)}</span> <code class="task-schedule-raw">${escapeHtml(task.schedule)}</code>` : `<code>${escapeHtml(task.schedule)}</code>`}</span></div>` : ''}
|
|
518
531
|
`;
|
|
519
532
|
|
|
520
533
|
// Content (prompt or command)
|
|
@@ -597,7 +610,7 @@
|
|
|
597
610
|
? DOMPurify.sanitize(marked.parse(outputText))
|
|
598
611
|
: escapeHtml(outputText);
|
|
599
612
|
html += `<div class="task-result-item">
|
|
600
|
-
<div class="task-result-header"><span class="task-result-time">${escapeHtml(time)}
|
|
613
|
+
<div class="task-result-header"><span class="task-result-time">${escapeHtml(time)}</span><div class="task-result-header-right">${duration ? '<span class="task-result-duration">' + escapeHtml(duration) + '</span>' : ''}<span class="task-result-status ${status}">${isError ? 'Failed' : 'OK'}</span></div></div>
|
|
601
614
|
<div class="task-result-output">${renderedOutput}</div>
|
|
602
615
|
</div>`;
|
|
603
616
|
});
|
|
@@ -611,8 +624,13 @@
|
|
|
611
624
|
document.getElementById('taskDetailTitle').textContent = 'Create Task';
|
|
612
625
|
document.getElementById('taskDetailActions').style.display = 'none';
|
|
613
626
|
document.getElementById('taskInfo').style.display = 'none';
|
|
627
|
+
document.querySelector('.task-detail-body').classList.add('create-mode');
|
|
614
628
|
showTaskDetail();
|
|
615
629
|
initTaskChat(null);
|
|
630
|
+
// Auto-open chat overlay on mobile
|
|
631
|
+
if (window.matchMedia('(max-width: 768px)').matches) {
|
|
632
|
+
document.getElementById('taskChatPanel').classList.add('open');
|
|
633
|
+
}
|
|
616
634
|
}
|
|
617
635
|
|
|
618
636
|
// On task chat done, refresh task info without clearing chat messages
|
|
@@ -664,6 +682,17 @@
|
|
|
664
682
|
}
|
|
665
683
|
});
|
|
666
684
|
|
|
685
|
+
// Mobile task chat overlay toggle
|
|
686
|
+
document.getElementById('taskChatToggle').addEventListener('click', () => {
|
|
687
|
+
document.getElementById('taskChatPanel').classList.add('open');
|
|
688
|
+
});
|
|
689
|
+
document.getElementById('taskChatClose').addEventListener('click', () => {
|
|
690
|
+
document.getElementById('taskChatPanel').classList.remove('open');
|
|
691
|
+
});
|
|
692
|
+
document.getElementById('taskCreateOpenChat').addEventListener('click', () => {
|
|
693
|
+
document.getElementById('taskChatPanel').classList.add('open');
|
|
694
|
+
});
|
|
695
|
+
|
|
667
696
|
connectWs();
|
|
668
697
|
loadConversations();
|
|
669
698
|
</script>
|
package/public/setup.html
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
6
6
|
<meta name="color-scheme" content="light dark">
|
|
7
7
|
<title>goto-assistant — Setup</title>
|
|
8
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
|
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2.1.1/css/pico.min.css">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&family=Instrument+Serif:ital@0;1&display=swap" rel="stylesheet">
|
|
12
12
|
<link rel="stylesheet" href="/style.css">
|
|
13
|
-
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
14
|
-
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
|
13
|
+
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.12/marked.min.js"></script>
|
|
14
|
+
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.3.1/dist/purify.min.js"></script>
|
|
15
15
|
<script src="/cron-sync.js"></script>
|
|
16
16
|
<script src="/setup.js"></script>
|
|
17
17
|
<script src="/chat-core.js"></script>
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
</div>
|
|
60
60
|
|
|
61
61
|
<!-- Right: chat panel -->
|
|
62
|
-
<div class="setup-chat" id="setupChat">
|
|
62
|
+
<div class="setup-chat mobile-slide-overlay" id="setupChat">
|
|
63
63
|
<div class="setup-chat-header">
|
|
64
64
|
<h3>Setup Wizard</h3>
|
|
65
65
|
<button class="setup-chat-close" id="chatCloseBtn">×</button>
|
package/public/style.css
CHANGED
|
@@ -177,7 +177,8 @@ body {
|
|
|
177
177
|
.btn-icon, .settings-btn, .hamburger-btn,
|
|
178
178
|
.file-upload-btn, .file-preview-remove,
|
|
179
179
|
.conversation-item .delete-btn,
|
|
180
|
-
.setup-chat-close,
|
|
180
|
+
.setup-chat-close, .task-chat-close, .task-chat-toggle,
|
|
181
|
+
.task-create-empty-btn,
|
|
181
182
|
.sidebar-tab, .task-back-btn,
|
|
182
183
|
.task-detail-actions .btn-icon {
|
|
183
184
|
background: none; border: none; box-shadow: none;
|
|
@@ -703,7 +704,7 @@ body {
|
|
|
703
704
|
}
|
|
704
705
|
|
|
705
706
|
.task-detail-body {
|
|
706
|
-
flex: 1; display: flex; flex-direction:
|
|
707
|
+
flex: 1; display: flex; flex-direction: row;
|
|
707
708
|
overflow: hidden;
|
|
708
709
|
}
|
|
709
710
|
|
|
@@ -711,8 +712,8 @@ body {
|
|
|
711
712
|
.task-info {
|
|
712
713
|
padding: 16px 20px;
|
|
713
714
|
overflow-y: auto;
|
|
714
|
-
|
|
715
|
-
|
|
715
|
+
flex: 1;
|
|
716
|
+
min-width: 0;
|
|
716
717
|
}
|
|
717
718
|
|
|
718
719
|
.task-meta {
|
|
@@ -740,6 +741,14 @@ body {
|
|
|
740
741
|
padding: 2px 6px;
|
|
741
742
|
border-radius: 4px;
|
|
742
743
|
}
|
|
744
|
+
.task-schedule-human {
|
|
745
|
+
font-weight: 450;
|
|
746
|
+
}
|
|
747
|
+
.task-schedule-raw {
|
|
748
|
+
font-size: 11px;
|
|
749
|
+
color: var(--text-muted);
|
|
750
|
+
margin-left: 6px;
|
|
751
|
+
}
|
|
743
752
|
|
|
744
753
|
.task-enabled-badge {
|
|
745
754
|
color: #22c55e; font-weight: 600; font-size: 12px;
|
|
@@ -783,12 +792,24 @@ body {
|
|
|
783
792
|
}
|
|
784
793
|
.task-result-header {
|
|
785
794
|
display: flex; justify-content: space-between; align-items: center;
|
|
786
|
-
padding:
|
|
795
|
+
padding: 8px 12px;
|
|
787
796
|
background: var(--surface-2);
|
|
788
797
|
border-bottom: 1px solid var(--surface-border);
|
|
789
798
|
}
|
|
790
799
|
.task-result-time {
|
|
791
|
-
font-size:
|
|
800
|
+
font-size: 13px; font-weight: 500; color: var(--text-secondary);
|
|
801
|
+
}
|
|
802
|
+
.task-result-header-right {
|
|
803
|
+
display: flex; align-items: center; gap: 8px;
|
|
804
|
+
}
|
|
805
|
+
.task-result-duration {
|
|
806
|
+
font-family: var(--font-mono);
|
|
807
|
+
font-size: 12px;
|
|
808
|
+
color: var(--text-secondary);
|
|
809
|
+
background: var(--surface-1);
|
|
810
|
+
border: 1px solid var(--surface-border);
|
|
811
|
+
padding: 1px 7px;
|
|
812
|
+
border-radius: 4px;
|
|
792
813
|
}
|
|
793
814
|
.task-result-status {
|
|
794
815
|
font-size: 11px; font-weight: 700; text-transform: uppercase;
|
|
@@ -859,9 +880,75 @@ body {
|
|
|
859
880
|
.task-result-output img { max-width: 100%; border-radius: 6px; }
|
|
860
881
|
|
|
861
882
|
/* ---- Task Inline Chat ---- */
|
|
883
|
+
.task-chat-toggle {
|
|
884
|
+
display: none;
|
|
885
|
+
cursor: pointer; font-size: 18px; padding: 6px 8px;
|
|
886
|
+
border-radius: 8px;
|
|
887
|
+
color: var(--text-secondary);
|
|
888
|
+
transition: all 0.15s ease;
|
|
889
|
+
}
|
|
890
|
+
.task-chat-toggle:hover {
|
|
891
|
+
background: var(--surface-2);
|
|
892
|
+
color: var(--text-primary);
|
|
893
|
+
}
|
|
862
894
|
.task-chat-panel {
|
|
863
|
-
|
|
895
|
+
width: 360px; flex-shrink: 0;
|
|
896
|
+
display: flex; flex-direction: column;
|
|
864
897
|
min-height: 0; overflow: hidden;
|
|
898
|
+
border-left: 1px solid var(--surface-border);
|
|
899
|
+
}
|
|
900
|
+
.task-chat-header {
|
|
901
|
+
display: none;
|
|
902
|
+
padding: 14px 16px;
|
|
903
|
+
border-bottom: 1px solid var(--surface-border);
|
|
904
|
+
justify-content: space-between; align-items: center;
|
|
905
|
+
}
|
|
906
|
+
.task-chat-header h3 {
|
|
907
|
+
margin: 0; font-size: 15px; font-weight: 600;
|
|
908
|
+
}
|
|
909
|
+
.task-chat-close {
|
|
910
|
+
font-size: 20px; cursor: pointer;
|
|
911
|
+
color: var(--text-muted); padding: 4px 8px;
|
|
912
|
+
border-radius: 8px;
|
|
913
|
+
transition: all 0.15s ease;
|
|
914
|
+
}
|
|
915
|
+
.task-chat-close:hover {
|
|
916
|
+
background: var(--surface-2);
|
|
917
|
+
color: var(--text-primary);
|
|
918
|
+
}
|
|
919
|
+
.task-detail-body.create-mode .task-chat-panel {
|
|
920
|
+
flex: 1; width: auto; border-left: none;
|
|
921
|
+
}
|
|
922
|
+
.task-create-empty {
|
|
923
|
+
display: none;
|
|
924
|
+
flex-direction: column;
|
|
925
|
+
align-items: center;
|
|
926
|
+
justify-content: center;
|
|
927
|
+
gap: 16px;
|
|
928
|
+
color: var(--text-muted);
|
|
929
|
+
font-style: italic;
|
|
930
|
+
font-family: var(--font-display);
|
|
931
|
+
font-size: 18px;
|
|
932
|
+
}
|
|
933
|
+
.task-create-empty p { margin: 0; }
|
|
934
|
+
.task-detail-body.create-mode .task-create-empty { display: flex; flex: 1; }
|
|
935
|
+
.task-create-empty-btn {
|
|
936
|
+
background: var(--accent);
|
|
937
|
+
color: #fff;
|
|
938
|
+
border-radius: 12px;
|
|
939
|
+
padding: 10px 24px;
|
|
940
|
+
font-family: var(--font-body);
|
|
941
|
+
font-weight: 600;
|
|
942
|
+
font-size: 14px;
|
|
943
|
+
cursor: pointer;
|
|
944
|
+
transition: all 0.15s ease;
|
|
945
|
+
}
|
|
946
|
+
.task-create-empty-btn:hover {
|
|
947
|
+
background: var(--accent-hover);
|
|
948
|
+
transform: translateY(-1px);
|
|
949
|
+
}
|
|
950
|
+
@media (min-width: 769px) {
|
|
951
|
+
.task-create-empty { display: none !important; }
|
|
865
952
|
}
|
|
866
953
|
.task-chat-messages {
|
|
867
954
|
flex: 1; overflow-y: auto; padding: 16px 20px;
|
|
@@ -1060,26 +1147,37 @@ body {
|
|
|
1060
1147
|
|
|
1061
1148
|
.input-area { padding: 10px 12px 12px; padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
|
|
1062
1149
|
.input-row { max-width: 100%; border-radius: 16px; }
|
|
1063
|
-
.input-row textarea
|
|
1150
|
+
.input-row textarea,
|
|
1151
|
+
.setup-chat-input-row textarea { font-size: 16px; }
|
|
1064
1152
|
|
|
1065
1153
|
.conversation-item .delete-btn { display: inline-block; }
|
|
1066
1154
|
|
|
1067
1155
|
/* Task mobile */
|
|
1068
1156
|
.task-meta { flex-direction: column; gap: 4px; }
|
|
1069
|
-
.task-
|
|
1070
|
-
.task-info {
|
|
1157
|
+
.task-detail-body { flex-direction: column; }
|
|
1158
|
+
.task-info { flex: 1; }
|
|
1159
|
+
.task-chat-toggle { display: block; }
|
|
1160
|
+
/* Shared mobile slide-in overlay */
|
|
1161
|
+
.mobile-slide-overlay {
|
|
1162
|
+
position: fixed; top: 0; right: 0; bottom: 0; left: 0;
|
|
1163
|
+
/* width: 100% is NOT redundant — it overrides explicit widths (e.g. .setup-chat 400px,
|
|
1164
|
+
.task-chat-panel 360px) that would otherwise take priority over left/right: 0 */
|
|
1165
|
+
width: 100%;
|
|
1166
|
+
background: var(--surface-0);
|
|
1167
|
+
transform: translateX(100%);
|
|
1168
|
+
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
1169
|
+
z-index: 15;
|
|
1170
|
+
}
|
|
1171
|
+
.mobile-slide-overlay.open { transform: translateX(0); }
|
|
1172
|
+
|
|
1173
|
+
.task-chat-panel { border-left: none; }
|
|
1174
|
+
.task-chat-header { display: flex; }
|
|
1071
1175
|
.task-chat-input { padding: 10px 12px 12px; padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
|
|
1072
1176
|
|
|
1073
1177
|
/* Setup mobile */
|
|
1074
1178
|
.setup-layout { flex-direction: column; }
|
|
1075
1179
|
.setup-form { flex: 1; }
|
|
1076
|
-
.setup-chat {
|
|
1077
|
-
position: fixed; top: 0; right: 0; bottom: 0; width: 100%;
|
|
1078
|
-
transform: translateX(100%);
|
|
1079
|
-
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
1080
|
-
z-index: 15;
|
|
1081
|
-
}
|
|
1082
|
-
.setup-chat.open { transform: translateX(0); }
|
|
1180
|
+
.setup-chat-input { padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
|
|
1083
1181
|
}
|
|
1084
1182
|
|
|
1085
1183
|
@media (min-width: 769px) {
|
package/public/task-chat.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
// task-chat.js — Inline chat logic for task mode (create/modify tasks via AI).
|
|
2
2
|
// Loaded as a plain <script> in the browser; importable via require() in tests.
|
|
3
3
|
|
|
4
|
+
function cronToHuman(expr) {
|
|
5
|
+
if (typeof cronstrue === 'undefined' || !expr) return null;
|
|
6
|
+
try {
|
|
7
|
+
return cronstrue.toString(expr);
|
|
8
|
+
} catch (e) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
4
13
|
var taskRunState = {};
|
|
5
14
|
// Shape: { [taskId]: { pollTimer, timeoutTimer, runStartTime, pendingResult } }
|
|
6
15
|
// pendingResult: null while running, string (chat text) when result arrived but user wasn't viewing this task
|
|
@@ -241,6 +250,7 @@ function disconnectTaskChat() {
|
|
|
241
250
|
|
|
242
251
|
if (typeof module !== 'undefined' && module.exports) {
|
|
243
252
|
module.exports = {
|
|
253
|
+
cronToHuman: cronToHuman,
|
|
244
254
|
taskChatState: taskChatState,
|
|
245
255
|
taskRunState: taskRunState,
|
|
246
256
|
cancelTaskRun: cancelTaskRun,
|