mock-service-cli 4.11.1 → 4.12.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/README.md +32 -1
- package/dist/api-overview.html +614 -587
- package/dist/file-explorer.html +79 -36
- package/dist/meta.json +2415 -2239
- package/dist/runtime.js +14 -14
- package/package.json +1 -1
package/dist/file-explorer.html
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
.container {
|
|
32
|
-
max-width:
|
|
32
|
+
max-width: 1400px;
|
|
33
33
|
margin: 0 auto;
|
|
34
34
|
display: flex;
|
|
35
35
|
flex-direction: column;
|
|
@@ -95,26 +95,31 @@
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
.toolbar {
|
|
98
|
-
padding:
|
|
98
|
+
padding: 5px 6px;
|
|
99
99
|
border-bottom: 1px solid #eee;
|
|
100
100
|
display: flex;
|
|
101
|
-
gap:
|
|
101
|
+
gap: 4px;
|
|
102
102
|
align-items: center;
|
|
103
103
|
flex-wrap: wrap;
|
|
104
104
|
flex-shrink: 0;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/* Keep the dynamic counter from re-wrapping the toolbar while it updates. */
|
|
107
108
|
.stats-info {
|
|
108
109
|
font-size: 12px;
|
|
109
110
|
color: #666;
|
|
110
|
-
padding: 4px
|
|
111
|
+
padding: 4px 4px;
|
|
111
112
|
background: #f8f8f8;
|
|
112
113
|
border-radius: 4px;
|
|
114
|
+
min-width: 168px;
|
|
115
|
+
text-align: center;
|
|
116
|
+
white-space: nowrap;
|
|
117
|
+
font-variant-numeric: tabular-nums;
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
.search-box {
|
|
116
|
-
flex: 1;
|
|
117
|
-
min-width:
|
|
121
|
+
flex: 1 1 120px;
|
|
122
|
+
min-width: 120px;
|
|
118
123
|
}
|
|
119
124
|
|
|
120
125
|
.search-box input {
|
|
@@ -138,7 +143,7 @@
|
|
|
138
143
|
}
|
|
139
144
|
|
|
140
145
|
.sort-select {
|
|
141
|
-
padding: 6px
|
|
146
|
+
padding: 6px 8px;
|
|
142
147
|
border: 1px solid #ddd;
|
|
143
148
|
border-radius: 4px;
|
|
144
149
|
font-size: 12px;
|
|
@@ -152,7 +157,7 @@
|
|
|
152
157
|
}
|
|
153
158
|
|
|
154
159
|
.sort-order-btn {
|
|
155
|
-
padding: 6px
|
|
160
|
+
padding: 6px 4px;
|
|
156
161
|
background: #f0f0f0;
|
|
157
162
|
border: none;
|
|
158
163
|
border-radius: 4px;
|
|
@@ -166,7 +171,8 @@
|
|
|
166
171
|
}
|
|
167
172
|
|
|
168
173
|
.toolbar-action-btn {
|
|
169
|
-
padding: 6px
|
|
174
|
+
padding: 6px 4px;
|
|
175
|
+
white-space: nowrap;
|
|
170
176
|
background: #f0f0f0;
|
|
171
177
|
border: none;
|
|
172
178
|
border-radius: 4px;
|
|
@@ -210,7 +216,8 @@
|
|
|
210
216
|
}
|
|
211
217
|
|
|
212
218
|
.back-btn {
|
|
213
|
-
padding: 6px
|
|
219
|
+
padding: 6px 4px;
|
|
220
|
+
white-space: nowrap;
|
|
214
221
|
background: #f0f0f0;
|
|
215
222
|
border: none;
|
|
216
223
|
border-radius: 4px;
|
|
@@ -239,7 +246,8 @@
|
|
|
239
246
|
}
|
|
240
247
|
|
|
241
248
|
.view-btn {
|
|
242
|
-
padding: 4px
|
|
249
|
+
padding: 4px 4px;
|
|
250
|
+
white-space: nowrap;
|
|
243
251
|
background: transparent;
|
|
244
252
|
border: none;
|
|
245
253
|
border-radius: 3px;
|
|
@@ -254,7 +262,7 @@
|
|
|
254
262
|
}
|
|
255
263
|
|
|
256
264
|
.refresh-btn {
|
|
257
|
-
padding:
|
|
265
|
+
padding: 4px 4px;
|
|
258
266
|
background: #f0f0f0;
|
|
259
267
|
border: none;
|
|
260
268
|
border-radius: 4px;
|
|
@@ -268,7 +276,7 @@
|
|
|
268
276
|
}
|
|
269
277
|
|
|
270
278
|
.toggle-hidden-btn {
|
|
271
|
-
padding:
|
|
279
|
+
padding: 4px 4px;
|
|
272
280
|
background: #f0f0f0;
|
|
273
281
|
border: none;
|
|
274
282
|
border-radius: 4px;
|
|
@@ -302,7 +310,7 @@
|
|
|
302
310
|
}
|
|
303
311
|
|
|
304
312
|
.context-menu-item {
|
|
305
|
-
padding:
|
|
313
|
+
padding: 5px 8px;
|
|
306
314
|
cursor: pointer;
|
|
307
315
|
font-size: 14px;
|
|
308
316
|
color: #333;
|
|
@@ -379,8 +387,8 @@
|
|
|
379
387
|
display: flex;
|
|
380
388
|
flex-direction: column;
|
|
381
389
|
align-items: center;
|
|
382
|
-
padding:
|
|
383
|
-
border-radius:
|
|
390
|
+
padding: 5px 4px;
|
|
391
|
+
border-radius: 4px;
|
|
384
392
|
cursor: pointer;
|
|
385
393
|
transition: background-color 0.2s;
|
|
386
394
|
}
|
|
@@ -1052,12 +1060,7 @@
|
|
|
1052
1060
|
>
|
|
1053
1061
|
压缩选中
|
|
1054
1062
|
</button>
|
|
1055
|
-
<button
|
|
1056
|
-
class="toggle-hidden-btn"
|
|
1057
|
-
id="toggleHiddenBtn"
|
|
1058
|
-
onclick="toggleHiddenFiles()"
|
|
1059
|
-
title="显示/隐藏隐藏文件"
|
|
1060
|
-
>
|
|
1063
|
+
<button class="toggle-hidden-btn" id="toggleHiddenBtn" onclick="toggleHiddenFiles()" title="显示隐藏文件">
|
|
1061
1064
|
👁️
|
|
1062
1065
|
</button>
|
|
1063
1066
|
<button
|
|
@@ -1083,8 +1086,8 @@
|
|
|
1083
1086
|
</button>
|
|
1084
1087
|
</div>
|
|
1085
1088
|
<div class="view-toggle">
|
|
1086
|
-
<button class="view-btn
|
|
1087
|
-
<button class="view-btn" id="listViewBtn" onclick="setView('list')">列表</button>
|
|
1089
|
+
<button class="view-btn" id="gridViewBtn" onclick="setView('grid')">网格</button>
|
|
1090
|
+
<button class="view-btn active" id="listViewBtn" onclick="setView('list')">列表</button>
|
|
1088
1091
|
</div>
|
|
1089
1092
|
</div>
|
|
1090
1093
|
<div class="file-grid" id="fileGrid"></div>
|
|
@@ -1123,14 +1126,18 @@
|
|
|
1123
1126
|
<div class="context-menu-item edit-only edit-flex" id="ctxExtract" onclick="handleContextMenuAction('extract')">
|
|
1124
1127
|
📦 解压到当前目录
|
|
1125
1128
|
</div>
|
|
1126
|
-
<div
|
|
1129
|
+
<div
|
|
1130
|
+
class="context-menu-item edit-only edit-flex"
|
|
1131
|
+
id="ctxArchivePreview"
|
|
1132
|
+
onclick="handleContextMenuAction('archivePreview')"
|
|
1133
|
+
>
|
|
1127
1134
|
🔎 查看压缩包
|
|
1128
1135
|
</div>
|
|
1129
1136
|
</div>
|
|
1130
1137
|
|
|
1131
1138
|
<script>
|
|
1132
1139
|
let currentPath = '/';
|
|
1133
|
-
let currentView = '
|
|
1140
|
+
let currentView = 'list';
|
|
1134
1141
|
let currentFiles = [];
|
|
1135
1142
|
let allFiles = [];
|
|
1136
1143
|
let searchQuery = '';
|
|
@@ -1418,7 +1425,12 @@
|
|
|
1418
1425
|
function getStoredExplorerPath() {
|
|
1419
1426
|
try {
|
|
1420
1427
|
const stored = JSON.parse(sessionStorage.getItem(EXPLORER_PATH_STORAGE_KEY));
|
|
1421
|
-
if (
|
|
1428
|
+
if (
|
|
1429
|
+
stored &&
|
|
1430
|
+
stored.rootId === explorerRootId &&
|
|
1431
|
+
typeof stored.path === 'string' &&
|
|
1432
|
+
stored.path.startsWith('/')
|
|
1433
|
+
) {
|
|
1422
1434
|
return stored.path;
|
|
1423
1435
|
}
|
|
1424
1436
|
} catch (error) {
|
|
@@ -1558,9 +1570,17 @@
|
|
|
1558
1570
|
hideContextMenu();
|
|
1559
1571
|
}
|
|
1560
1572
|
|
|
1573
|
+
function isSameExplorerPath(left, right) {
|
|
1574
|
+
const normalize = value => String(value || '/').replace(/\/+$/, '') || '/';
|
|
1575
|
+
return normalize(left) === normalize(right);
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1561
1578
|
async function loadDirectory(path, { fallbackToRoot = false } = {}) {
|
|
1562
1579
|
const fileGrid = document.getElementById('fileGrid');
|
|
1563
1580
|
const oldCurrentPath = currentPath;
|
|
1581
|
+
// Refresh buttons and post-operation reloads revisit the same directory:
|
|
1582
|
+
// keep the active search instead of silently resetting the filter.
|
|
1583
|
+
const keepSearch = isSameExplorerPath(path, currentPath);
|
|
1564
1584
|
const requestId = ++directoryRequestId;
|
|
1565
1585
|
|
|
1566
1586
|
if (directoryRequestController) {
|
|
@@ -1592,10 +1612,15 @@
|
|
|
1592
1612
|
storeExplorerPath(currentPath);
|
|
1593
1613
|
allFiles = data.files || [];
|
|
1594
1614
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1615
|
+
const searchInput = document.getElementById('searchInput');
|
|
1616
|
+
if (!keepSearch) {
|
|
1617
|
+
if (searchInput) searchInput.value = '';
|
|
1618
|
+
searchQuery = '';
|
|
1619
|
+
} else if (searchInput) {
|
|
1620
|
+
// A refresh may land inside the search debounce window; resync so the
|
|
1621
|
+
// filtered view matches the visible query immediately.
|
|
1622
|
+
searchQuery = searchInput.value;
|
|
1597
1623
|
}
|
|
1598
|
-
searchQuery = '';
|
|
1599
1624
|
|
|
1600
1625
|
renderBreadcrumb(data.currentPath, data.parentPath);
|
|
1601
1626
|
processAndRenderFiles();
|
|
@@ -1700,7 +1725,7 @@
|
|
|
1700
1725
|
if (showHiddenFiles) {
|
|
1701
1726
|
btn.classList.add('active');
|
|
1702
1727
|
btn.innerHTML = '👁️';
|
|
1703
|
-
btn.title = '
|
|
1728
|
+
btn.title = '不再显示隐藏文件';
|
|
1704
1729
|
} else {
|
|
1705
1730
|
btn.classList.remove('active');
|
|
1706
1731
|
btn.innerHTML = '👁️🗨️';
|
|
@@ -1767,7 +1792,9 @@
|
|
|
1767
1792
|
|
|
1768
1793
|
function isArchiveFile(filePath) {
|
|
1769
1794
|
const lowerPath = String(filePath || '').toLowerCase();
|
|
1770
|
-
return archiveCapabilities.readExtensions.some(extension =>
|
|
1795
|
+
return archiveCapabilities.readExtensions.some(extension =>
|
|
1796
|
+
lowerPath.endsWith(`.${String(extension).toLowerCase()}`)
|
|
1797
|
+
);
|
|
1771
1798
|
}
|
|
1772
1799
|
|
|
1773
1800
|
async function createArchiveFromSelection(sourceOverride) {
|
|
@@ -1776,7 +1803,13 @@
|
|
|
1776
1803
|
showToast('请先选择要压缩的文件或文件夹');
|
|
1777
1804
|
return;
|
|
1778
1805
|
}
|
|
1779
|
-
const defaultName =
|
|
1806
|
+
const defaultName =
|
|
1807
|
+
sources.length === 1
|
|
1808
|
+
? sources[0]
|
|
1809
|
+
.split('/')
|
|
1810
|
+
.pop()
|
|
1811
|
+
.replace(/\.[^.]+$/, '')
|
|
1812
|
+
: 'archive';
|
|
1780
1813
|
const name = window.prompt('压缩包名称', defaultName);
|
|
1781
1814
|
if (name === null || !name.trim()) return;
|
|
1782
1815
|
const formats = archiveCapabilities.createFormats;
|
|
@@ -1800,7 +1833,9 @@
|
|
|
1800
1833
|
}
|
|
1801
1834
|
|
|
1802
1835
|
async function extractArchive(filePath) {
|
|
1803
|
-
const confirmed = window.confirm(
|
|
1836
|
+
const confirmed = window.confirm(
|
|
1837
|
+
`将 “${filePath.split('/').pop()}” 解压到当前目录吗?已有同名文件将导致整个操作失败。`
|
|
1838
|
+
);
|
|
1804
1839
|
if (!confirmed) return;
|
|
1805
1840
|
await startArchiveJob({ operation: 'extract', path: filePath, destinationPath: currentPath });
|
|
1806
1841
|
}
|
|
@@ -1889,8 +1924,16 @@
|
|
|
1889
1924
|
<div class="binary-info">${entries.length} 项 · 解压后 ${formatSize(preview.totalSize || 0)} · 包内数据 ${formatSize(preview.totalPackedSize || 0)}</div>
|
|
1890
1925
|
<div class="text-preview">${entries
|
|
1891
1926
|
.map(entry => {
|
|
1892
|
-
const
|
|
1893
|
-
const
|
|
1927
|
+
const normalizedPath = String(entry.path || '').replace(/\/+$/, '');
|
|
1928
|
+
const segments = normalizedPath.split('/');
|
|
1929
|
+
const depth = segments.length - 1;
|
|
1930
|
+
const icon = entry.isSymbolicLink ? '🔗' : entry.isDirectory ? '📁' : '📄';
|
|
1931
|
+
const suffix = entry.isDirectory
|
|
1932
|
+
? '/'
|
|
1933
|
+
: entry.isSymbolicLink
|
|
1934
|
+
? ' (链接)'
|
|
1935
|
+
: ` (${formatSize(entry.size || 0)})`;
|
|
1936
|
+
const label = `${icon} ${segments.pop()}${suffix}`;
|
|
1894
1937
|
return `<div style="padding-left:${depth * 16}px">${escapeHtml(label)}</div>`;
|
|
1895
1938
|
})
|
|
1896
1939
|
.join('')}</div>
|