@stravica-ai/digital-operator-pa 0.1.7 → 0.1.8
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.
|
@@ -66,8 +66,14 @@
|
|
|
66
66
|
.ws-next-text { color: var(--text); }
|
|
67
67
|
.ws-docs { margin-top: 0.4rem; padding-top: 0.35rem; border-top: 1px solid var(--border); }
|
|
68
68
|
.doc-list { list-style: none; }
|
|
69
|
-
|
|
69
|
+
/* Each row: the document link (flex-grows) + a pop-out-to-new-tab button. */
|
|
70
|
+
.doc-row { display: flex; align-items: center; gap: 0.4rem; }
|
|
71
|
+
.doc-item { padding: 0.3rem 0; cursor: pointer; color: var(--accent); font-size: 0.78rem; transition: color 0.12s; display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; text-decoration: none; flex: 1 1 auto; min-width: 0; }
|
|
70
72
|
.doc-item:hover { color: var(--text-bright); }
|
|
73
|
+
/* Open-in-new-tab affordance. Dim until hovered so it doesn't compete with
|
|
74
|
+
* the label; opens the document in a full browser tab (target=_blank). */
|
|
75
|
+
.doc-ext { flex: 0 0 auto; color: var(--text-dim); font-size: 0.85rem; line-height: 1; text-decoration: none; padding: 0.2rem 0.3rem; border-radius: 4px; transition: color 0.12s, background 0.12s; }
|
|
76
|
+
.doc-ext:hover { color: var(--accent); background: var(--bg-highlight); }
|
|
71
77
|
.doc-item .doc-label { color: var(--accent); }
|
|
72
78
|
.doc-item:hover .doc-label { color: var(--text-bright); }
|
|
73
79
|
.doc-item .doc-path { color: var(--text-dim); font-size: 0.68rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
@@ -140,6 +146,10 @@
|
|
|
140
146
|
document.getElementById('task-modal').classList.remove('open');
|
|
141
147
|
}
|
|
142
148
|
function esc(s) { var el = document.createElement('span'); el.textContent = s; return el.innerHTML; }
|
|
149
|
+
// Direct-serve URL for a repo-relative doc path — the document's full-page
|
|
150
|
+
// URL (server renders .md, serves .html as-is, other types raw). Used for
|
|
151
|
+
// the open-in-new-tab affordance. Segments encoded so spaces/unicode survive.
|
|
152
|
+
function rawUrl(path) { return '/' + String(path).split('/').map(encodeURIComponent).join('/'); }
|
|
143
153
|
function statusBadgeClass(status) {
|
|
144
154
|
var s = (status || '').toLowerCase();
|
|
145
155
|
if (s === 'active' || s === 'in-progress') return 'badge-active';
|
|
@@ -194,12 +204,15 @@
|
|
|
194
204
|
var docLabel = (typeof doc === 'object' && doc.label) ? doc.label : docPath.split('/').pop();
|
|
195
205
|
var docStatus = (typeof doc === 'object' && doc.status) ? doc.status : null;
|
|
196
206
|
var viewPath = '/core/tools/workspace/app/viewer.html?file=' + encodeURIComponent(docPath);
|
|
207
|
+
var extPath = rawUrl(docPath);
|
|
197
208
|
var labelMatchesPath = docLabel === docPath || docLabel === docPath.split('/').pop();
|
|
198
|
-
var html = '<li><a class="doc-item" href="' + viewPath + '" onclick="if(!event.metaKey&&!event.ctrlKey){event.preventDefault();navTo(\'' + viewPath + '\')}" title="' + esc(docPath) + '">';
|
|
209
|
+
var html = '<li class="doc-row"><a class="doc-item" href="' + viewPath + '" onclick="if(!event.metaKey&&!event.ctrlKey){event.preventDefault();navTo(\'' + viewPath + '\')}" title="' + esc(docPath) + '">';
|
|
199
210
|
html += docStatusBadge(docStatus);
|
|
200
211
|
html += '<span class="doc-label">' + esc(docLabel) + '</span>';
|
|
201
212
|
if (!labelMatchesPath) html += '<span class="doc-path">' + esc(docPath) + '</span>';
|
|
202
|
-
html += '</a
|
|
213
|
+
html += '</a>';
|
|
214
|
+
html += '<a class="doc-ext" href="' + esc(extPath) + '" target="_blank" rel="noopener" title="Open in a full browser tab">↗</a>';
|
|
215
|
+
html += '</li>';
|
|
203
216
|
return html;
|
|
204
217
|
}
|
|
205
218
|
|
|
@@ -15,6 +15,18 @@
|
|
|
15
15
|
.back-btn:hover { border-color: var(--accent); }
|
|
16
16
|
.breadcrumb { color: var(--text-dim); font-size: 0.75rem; }
|
|
17
17
|
.breadcrumb span { color: var(--text); }
|
|
18
|
+
/* Pop the current document out to a full browser tab (escapes the workspace
|
|
19
|
+
* shell iframe). Pushed to the right of the toolbar. Hidden until a file is
|
|
20
|
+
* resolved. Styled like .back-btn — it's toolbar chrome. */
|
|
21
|
+
.open-ext { margin-left: auto; background: var(--bg-highlight); border: 1px solid var(--border); border-radius: 4px; padding: 0.3rem 0.7rem; color: var(--accent); font-family: inherit; font-size: 0.8rem; cursor: pointer; text-decoration: none; transition: border-color 0.12s; white-space: nowrap; }
|
|
22
|
+
.open-ext:hover { border-color: var(--accent); }
|
|
23
|
+
/* HTML documents (e.g. infographics) render as the actual page inside a
|
|
24
|
+
* full-bleed frame rather than as escaped source. Height fills the viewport
|
|
25
|
+
* below the sticky toolbar. */
|
|
26
|
+
.html-frame { display: block; width: 100%; height: calc(100vh - 2.9rem); border: 0; background: var(--bg); }
|
|
27
|
+
/* When the content area hosts a full-bleed HTML frame, drop the prose
|
|
28
|
+
* column constraints so the page uses the full width. */
|
|
29
|
+
.content.is-frame { max-width: none; margin: 0; padding: 0; }
|
|
18
30
|
/* Prose surface: humanist sans for long-form reading. The toolbar keeps
|
|
19
31
|
* the inherited monospace (it's chrome, not content), and code / pre /
|
|
20
32
|
* plain-text within .content explicitly opt back to monospace below.
|
|
@@ -64,6 +76,7 @@
|
|
|
64
76
|
<div class="toolbar">
|
|
65
77
|
<a class="back-btn" id="back-btn" onclick="goBack()">Back</a>
|
|
66
78
|
<div class="breadcrumb" id="breadcrumb">No document selected.</div>
|
|
79
|
+
<a class="open-ext" id="open-ext" target="_blank" rel="noopener" style="display:none" title="Open in a full browser tab">Open in new tab ↗</a>
|
|
67
80
|
</div>
|
|
68
81
|
<div class="content" id="content"><div class="empty-state">No document selected.</div></div>
|
|
69
82
|
<noscript><div style="padding:2rem; color:#a1a1aa; text-align:center;">Enable JavaScript to view documents.</div></noscript>
|
|
@@ -88,6 +101,13 @@
|
|
|
88
101
|
window.history.back();
|
|
89
102
|
}
|
|
90
103
|
function esc(s) { var el = document.createElement('span'); el.textContent = s; return el.innerHTML; }
|
|
104
|
+
// Direct-serve URL for a repo-relative path. The workspace server serves any
|
|
105
|
+
// repo file at its own path (.md is rendered to HTML, .html served as-is,
|
|
106
|
+
// everything else raw) — so this is the canonical "full page" URL for a
|
|
107
|
+
// document. Each segment is encoded so spaces / unicode survive.
|
|
108
|
+
function rawUrl(path) {
|
|
109
|
+
return '/' + String(path).split('/').map(encodeURIComponent).join('/');
|
|
110
|
+
}
|
|
91
111
|
var params = new URLSearchParams(window.location.search);
|
|
92
112
|
var filePath = params.get('file');
|
|
93
113
|
if (filePath) {
|
|
@@ -95,21 +115,42 @@
|
|
|
95
115
|
var breadHtml = parts.map(function(p, i) { return i === parts.length - 1 ? '<span>' + esc(p) + '</span>' : esc(p); }).join(' / ');
|
|
96
116
|
document.getElementById('breadcrumb').innerHTML = breadHtml;
|
|
97
117
|
document.title = parts[parts.length - 1] || 'Document Viewer';
|
|
118
|
+
// Wire the "Open in new tab" button to the document's full-page URL for
|
|
119
|
+
// every file type (md rendered, html raw, other raw).
|
|
120
|
+
var docUrl = rawUrl(filePath);
|
|
121
|
+
var openExt = document.getElementById('open-ext');
|
|
122
|
+
openExt.href = docUrl;
|
|
123
|
+
openExt.style.display = '';
|
|
98
124
|
var contentEl = document.getElementById('content');
|
|
99
|
-
contentEl.innerHTML = '<div class="empty-state">Loading...</div>';
|
|
100
125
|
var isMarkdown = /\.(md|markdown)$/i.test(filePath);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
126
|
+
var isHtml = /\.(html?|htm)$/i.test(filePath);
|
|
127
|
+
if (isHtml) {
|
|
128
|
+
// Render the HTML document as the actual page inside a full-bleed frame,
|
|
129
|
+
// not as escaped source. src points at the server's direct-serve route.
|
|
130
|
+
// Sandboxed: scripts run (infographics animate) and popups/same-origin
|
|
131
|
+
// are permitted for the operator's own repo content; no top-navigation.
|
|
132
|
+
var frame = document.createElement('iframe');
|
|
133
|
+
frame.className = 'html-frame';
|
|
134
|
+
frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-popups');
|
|
135
|
+
frame.src = docUrl;
|
|
136
|
+
contentEl.classList.add('is-frame');
|
|
137
|
+
contentEl.innerHTML = '';
|
|
138
|
+
contentEl.appendChild(frame);
|
|
139
|
+
} else {
|
|
140
|
+
contentEl.innerHTML = '<div class="empty-state">Loading...</div>';
|
|
141
|
+
fetch('/__api/file?path=' + encodeURIComponent(filePath)).then(function(r) {
|
|
142
|
+
if (!r.ok) throw new Error('File not found');
|
|
143
|
+
return r.text();
|
|
144
|
+
}).then(function(text) {
|
|
145
|
+
var body = text;
|
|
146
|
+
try { var j = JSON.parse(text); if (typeof j.content === 'string') body = j.content; } catch (e) { /* raw text */ }
|
|
147
|
+
if (isMarkdown && typeof window.renderMd === 'function') {
|
|
148
|
+
contentEl.innerHTML = renderMd(body);
|
|
149
|
+
} else {
|
|
150
|
+
contentEl.innerHTML = '<div class="plain-text">' + esc(body) + '</div>';
|
|
151
|
+
}
|
|
152
|
+
}).catch(function(err) { contentEl.innerHTML = '<div class="empty-state">Could not load document: ' + esc(err.message) + '</div>'; });
|
|
153
|
+
}
|
|
113
154
|
}
|
|
114
155
|
</script>
|
|
115
156
|
</body>
|
package/package.json
CHANGED