json-object-editor 0.10.668 → 0.10.670
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/TESTING_PHASE1_PROGRESS.md +216 -0
- package/_www/ai-jobs.html +218 -0
- package/_www/mcp-nav.js +21 -12
- package/_www/mcp-test.html +287 -287
- package/capp/capp.css +3 -2
- package/css/joe-ai.css +71 -0
- package/css/joe.css +1 -1
- package/docs/protocol_report_template.html +479 -0
- package/docs/protocol_report_template.js +563 -0
- package/js/JsonObjectEditor.jquery.craydent.js +22 -3
- package/js/joe-ai.js +3220 -2301
- package/js/joe.js +23 -4
- package/js/joe.min.js +1 -1
- package/package.json +1 -1
- package/pages/template.html +2 -0
- package/server/fields/core.js +16 -2
- package/server/init.js +9 -1
- package/server/modules/AiJobs.js +412 -0
- package/server/modules/MCP.js +1481 -1368
- package/server/modules/Server.js +3 -0
- package/server/plugins/chatgpt.js +2332 -2113
- package/server/schemas/ai_prompt.js +9 -2
- package/server/schemas/report.js +1 -1
- package/server/schemas/task.js +1 -0
- package/web-components/field-jobs-container.js +224 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Phase 1 Progress Tracking - Testing Steps
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
Phase 1 implements client-side multi-job progress tracking UI. Jobs are tracked per button, with support for single and multiple jobs per button.
|
|
5
|
+
|
|
6
|
+
## Token Format
|
|
7
|
+
- **select_prompt**: Button token: `{objectId}_select_prompt_{timestamp}_{random}`, Job token: `{objectId}_{promptId}_{timestamp}_{random}`
|
|
8
|
+
- **ai:prompt (autofill)**: Token: `{objectId}_{fieldId}_{timestamp}_{random}`
|
|
9
|
+
- **proposeThought**: Token: `{objectId}_proposeThought_{timestamp}_{random}`
|
|
10
|
+
|
|
11
|
+
## Test 1: ai:prompt Button (Single Job) - Task Description Field
|
|
12
|
+
|
|
13
|
+
### Setup
|
|
14
|
+
1. Open a task object in JOE
|
|
15
|
+
2. Navigate to the "description" field (should have an "AI" button)
|
|
16
|
+
|
|
17
|
+
### Test Steps
|
|
18
|
+
1. **Verify button rendering**:
|
|
19
|
+
- Button should have class `joe-ai-button` and `joe-ai-autofill-button`
|
|
20
|
+
- Button should have `data-progress-token` attribute (format: `{objectId}_description_{timestamp}_{random}`)
|
|
21
|
+
- Button should have `data-field-name="description"` attribute
|
|
22
|
+
- Progress div should exist below button with `data-for-token` matching button token
|
|
23
|
+
- Jobs list container should exist below progress div
|
|
24
|
+
|
|
25
|
+
2. **Click AI button**:
|
|
26
|
+
- Button should disable and show loading state
|
|
27
|
+
- Button text should change to "Running..." or show percentage if available
|
|
28
|
+
- Progress text field should show "Starting..." or progress message
|
|
29
|
+
- Jobs list should show one job: "AI Autofill - Running..."
|
|
30
|
+
|
|
31
|
+
3. **During execution** (if progress updates are sent):
|
|
32
|
+
- Button text should update with percentage: "Running... X%"
|
|
33
|
+
- Progress text should show detailed message
|
|
34
|
+
- Job in list should show percentage
|
|
35
|
+
|
|
36
|
+
4. **On completion**:
|
|
37
|
+
- Button text should show "Complete" briefly, then restore to "AI"
|
|
38
|
+
- Button should re-enable
|
|
39
|
+
- Progress text should clear after 2 seconds
|
|
40
|
+
- Job should be removed from list
|
|
41
|
+
|
|
42
|
+
5. **On error**:
|
|
43
|
+
- Button should re-enable
|
|
44
|
+
- Job should be cleared
|
|
45
|
+
- Error alert should appear
|
|
46
|
+
|
|
47
|
+
### Expected Results
|
|
48
|
+
- Single job tracked correctly
|
|
49
|
+
- Button shows percentage when single job is active
|
|
50
|
+
- UI updates properly
|
|
51
|
+
- Job cleared on completion/error
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Test 2: select_prompt Field (Multiple Jobs)
|
|
56
|
+
|
|
57
|
+
### Setup
|
|
58
|
+
1. Open a protocol object (or any object with `select_prompt` field)
|
|
59
|
+
2. Navigate to the "Run AI Prompt" field
|
|
60
|
+
|
|
61
|
+
### Test Steps
|
|
62
|
+
1. **Verify button rendering**:
|
|
63
|
+
- Button token format: `{objectId}_select_prompt_{timestamp}_{random}`
|
|
64
|
+
- Progress and jobs containers exist
|
|
65
|
+
|
|
66
|
+
2. **Run first prompt**:
|
|
67
|
+
- Select a prompt from dropdown
|
|
68
|
+
- Click "Run AI Prompt"
|
|
69
|
+
- Button should disable
|
|
70
|
+
- Job token should be generated: `{objectId}_{promptId}_{timestamp}_{random}`
|
|
71
|
+
- Jobs list should show: "{Prompt Name} - Running..."
|
|
72
|
+
- Button text should show percentage (single job)
|
|
73
|
+
|
|
74
|
+
3. **Run second prompt** (while first is running):
|
|
75
|
+
- Select a different prompt
|
|
76
|
+
- Click "Run AI Prompt" again
|
|
77
|
+
- Second job should be added to jobs list
|
|
78
|
+
- Button text should change to "2 jobs running"
|
|
79
|
+
- Progress text field should clear (multiple jobs shown in list)
|
|
80
|
+
|
|
81
|
+
4. **Verify multiple jobs display**:
|
|
82
|
+
- Jobs list should show both jobs with their names and statuses
|
|
83
|
+
- Each job should show percentage or "Running..."
|
|
84
|
+
|
|
85
|
+
5. **Complete jobs**:
|
|
86
|
+
- As jobs complete, they should be removed from list
|
|
87
|
+
- Button text should update: "2 jobs running" → "1 job running" → percentage → "Complete" → original text
|
|
88
|
+
- When all jobs complete, button should re-enable
|
|
89
|
+
|
|
90
|
+
### Expected Results
|
|
91
|
+
- Multiple jobs tracked correctly
|
|
92
|
+
- Button shows "X jobs running" when multiple active
|
|
93
|
+
- Jobs list displays all active jobs
|
|
94
|
+
- Button shows percentage when only one job remains
|
|
95
|
+
- All jobs cleared when complete
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Test 3: proposeThought Field (Single Job)
|
|
100
|
+
|
|
101
|
+
### Setup
|
|
102
|
+
1. Open a task object
|
|
103
|
+
2. Navigate to "Propose Thought" field
|
|
104
|
+
|
|
105
|
+
### Test Steps
|
|
106
|
+
1. **Verify button rendering**:
|
|
107
|
+
- Button token format: `{objectId}_proposeThought_{timestamp}_{random}`
|
|
108
|
+
- Progress and jobs containers exist
|
|
109
|
+
|
|
110
|
+
2. **Run thought agent**:
|
|
111
|
+
- Click "Run Thought Agent"
|
|
112
|
+
- Button should disable
|
|
113
|
+
- Job should be tracked (token: `{objectId}_proposeThought_{timestamp}_{random}`)
|
|
114
|
+
- Button text should show "Running..." or percentage
|
|
115
|
+
- Progress text should show status
|
|
116
|
+
|
|
117
|
+
3. **On completion**:
|
|
118
|
+
- Button should re-enable
|
|
119
|
+
- Job should be cleared
|
|
120
|
+
- UI should restore
|
|
121
|
+
|
|
122
|
+
### Expected Results
|
|
123
|
+
- Single job tracked correctly
|
|
124
|
+
- UI updates properly
|
|
125
|
+
- Job cleared on completion
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Test 4: Navigation (Field Rerender)
|
|
130
|
+
|
|
131
|
+
### Setup
|
|
132
|
+
1. Start a job on any field
|
|
133
|
+
2. Navigate away (list view or different object)
|
|
134
|
+
3. Navigate back to the same object
|
|
135
|
+
|
|
136
|
+
### Test Steps
|
|
137
|
+
1. **Before navigation**:
|
|
138
|
+
- Note the button token
|
|
139
|
+
- Start a job
|
|
140
|
+
- Verify job is tracked
|
|
141
|
+
|
|
142
|
+
2. **After navigation**:
|
|
143
|
+
- Field rerenders with new button token
|
|
144
|
+
- Old job tracking is lost (expected for Phase 1)
|
|
145
|
+
- New button has no active jobs
|
|
146
|
+
|
|
147
|
+
### Expected Results
|
|
148
|
+
- Field rerender creates new button with new token
|
|
149
|
+
- Old jobs are not visible (Phase 2 will add server-side reconnection)
|
|
150
|
+
- New button works normally
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Test 5: Error Handling
|
|
155
|
+
|
|
156
|
+
### Test Steps
|
|
157
|
+
1. **Network error**:
|
|
158
|
+
- Start a job
|
|
159
|
+
- Simulate network failure (disable network or invalid endpoint)
|
|
160
|
+
- Verify error alert appears
|
|
161
|
+
- Verify job is cleared
|
|
162
|
+
- Verify button re-enables
|
|
163
|
+
|
|
164
|
+
2. **Server error**:
|
|
165
|
+
- Start a job with invalid parameters
|
|
166
|
+
- Verify error message appears
|
|
167
|
+
- Verify job is cleared
|
|
168
|
+
- Verify button re-enables
|
|
169
|
+
|
|
170
|
+
### Expected Results
|
|
171
|
+
- Errors are handled gracefully
|
|
172
|
+
- Jobs are cleared on error
|
|
173
|
+
- UI is restored
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Test 6: Multiple Windows
|
|
178
|
+
|
|
179
|
+
### Setup
|
|
180
|
+
1. Open same object in two browser windows/tabs
|
|
181
|
+
|
|
182
|
+
### Test Steps
|
|
183
|
+
1. **Window 1**:
|
|
184
|
+
- Start a job
|
|
185
|
+
- Verify job is tracked
|
|
186
|
+
|
|
187
|
+
2. **Window 2**:
|
|
188
|
+
- Should have independent button token
|
|
189
|
+
- Can start separate job
|
|
190
|
+
- Jobs are tracked independently
|
|
191
|
+
|
|
192
|
+
### Expected Results
|
|
193
|
+
- Each window has independent job tracking
|
|
194
|
+
- No conflicts between windows
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Debugging
|
|
199
|
+
|
|
200
|
+
### Console Checks
|
|
201
|
+
- `Ai.activeJobs` Map should contain button tokens as keys
|
|
202
|
+
- Each value should be an array of job objects
|
|
203
|
+
- Job objects should have: `token`, `promptId`, `promptName`, `progress`, `total`, `message`
|
|
204
|
+
|
|
205
|
+
### Common Issues
|
|
206
|
+
1. **Button not found**: Check `data-progress-token` attribute exists
|
|
207
|
+
2. **Jobs not showing**: Check `Ai.activeJobs` Map has entries
|
|
208
|
+
3. **UI not updating**: Check `renderJobsList` is being called
|
|
209
|
+
4. **Token format wrong**: Verify token matches expected format for field type
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Notes
|
|
214
|
+
- Phase 1 is client-side only. Server-side progress tracking comes in Phase 2.
|
|
215
|
+
- Jobs are lost on page refresh or field rerender (Phase 2 will add persistence).
|
|
216
|
+
- Progress updates require server-side implementation (Phase 2/3).
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>JOE AI Jobs Test</title>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
+
<style>
|
|
8
|
+
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;margin:20px;}
|
|
9
|
+
label{display:block;margin:8px 0 4px}
|
|
10
|
+
select, input, textarea, button{font-size:14px;padding:6px 12px}
|
|
11
|
+
textarea{width:100%;height:160px;font-family:ui-monospace,Menlo,Consolas,monospace}
|
|
12
|
+
pre{background:#f6f8fa;border:1px solid #e1e4e8;padding:10px;overflow:auto;max-height:400px}
|
|
13
|
+
.row{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin:8px 0}
|
|
14
|
+
.small{font-size:12px;color:#666}
|
|
15
|
+
.bad{color:#b00020}
|
|
16
|
+
.good{color:#0a7d00}
|
|
17
|
+
.section{margin:20px 0;padding:15px;border:1px solid #e1e4e8;border-radius:4px}
|
|
18
|
+
button{cursor:pointer;background:#0366d6;color:white;border:none;border-radius:4px}
|
|
19
|
+
button:hover{background:#0256c2}
|
|
20
|
+
button:disabled{background:#ccc;cursor:not-allowed}
|
|
21
|
+
input[type="text"]{min-width:300px;padding:6px}
|
|
22
|
+
</style>
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<div id="mcp-nav"></div>
|
|
26
|
+
<script src="/JsonObjectEditor/_www/mcp-nav.js"></script>
|
|
27
|
+
<h1>JOE AI Jobs Test</h1>
|
|
28
|
+
<div class="small">View and test active AI job tracking. Jobs are keyed by {objectId}_{identifier}.</div>
|
|
29
|
+
|
|
30
|
+
<div class="section">
|
|
31
|
+
<h3>All Active Jobs</h3>
|
|
32
|
+
<div class="row">
|
|
33
|
+
<button id="refreshAll">Refresh</button>
|
|
34
|
+
<span id="allStatus" class="small"></span>
|
|
35
|
+
</div>
|
|
36
|
+
<pre id="allJobsResult"></pre>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="section">
|
|
40
|
+
<h3>View Active Jobs for Object</h3>
|
|
41
|
+
<div class="row">
|
|
42
|
+
<label for="objectId">Object ID:</label>
|
|
43
|
+
<input id="objectId" type="text" placeholder="e.g. 5ce3d147-f37d-4c8f-9c00-bc185182c4d5" />
|
|
44
|
+
<button id="loadJobs">Load Jobs</button>
|
|
45
|
+
<span id="loadStatus" class="small"></span>
|
|
46
|
+
</div>
|
|
47
|
+
<pre id="jobsResult"></pre>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="section">
|
|
51
|
+
<h3>Example Job Tokens</h3>
|
|
52
|
+
<div class="small">Use these formats to understand token structure:</div>
|
|
53
|
+
<pre id="examples">select_prompt: {objectId}_select_prompt_{timestamp}_{random}
|
|
54
|
+
Example: 5ce3d147-f37d-4c8f-9c00-bc185182c4d5_select_prompt_1771022571836_abc123
|
|
55
|
+
Lookup Key: 5ce3d147-f37d-4c8f-9c00-bc185182c4d5_select_prompt
|
|
56
|
+
|
|
57
|
+
Job token (when prompt selected): {objectId}_{promptId}_{timestamp}_{random}
|
|
58
|
+
Example: 5ce3d147-f37d-4c8f-9c00-bc185182c4d5_promptId123_1771022571837_xyz789
|
|
59
|
+
Lookup Key: 5ce3d147-f37d-4c8f-9c00-bc185182c4d5_promptId123
|
|
60
|
+
|
|
61
|
+
ai:prompt (autofill): {objectId}_{fieldId}_{timestamp}_{random}
|
|
62
|
+
Example: 5ce3d147-f37d-4c8f-9c00-bc185182c4d5_description_1771022571838_def456
|
|
63
|
+
Lookup Key: 5ce3d147-f37d-4c8f-9c00-bc185182c4d5_description
|
|
64
|
+
|
|
65
|
+
proposeThought: {objectId}_proposeThought_{timestamp}_{random}
|
|
66
|
+
Example: 5ce3d147-f37d-4c8f-9c00-bc185182c4d5_proposeThought_1771022571839_ghi789
|
|
67
|
+
Lookup Key: 5ce3d147-f37d-4c8f-9c00-bc185182c4d5_proposeThought</pre>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div class="section">
|
|
71
|
+
<h3>Example Job JSON</h3>
|
|
72
|
+
<div class="small">Example job data structure (for reference):</div>
|
|
73
|
+
<pre id="exampleJson">{
|
|
74
|
+
"token": "5ce3d147-f37d-4c8f-9c00-bc185182c4d5_promptId123_1771022571837_xyz789",
|
|
75
|
+
"startTime": "2025-01-15T10:30:00.000Z",
|
|
76
|
+
"status": "running",
|
|
77
|
+
"promptId": "promptId123",
|
|
78
|
+
"promptName": "Generate Summary",
|
|
79
|
+
"fieldId": null,
|
|
80
|
+
"progress": 45,
|
|
81
|
+
"total": 100,
|
|
82
|
+
"message": "Processing files..."
|
|
83
|
+
}</pre>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="section">
|
|
87
|
+
<h3>Test extractKey Function</h3>
|
|
88
|
+
<div class="row">
|
|
89
|
+
<label for="testToken">Token:</label>
|
|
90
|
+
<input id="testToken" type="text" placeholder="Paste a token here" style="flex:1" />
|
|
91
|
+
<button id="testExtract">Test extractKey</button>
|
|
92
|
+
</div>
|
|
93
|
+
<pre id="extractResult"></pre>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<script>
|
|
97
|
+
// Load all active jobs on page load
|
|
98
|
+
function loadAllJobs() {
|
|
99
|
+
var statusEl = document.getElementById('allStatus');
|
|
100
|
+
var resultEl = document.getElementById('allJobsResult');
|
|
101
|
+
|
|
102
|
+
statusEl.textContent = 'Loading...';
|
|
103
|
+
statusEl.className = 'small';
|
|
104
|
+
resultEl.textContent = '';
|
|
105
|
+
|
|
106
|
+
fetch('/API/aijobs')
|
|
107
|
+
.then(function(res) {
|
|
108
|
+
if (!res.ok) {
|
|
109
|
+
throw new Error('HTTP ' + res.status + ': ' + res.statusText);
|
|
110
|
+
}
|
|
111
|
+
return res.json();
|
|
112
|
+
})
|
|
113
|
+
.then(function(data) {
|
|
114
|
+
var count = data.count || 0;
|
|
115
|
+
var lookupKeys = data.lookupKeys || 0;
|
|
116
|
+
var jobsArray = data.jobs || [];
|
|
117
|
+
statusEl.textContent = 'Success - ' + count + ' job(s) across ' + lookupKeys + ' lookup key(s)';
|
|
118
|
+
statusEl.className = 'small good';
|
|
119
|
+
|
|
120
|
+
// Format for display
|
|
121
|
+
var displayData = {
|
|
122
|
+
count: count,
|
|
123
|
+
lookupKeys: lookupKeys,
|
|
124
|
+
jobs: jobsArray
|
|
125
|
+
};
|
|
126
|
+
resultEl.textContent = JSON.stringify(displayData, null, 2);
|
|
127
|
+
})
|
|
128
|
+
.catch(function(e) {
|
|
129
|
+
statusEl.textContent = 'Error: ' + e.message;
|
|
130
|
+
statusEl.className = 'small bad';
|
|
131
|
+
resultEl.textContent = 'Error: ' + e.message;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Load on page load
|
|
136
|
+
if (document.readyState === 'loading') {
|
|
137
|
+
document.addEventListener('DOMContentLoaded', loadAllJobs);
|
|
138
|
+
} else {
|
|
139
|
+
loadAllJobs();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
document.getElementById('refreshAll').addEventListener('click', loadAllJobs);
|
|
143
|
+
|
|
144
|
+
document.getElementById('loadJobs').addEventListener('click', async function() {
|
|
145
|
+
var objectId = document.getElementById('objectId').value.trim();
|
|
146
|
+
var statusEl = document.getElementById('loadStatus');
|
|
147
|
+
var resultEl = document.getElementById('jobsResult');
|
|
148
|
+
|
|
149
|
+
if (!objectId) {
|
|
150
|
+
statusEl.textContent = 'Please enter an object ID';
|
|
151
|
+
statusEl.className = 'small bad';
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
statusEl.textContent = 'Loading...';
|
|
156
|
+
statusEl.className = 'small';
|
|
157
|
+
resultEl.textContent = '';
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
var res = await fetch('/API/aijobs/' + encodeURIComponent(objectId));
|
|
161
|
+
if (!res.ok) {
|
|
162
|
+
throw new Error('HTTP ' + res.status + ': ' + res.statusText);
|
|
163
|
+
}
|
|
164
|
+
var data = await res.json();
|
|
165
|
+
statusEl.textContent = 'Success - ' + (data.count || 0) + ' job(s)';
|
|
166
|
+
statusEl.className = 'small good';
|
|
167
|
+
resultEl.textContent = JSON.stringify(data, null, 2);
|
|
168
|
+
} catch (e) {
|
|
169
|
+
statusEl.textContent = 'Error: ' + e.message;
|
|
170
|
+
statusEl.className = 'small bad';
|
|
171
|
+
resultEl.textContent = 'Error: ' + e.message;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
document.getElementById('testExtract').addEventListener('click', function() {
|
|
176
|
+
var token = document.getElementById('testToken').value.trim();
|
|
177
|
+
var resultEl = document.getElementById('extractResult');
|
|
178
|
+
|
|
179
|
+
if (!token) {
|
|
180
|
+
resultEl.textContent = 'Please enter a token';
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Token format: objectId_identifier_timestamp_random
|
|
185
|
+
// Extract: objectId_identifier (everything before last 2 underscores)
|
|
186
|
+
var parts = token.split('_');
|
|
187
|
+
if (parts.length < 3) {
|
|
188
|
+
resultEl.textContent = 'Invalid token format. Need at least 3 parts separated by underscores.';
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
var lookupKey = parts.slice(0, -2).join('_');
|
|
193
|
+
var timestamp = parts[parts.length - 2];
|
|
194
|
+
var random = parts[parts.length - 1];
|
|
195
|
+
|
|
196
|
+
resultEl.textContent = JSON.stringify({
|
|
197
|
+
token: token,
|
|
198
|
+
lookupKey: lookupKey,
|
|
199
|
+
timestamp: timestamp,
|
|
200
|
+
random: random,
|
|
201
|
+
parts: parts.length
|
|
202
|
+
}, null, 2);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Auto-load on Enter key
|
|
206
|
+
document.getElementById('objectId').addEventListener('keypress', function(e) {
|
|
207
|
+
if (e.key === 'Enter') {
|
|
208
|
+
document.getElementById('loadJobs').click();
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
document.getElementById('testToken').addEventListener('keypress', function(e) {
|
|
212
|
+
if (e.key === 'Enter') {
|
|
213
|
+
document.getElementById('testExtract').click();
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
</script>
|
|
217
|
+
</body>
|
|
218
|
+
</html>
|
package/_www/mcp-nav.js
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
;(function(){
|
|
2
|
+
// Creates navigation element with two rows of links and info display. Returns the nav DOM element.
|
|
2
3
|
function buildNav(){
|
|
3
4
|
var nav = document.createElement('nav');
|
|
4
|
-
nav.setAttribute('style','display:flex;flex-direction:column;gap:6px;margin-bottom:8px');
|
|
5
|
+
nav.setAttribute('style','display:flex;flex-direction:column;gap:6px;margin-bottom:8px;font-size:14px;');
|
|
5
6
|
var infoRow = '<div id="mcp-nav-info" class="small" style="opacity:.8"></div>';
|
|
6
|
-
var
|
|
7
|
+
var linksRow1 = [
|
|
7
8
|
'<div style="display:flex;gap:10px;align-items:center">',
|
|
8
|
-
'<a href="/mcp-test.html" target="mcp_test_win"
|
|
9
|
-
'<a href="/mcp-export.html" target="mcp_export_win"
|
|
10
|
-
'<a href="/mcp-
|
|
11
|
-
'<a href="/
|
|
12
|
-
'<a href="/mcp-prompt.html" target="mcp_prompt_win" rel="noopener">MCP Prompt</a>',
|
|
13
|
-
'<a href="/ai-widget-test.html" target="ai_widget_test_win" rel="noopener">AI Widget</a>',
|
|
14
|
-
'<a href="/plugins-test.html" target="plugins_test_win" rel="noopener">Plugins</a>',
|
|
9
|
+
'<a href="/mcp-test.html" target="mcp_test_win">MCP Test</a>',
|
|
10
|
+
'<a href="/mcp-export.html" target="mcp_export_win">MCP Export</a>',
|
|
11
|
+
'<a href="/mcp-prompt.html" target="mcp_prompt_win">MCP Prompt</a>',
|
|
12
|
+
'<a href="/.well-known/mcp/manifest.json" target="mcp_manifest_win">MCP Manifest</a>',
|
|
15
13
|
'<span style="margin-left:auto"></span>',
|
|
16
|
-
'<a href="/privacy" target="privacy_win"
|
|
17
|
-
'<a href="/terms" target="terms_win"
|
|
14
|
+
'<a href="/privacy" target="privacy_win">Privacy</a>',
|
|
15
|
+
'<a href="/terms" target="terms_win">Terms</a>',
|
|
18
16
|
'</div>'
|
|
19
17
|
].join('');
|
|
20
|
-
|
|
18
|
+
var linksRow2 = [
|
|
19
|
+
'<div style="display:flex;gap:10px;align-items:center">',
|
|
20
|
+
'<a href="/mcp-schemas.html" target="mcp_schemas_win">Schemas</a>',
|
|
21
|
+
'<a href="/matrix.html" target="matrix_win">Matrix</a>',
|
|
22
|
+
'<a href="/ai-widget-test.html" target="ai_widget_test_win">AI Widget</a>',
|
|
23
|
+
'<a href="/ai-jobs.html" target="ai_jobs_win">AI Jobs</a>',
|
|
24
|
+
'<a href="/plugins-test.html" target="plugins_test_win">Plugins</a>',
|
|
25
|
+
'</div>'
|
|
26
|
+
].join('');
|
|
27
|
+
nav.innerHTML = infoRow + linksRow1 + linksRow2;
|
|
21
28
|
return nav;
|
|
22
29
|
}
|
|
30
|
+
// Inserts navigation into DOM, replacing placeholder if present or prepending to body. No return value.
|
|
23
31
|
function insert(){
|
|
24
32
|
var placeholder = document.getElementById('mcp-nav');
|
|
25
33
|
var nav = buildNav();
|
|
@@ -29,6 +37,7 @@
|
|
|
29
37
|
document.body.insertBefore(nav, document.body.firstChild);
|
|
30
38
|
}
|
|
31
39
|
}
|
|
40
|
+
// Fetches MCP manifest and updates info display with instance name, version, and hostname. No return value.
|
|
32
41
|
async function updateInstance(){
|
|
33
42
|
try{
|
|
34
43
|
var res = await fetch('/.well-known/mcp/manifest.json');
|