dsh-linked-workspace 0.8.0 → 0.8.1
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 +5 -0
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/docs/VERIFY.md +9 -0
- package/lib/client.js +188 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.1 — 2026-11-09
|
|
4
|
+
|
|
5
|
+
- **Image zoom**: the image preview gains zoom-out/zoom-in buttons, a percentage readout, “Fit to window” and “1:1” controls, plus `Ctrl/Cmd + wheel` zoom.
|
|
6
|
+
- **HTML preview**: `.html` and `.htm` files now render in a sandboxed iframe in the content panel. Scripts are disabled; the status bar notes this.
|
|
7
|
+
|
|
3
8
|
## 0.8.0 — 2026-11-09
|
|
4
9
|
|
|
5
10
|
- **Image previews**: PNG/JPG/GIF/WebP/BMP/ICO/AVIF/SVG now render in the content panel for both remote and local files (`media.read` / `local.media`, 24 MB cap).
|
package/README.en.md
CHANGED
|
@@ -61,7 +61,7 @@ The content panel now edits **Markdown / PDF / DOCX / XLSX / PPTX** files on bot
|
|
|
61
61
|
- **Refresh**: reload roots, git status and search.
|
|
62
62
|
- File-type icons for JS/TS/Python/JSON/Markdown/Shell/YAML/images/archives/config files, etc.
|
|
63
63
|
- Editor: line numbers, syntax highlighting (layered rendering), theme colors, `Ctrl/Cmd+S` save, conflict prompt (reload/force save), read-only protection for binary and >2 MB files, UTF-8/LF/CRLF/line-count status bar. The embedded content panel hides the path/save header; unsaved changes show a gray dot in the file tab. Read-only tabs render the file content but cannot be edited or saved.
|
|
64
|
-
- **Image and
|
|
64
|
+
- **Image, CSV and HTML preview**: PNG/JPG/GIF/WebP/BMP/ICO/AVIF/SVG render directly in the content panel (up to 24 MB) with toolbar zoom, `Ctrl/Cmd + wheel` zoom, fit-to-window and 1:1 actual-size controls. CSV/TSV opens as a table by default with a Table/Source toggle; the source view remains editable and saveable. Tables are capped at 1000 rows and 80 columns. HTML/HTM files render in a sandboxed iframe with scripts disabled.
|
|
65
65
|
- **Shortcuts**: `Ctrl/Cmd+S` saves the current file; `Ctrl/Cmd+W` closes the current tab (with a save/discard/cancel prompt when dirty); `Ctrl+Tab` / `Shift+Ctrl+Tab` cycles tabs; `Ctrl/Cmd+B` toggles the directory panel; `Ctrl/Cmd+Shift+F` opens file search.
|
|
66
66
|
- **Auto save**: enable it under Settings → Connections → Editor behavior. It saves about one second after you stop typing. When off, use `Ctrl+S`. Closing a dirty file always asks first instead of discarding silently.
|
|
67
67
|
- Multiple files can be open at once (up to 12 slots). A single click opens a preview tab and replaces the current unpinned preview; double-click pins the tab; clicking the same file focuses its existing tab. Local files use the same internal editor and can be edited and saved.
|
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
- **刷新**:重新拉取根目录、git 状态与搜索。
|
|
63
63
|
- 不同文件类型使用不同彩色图标(JS/TS/Python/JSON/Markdown/Shell/YAML/图片/压缩包/配置文件等)。
|
|
64
64
|
- 编辑器:行号、语法高亮(叠层渲染)、主题配色、`Ctrl/Cmd+S` 保存、冲突提示(重新加载/强制保存)、二进制与 >2 MB 文件只读保护、UTF-8/LF/CRLF/行数状态栏;内嵌内容面板不显示文件路径/保存工具条,未保存修改在文件标签前显示灰点。
|
|
65
|
-
-
|
|
65
|
+
- **图片、CSV 与网页预览**:PNG/JPG/GIF/WebP/BMP/ICO/AVIF/SVG 直接在内容面板按图片显示(最大 24 MB),支持按钮缩放、`Ctrl/Cmd + 滚轮` 缩放、适应窗口和 1:1 原始大小;CSV/TSV 默认按表格显示,可在「表格 / 源码」间切换,源码视图仍可编辑保存。超过 1000 行或 80 列时只展示截断后的表格。HTML/HTM 文件以沙箱 iframe 渲染网页预览(脚本禁用)。
|
|
66
66
|
- **快捷键**:`Ctrl/Cmd+S` 保存当前文件;`Ctrl/Cmd+W` 关闭当前标签(未保存时提示保存/放弃/取消);`Ctrl+Tab` / `Shift+Ctrl+Tab` 切换标签;`Ctrl/Cmd+B` 显示/隐藏目录面板;`Ctrl/Cmd+Shift+F` 打开文件搜索。
|
|
67
67
|
- **自动保存**:设置 → 连接 →「编辑器行为」中开启;停止输入约 1 秒后自动保存。关闭时使用 `Ctrl+S` 手动保存;未保存文件关闭前会弹窗,不会静默丢弃。
|
|
68
68
|
- **多个文件同时打开**:文件内容面板内最多同时打开 12 个标签;单击文件打开预览标签并替换未固定的预览,双击固定标签,重复点击同一文件聚焦原标签;本地文件与远端文件共用内部编辑器,本地文件可编辑保存。
|
package/docs/VERIFY.md
CHANGED
|
@@ -123,3 +123,12 @@ Host code (lib/) reloads live with `plugin_manager set_plugin include:linked-wor
|
|
|
123
123
|
5. 移动后目录树与内容面板一致;已打开的旧标签关闭,已修改未保存的文件阻止移动并提示先保存。
|
|
124
124
|
6. 目标目录已有同名文件时提示冲突(409),不覆盖;文件夹不能移动到自身或其子目录。
|
|
125
125
|
7. 移动选择器不能浏览到当前工作区/本地目录之外,根目录本身不可作为被移动项。
|
|
126
|
+
|
|
127
|
+
## 图片缩放与网页预览(0.8.1)
|
|
128
|
+
|
|
129
|
+
1. 打开 PNG/SVG:图片默认适应窗口;点「适应窗口」重新适配,「1:1」恢复原始像素。
|
|
130
|
+
2. 点 `−` / `+` 时缩放比例按 20% 步进,状态栏百分比同步;`Ctrl/Cmd + 滚轮` 可连续缩放,最小 5%、最大 800%。
|
|
131
|
+
3. 放大后图片区域出现滚动条,可查看四周;缩小后图片保持居中,不出现拉伸变形。
|
|
132
|
+
4. 本地与远端各打开一个 `.html`/`.htm`:内容面板以 iframe 渲染页面,状态栏显示 HTML、大小和「网页脚本已禁用」。
|
|
133
|
+
5. HTML 内联样式与静态标签正常显示;页面内脚本不执行,链接不会跳出 DSH 主界面。
|
|
134
|
+
6. 读取失败的 HTML(权限/过大/二进制)显示错误提示和重试按钮。
|
package/lib/client.js
CHANGED
|
@@ -2006,6 +2006,12 @@ function fileIconSpec(name, isDir) {
|
|
|
2006
2006
|
truncated: '内容已截断',
|
|
2007
2007
|
fileLoadError: '无法打开文件',
|
|
2008
2008
|
imageLoadError: '无法预览图片',
|
|
2009
|
+
imageZoomIn: '放大',
|
|
2010
|
+
imageZoomOut: '缩小',
|
|
2011
|
+
imageFit: '适应窗口',
|
|
2012
|
+
imageActualSize: '原始大小',
|
|
2013
|
+
htmlLoadError: '无法预览网页',
|
|
2014
|
+
htmlScriptsDisabled: '网页脚本已禁用',
|
|
2009
2015
|
previewOnly: '图片预览',
|
|
2010
2016
|
csvTable: '表格',
|
|
2011
2017
|
csvSource: '源码',
|
|
@@ -2329,6 +2335,12 @@ function fileIconSpec(name, isDir) {
|
|
|
2329
2335
|
truncated: 'Content truncated',
|
|
2330
2336
|
fileLoadError: 'Cannot open the file',
|
|
2331
2337
|
imageLoadError: 'Cannot preview this image',
|
|
2338
|
+
imageZoomIn: 'Zoom in',
|
|
2339
|
+
imageZoomOut: 'Zoom out',
|
|
2340
|
+
imageFit: 'Fit to window',
|
|
2341
|
+
imageActualSize: 'Actual size',
|
|
2342
|
+
htmlLoadError: 'Cannot preview this page',
|
|
2343
|
+
htmlScriptsDisabled: 'Page scripts are disabled',
|
|
2332
2344
|
previewOnly: 'Image preview',
|
|
2333
2345
|
csvTable: 'Table',
|
|
2334
2346
|
csvSource: 'Source',
|
|
@@ -2713,6 +2725,7 @@ function fileIconSpec(name, isDir) {
|
|
|
2713
2725
|
if (value === '.xlsx') return 'xlsx';
|
|
2714
2726
|
if (value === '.pptx') return 'pptx';
|
|
2715
2727
|
if (value === '.csv' || value === '.tsv') return 'csv';
|
|
2728
|
+
if (value === '.html' || value === '.htm') return 'html';
|
|
2716
2729
|
if (value === '.png' || value === '.jpg' || value === '.jpeg' || value === '.gif'
|
|
2717
2730
|
|| value === '.webp' || value === '.bmp' || value === '.ico' || value === '.avif'
|
|
2718
2731
|
|| value === '.svg') return 'image';
|
|
@@ -3213,8 +3226,14 @@ function fileIconSpec(name, isDir) {
|
|
|
3213
3226
|
'.rw-image-preview{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary)}',
|
|
3214
3227
|
'.rw-image-head{display:flex;align-items:center;gap:8px;padding:6px 10px;border-bottom:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);min-width:0;flex:none}',
|
|
3215
3228
|
'.rw-image-head .rw-actions{display:flex;align-items:center;gap:6px;flex:none}',
|
|
3216
|
-
'.rw-image-stage{display:
|
|
3217
|
-
'.rw-image{
|
|
3229
|
+
'.rw-image-stage{display:block;flex:1 1 auto;min-height:0;overflow:auto;padding:18px;text-align:center;background:repeating-conic-gradient(var(--dsw-alias-bg-layer-1) 0% 25%,var(--dsw-alias-bg-base) 0% 50%) 50%/18px 18px}',
|
|
3230
|
+
'.rw-image-canvas{display:inline-block;min-width:100%;min-height:100%;box-sizing:border-box}',
|
|
3231
|
+
'.rw-image{display:inline-block;max-width:none;max-height:none;vertical-align:top;border-radius:4px;box-shadow:0 2px 16px rgba(0,0,0,.18)}',
|
|
3232
|
+
'.rw-image-tools{display:flex;align-items:center;gap:4px;flex:none}',
|
|
3233
|
+
'.rw-image-zoom{min-width:52px;text-align:center;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary);font-size:12px}',
|
|
3234
|
+
'.rw-html-preview{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary)}',
|
|
3235
|
+
'.rw-html-stage{flex:1 1 auto;min-height:0;background:#fff;position:relative}',
|
|
3236
|
+
'.rw-html-frame{display:block;width:100%;height:100%;border:0;background:#fff}',
|
|
3218
3237
|
'.rw-csv-wrap{flex:1 1 auto;min-height:0;overflow:auto;background:var(--dsw-alias-bg-base)}',
|
|
3219
3238
|
'.rw-csv-table{border-collapse:collapse;font:12px/18px ui-monospace,"Cascadia Code",Consolas,monospace;min-width:100%}',
|
|
3220
3239
|
'.rw-csv-table th{position:sticky;top:0;z-index:1;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);font-weight:600;text-align:left;padding:4px 8px;border:1px solid var(--dsw-alias-border-l1);white-space:nowrap}',
|
|
@@ -3866,13 +3885,38 @@ function fileIconSpec(name, isDir) {
|
|
|
3866
3885
|
var state = useState({ loading: true, error: null, data: null });
|
|
3867
3886
|
var view = state[0];
|
|
3868
3887
|
var setView = state[1];
|
|
3888
|
+
var naturalState = useState(null);
|
|
3889
|
+
var natural = naturalState[0];
|
|
3890
|
+
var setNatural = naturalState[1];
|
|
3891
|
+
var zoomState = useState(1);
|
|
3892
|
+
var zoom = zoomState[0];
|
|
3893
|
+
var setZoom = zoomState[1];
|
|
3894
|
+
var stageRef = useRef(null);
|
|
3869
3895
|
var params = scopeParams(scope);
|
|
3896
|
+
var MIN_ZOOM = 0.05;
|
|
3897
|
+
var MAX_ZOOM = 8;
|
|
3898
|
+
|
|
3899
|
+
function clampZoom(value) {
|
|
3900
|
+
return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, value));
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
function zoomBy(factor) {
|
|
3904
|
+
setZoom(function (previous) { return clampZoom(previous * factor); });
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
function fitToStage() {
|
|
3908
|
+
if (!natural || !stageRef.current) return;
|
|
3909
|
+
var width = Math.max(40, stageRef.current.clientWidth - 36);
|
|
3910
|
+
var height = Math.max(40, stageRef.current.clientHeight - 36);
|
|
3911
|
+
setZoom(clampZoom(Math.min(1, width / natural.width, height / natural.height)));
|
|
3912
|
+
}
|
|
3870
3913
|
|
|
3871
3914
|
var load = useCallback(function () {
|
|
3872
3915
|
if (!filePath || (!local && !scope)) {
|
|
3873
3916
|
setView({ loading: false, error: new Error(t('noWorkspace')), data: null });
|
|
3874
3917
|
return;
|
|
3875
3918
|
}
|
|
3919
|
+
setNatural(null);
|
|
3876
3920
|
setView({ loading: true, error: null, data: null });
|
|
3877
3921
|
var request = local ? { path: filePath } : Object.assign({ path: filePath }, params);
|
|
3878
3922
|
apiCall(local ? 'local.media' : 'media.read', request).then(function (result) {
|
|
@@ -3884,13 +3928,59 @@ function fileIconSpec(name, isDir) {
|
|
|
3884
3928
|
|
|
3885
3929
|
useEffect(function () { load(); }, [load]);
|
|
3886
3930
|
|
|
3931
|
+
useEffect(function () {
|
|
3932
|
+
setZoom(1);
|
|
3933
|
+
setNatural(null);
|
|
3934
|
+
}, [filePath]);
|
|
3935
|
+
|
|
3936
|
+
useEffect(function () {
|
|
3937
|
+
if (!natural || !stageRef.current) return;
|
|
3938
|
+
var width = Math.max(40, stageRef.current.clientWidth - 36);
|
|
3939
|
+
var height = Math.max(40, stageRef.current.clientHeight - 36);
|
|
3940
|
+
setZoom(clampZoom(Math.min(1, width / natural.width, height / natural.height)));
|
|
3941
|
+
}, [natural]);
|
|
3942
|
+
|
|
3943
|
+
useEffect(function () {
|
|
3944
|
+
var stage = stageRef.current;
|
|
3945
|
+
if (!stage) return;
|
|
3946
|
+
function onWheel(event) {
|
|
3947
|
+
if (!event.ctrlKey && !event.metaKey) return;
|
|
3948
|
+
event.preventDefault();
|
|
3949
|
+
setZoom(function (previous) {
|
|
3950
|
+
return clampZoom(previous * (event.deltaY < 0 ? 1.2 : 1 / 1.2));
|
|
3951
|
+
});
|
|
3952
|
+
}
|
|
3953
|
+
stage.addEventListener('wheel', onWheel, { passive: false });
|
|
3954
|
+
return function () { stage.removeEventListener('wheel', onWheel); };
|
|
3955
|
+
}, [view.data]);
|
|
3956
|
+
|
|
3887
3957
|
var name = basename(filePath) || t('fileTabTitle');
|
|
3958
|
+
var zoomTools = view.data ? h('div', { className: 'rw-image-tools' },
|
|
3959
|
+
h('button', {
|
|
3960
|
+
type: 'button', className: 'rw-btn rw-btn-sm', title: t('imageZoomOut'),
|
|
3961
|
+
onClick: function () { zoomBy(1 / 1.2); },
|
|
3962
|
+
}, '−'),
|
|
3963
|
+
h('span', { className: 'rw-image-zoom' }, Math.round(zoom * 100) + '%'),
|
|
3964
|
+
h('button', {
|
|
3965
|
+
type: 'button', className: 'rw-btn rw-btn-sm', title: t('imageZoomIn'),
|
|
3966
|
+
onClick: function () { zoomBy(1.2); },
|
|
3967
|
+
}, '+'),
|
|
3968
|
+
h('button', {
|
|
3969
|
+
type: 'button', className: 'rw-btn rw-btn-sm', title: t('imageFit'),
|
|
3970
|
+
onClick: fitToStage,
|
|
3971
|
+
}, t('imageFit')),
|
|
3972
|
+
h('button', {
|
|
3973
|
+
type: 'button', className: 'rw-btn rw-btn-sm', title: t('imageActualSize'),
|
|
3974
|
+
onClick: function () { setZoom(1); },
|
|
3975
|
+
}, '1:1')) : null;
|
|
3976
|
+
|
|
3888
3977
|
var header = h('div', { className: 'rw-image-head' },
|
|
3889
3978
|
h(FileGlyph, { name: name, size: 15 }),
|
|
3890
3979
|
h('div', { className: 'rw-editor-title' },
|
|
3891
3980
|
h('span', { className: 'rw-editor-name', title: name }, name),
|
|
3892
3981
|
h('span', { className: 'rw-editor-path', title: filePath }, filePath)),
|
|
3893
3982
|
h('div', { className: 'rw-actions' },
|
|
3983
|
+
zoomTools,
|
|
3894
3984
|
h('button', { type: 'button', className: 'rw-btn rw-btn-sm', onClick: load, disabled: view.loading }, IconRefresh(12), t('reload'))));
|
|
3895
3985
|
|
|
3896
3986
|
if (view.loading) {
|
|
@@ -3904,24 +3994,104 @@ function fileIconSpec(name, isDir) {
|
|
|
3904
3994
|
h('button', { type: 'button', className: 'rw-btn', onClick: load }, IconRefresh(13), t('retry'))));
|
|
3905
3995
|
}
|
|
3906
3996
|
var data = view.data;
|
|
3997
|
+
var imageStyle = natural
|
|
3998
|
+
? { width: natural.width * zoom + 'px', height: natural.height * zoom + 'px' }
|
|
3999
|
+
: { maxWidth: '100%', maxHeight: '100%' };
|
|
3907
4000
|
return h('div', { className: 'rw-image-preview' },
|
|
3908
4001
|
header,
|
|
3909
|
-
h('div', { className: 'rw-image-stage' },
|
|
3910
|
-
h('
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
4002
|
+
h('div', { className: 'rw-image-stage', ref: stageRef },
|
|
4003
|
+
h('div', { className: 'rw-image-canvas' },
|
|
4004
|
+
h('img', {
|
|
4005
|
+
className: 'rw-image',
|
|
4006
|
+
src: 'data:' + data.mime + ';base64,' + data.base64,
|
|
4007
|
+
alt: name,
|
|
4008
|
+
style: imageStyle,
|
|
4009
|
+
onLoad: function (event) {
|
|
4010
|
+
var image = event.currentTarget;
|
|
4011
|
+
if (image.naturalWidth && image.naturalHeight) {
|
|
4012
|
+
setNatural({ width: image.naturalWidth, height: image.naturalHeight });
|
|
4013
|
+
}
|
|
4014
|
+
},
|
|
4015
|
+
onError: function () {
|
|
4016
|
+
setView({ loading: false, error: new Error(t('imageLoadError')), data: null });
|
|
4017
|
+
},
|
|
4018
|
+
}))),
|
|
3918
4019
|
h('div', { className: 'rw-editor-status' },
|
|
3919
4020
|
h('span', { className: 'rw-status-chip' }, data.mime || 'image'),
|
|
3920
4021
|
h('span', { className: 'rw-status-chip' }, formatBytes(data.size || 0)),
|
|
4022
|
+
h('span', { className: 'rw-status-chip' }, Math.round(zoom * 100) + '%'),
|
|
3921
4023
|
h('span', { className: 'rw-fill', style: { flex: '1 1 auto' } }),
|
|
3922
4024
|
h('span', { className: 'rw-status-chip' }, t('previewOnly'))));
|
|
3923
4025
|
}
|
|
3924
4026
|
|
|
4027
|
+
function HtmlPreview(props) {
|
|
4028
|
+
var ctx = props.ctx;
|
|
4029
|
+
var t = props.t;
|
|
4030
|
+
var locale = props.locale;
|
|
4031
|
+
var scope = props.scope;
|
|
4032
|
+
var local = props.local === true;
|
|
4033
|
+
var filePath = props.path;
|
|
4034
|
+
var state = useState({ loading: true, error: null, data: null });
|
|
4035
|
+
var view = state[0];
|
|
4036
|
+
var setView = state[1];
|
|
4037
|
+
var params = scopeParams(scope);
|
|
4038
|
+
|
|
4039
|
+
var load = useCallback(function () {
|
|
4040
|
+
if (!filePath || (!local && !scope)) {
|
|
4041
|
+
setView({ loading: false, error: new Error(t('noWorkspace')), data: null });
|
|
4042
|
+
return;
|
|
4043
|
+
}
|
|
4044
|
+
setView({ loading: true, error: null, data: null });
|
|
4045
|
+
var request = local ? { path: filePath } : Object.assign({ path: filePath }, params);
|
|
4046
|
+
apiCall(local ? 'local.read' : 'fs.read', request).then(function (result) {
|
|
4047
|
+
if (result.binary || result.tooLarge) {
|
|
4048
|
+
setView({ loading: false, error: new Error(t('htmlLoadError')), data: null });
|
|
4049
|
+
return;
|
|
4050
|
+
}
|
|
4051
|
+
setView({ loading: false, error: null, data: result });
|
|
4052
|
+
}).catch(function (error) {
|
|
4053
|
+
setView({ loading: false, error: error, data: null });
|
|
4054
|
+
});
|
|
4055
|
+
}, [scopeKeyOf(scope), filePath, local]);
|
|
4056
|
+
|
|
4057
|
+
useEffect(function () { load(); }, [load]);
|
|
4058
|
+
|
|
4059
|
+
var name = basename(filePath) || t('fileTabTitle');
|
|
4060
|
+
var header = h('div', { className: 'rw-image-head' },
|
|
4061
|
+
h(FileGlyph, { name: name, size: 15 }),
|
|
4062
|
+
h('div', { className: 'rw-editor-title' },
|
|
4063
|
+
h('span', { className: 'rw-editor-name', title: name }, name),
|
|
4064
|
+
h('span', { className: 'rw-editor-path', title: filePath }, filePath)),
|
|
4065
|
+
h('div', { className: 'rw-actions' },
|
|
4066
|
+
h('button', { type: 'button', className: 'rw-btn rw-btn-sm', onClick: load, disabled: view.loading }, IconRefresh(12), t('reload'))));
|
|
4067
|
+
|
|
4068
|
+
if (view.loading) {
|
|
4069
|
+
return h('div', { className: 'rw-html-preview' }, header,
|
|
4070
|
+
h('div', { className: 'rw-editor-message' }, Spinner(14), t('loading')));
|
|
4071
|
+
}
|
|
4072
|
+
if (view.error) {
|
|
4073
|
+
return h('div', { className: 'rw-html-preview' }, header,
|
|
4074
|
+
h('div', { className: 'rw-editor-message' },
|
|
4075
|
+
h(Banner, { kind: 'error' }, t('htmlLoadError') + ':' + messageOf(view.error, t, locale)),
|
|
4076
|
+
h('button', { type: 'button', className: 'rw-btn', onClick: load }, IconRefresh(13), t('retry'))));
|
|
4077
|
+
}
|
|
4078
|
+
var data = view.data;
|
|
4079
|
+
return h('div', { className: 'rw-html-preview' },
|
|
4080
|
+
header,
|
|
4081
|
+
h('div', { className: 'rw-html-stage' },
|
|
4082
|
+
h('iframe', {
|
|
4083
|
+
className: 'rw-html-frame',
|
|
4084
|
+
title: name,
|
|
4085
|
+
sandbox: '',
|
|
4086
|
+
srcDoc: data.content || '',
|
|
4087
|
+
})),
|
|
4088
|
+
h('div', { className: 'rw-editor-status' },
|
|
4089
|
+
h('span', { className: 'rw-status-chip' }, 'HTML'),
|
|
4090
|
+
h('span', { className: 'rw-status-chip' }, formatBytes(data.size || 0)),
|
|
4091
|
+
h('span', { className: 'rw-fill', style: { flex: '1 1 auto' } }),
|
|
4092
|
+
h('span', { className: 'rw-status-chip' }, t('htmlScriptsDisabled'))));
|
|
4093
|
+
}
|
|
4094
|
+
|
|
3925
4095
|
// ── editor ─────────────────────────────────────────────────────────────
|
|
3926
4096
|
|
|
3927
4097
|
function colorForToken(colors, type) {
|
|
@@ -5959,6 +6129,13 @@ function fileIconSpec(name, isDir) {
|
|
|
5959
6129
|
path: activeFile.path,
|
|
5960
6130
|
local: activeFile.side === 'local',
|
|
5961
6131
|
})
|
|
6132
|
+
: activeDocKind === 'html'
|
|
6133
|
+
? h(HtmlPreview, {
|
|
6134
|
+
ctx: ctx, t: t, locale: locale,
|
|
6135
|
+
scope: activeFile.side === 'remote' ? sourceScopeOf(selected) : undefined,
|
|
6136
|
+
path: activeFile.path,
|
|
6137
|
+
local: activeFile.side === 'local',
|
|
6138
|
+
})
|
|
5962
6139
|
: activeDocKind && activeDocKind !== 'csv'
|
|
5963
6140
|
? h(DocumentPane, {
|
|
5964
6141
|
ctx: ctx, t: t, locale: locale,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-linked-workspace",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Linked local ⇄ remote workspaces and SSH-backed remote directories for DeepSeek Harness: theme-aware split editor, file-type icons, and an agent tool that edits remote files directly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|