@lijian-ui/dsh-file-manager 0.1.21 → 0.2.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.
@@ -23,6 +23,7 @@ import type { PanelApi } from '../api.ts'
23
23
  import { filePicker, type PickState } from './file-picker.ts'
24
24
  import { ChevronDownIcon, ChevronRightIcon, CloseIcon, FileIcon, FolderIcon, FolderOpenIcon, SearchIcon } from '../components/icons.tsx'
25
25
  import { appendToDraft, insertFileReference, type RefSpan } from '../reference.ts'
26
+ import { t } from '../locales.ts'
26
27
  import pickerCss from '../styles/picker.module.css'
27
28
 
28
29
  /** Debounce for the workspace search box. */
@@ -194,9 +195,9 @@ export function FilePickerModal({
194
195
  {isDir && isOpen && (
195
196
  <div>
196
197
  {isLoading ? (
197
- <div className={pickerCss.note} style={{ paddingLeft: 8 + (depth + 1) * 16 }}>加载中…</div>
198
+ <div className={pickerCss.note} style={{ paddingLeft: 8 + (depth + 1) * 16 }}>{t('picker.loading')}</div>
198
199
  ) : children.length === 0 ? (
199
- <div className={pickerCss.note} style={{ paddingLeft: 8 + (depth + 1) * 16 }}>(空目录)</div>
200
+ <div className={pickerCss.note} style={{ paddingLeft: 8 + (depth + 1) * 16 }}>{t('picker.emptyDir')}</div>
200
201
  ) : (
201
202
  children.map((child) => renderNode(child, depth + 1))
202
203
  )}
@@ -218,8 +219,8 @@ export function FilePickerModal({
218
219
  <div className={pickerCss.modal} onClick={(e) => e.stopPropagation()}>
219
220
  <div className={pickerCss.header}>
220
221
  <div className={pickerCss.titleRow}>
221
- <span className={pickerCss.title}>引用项目文件</span>
222
- <button type="button" className={pickerCss.closeBtn} onClick={onClose} aria-label="关闭">
222
+ <span className={pickerCss.title}>{t('picker.title')}</span>
223
+ <button type="button" className={pickerCss.closeBtn} onClick={onClose} aria-label={t('picker.closeAria')}>
223
224
  <CloseIcon size={16} />
224
225
  </button>
225
226
  </div>
@@ -227,13 +228,13 @@ export function FilePickerModal({
227
228
  <span className={pickerCss.searchIcon}><SearchIcon size={14} /></span>
228
229
  <input
229
230
  className={pickerCss.searchInput}
230
- placeholder="搜索工作区文件…"
231
+ placeholder={t('picker.searchPlaceholder')}
231
232
  value={search}
232
233
  onChange={(e) => setSearch(e.target.value)}
233
234
  autoFocus
234
235
  />
235
236
  {search !== '' && (
236
- <button type="button" className={pickerCss.searchClear} onClick={() => setSearch('')} aria-label="清空">
237
+ <button type="button" className={pickerCss.searchClear} onClick={() => setSearch('')} aria-label={t('picker.clearAria')}>
237
238
  <CloseIcon size={13} />
238
239
  </button>
239
240
  )}
@@ -244,9 +245,9 @@ export function FilePickerModal({
244
245
  <div className={pickerCss.body}>
245
246
  {searchResults !== null ? (
246
247
  searching ? (
247
- <div className={pickerCss.note}>搜索中…</div>
248
+ <div className={pickerCss.note}>{t('explorer.search.searching')}</div>
248
249
  ) : searchResults.length === 0 ? (
249
- <div className={pickerCss.note}>没有匹配的文件</div>
250
+ <div className={pickerCss.note}>{t('explorer.search.empty')}</div>
250
251
  ) : (
251
252
  searchResults.map((hit) => {
252
253
  const isSel = selected.has(hit.path)
@@ -276,9 +277,9 @@ export function FilePickerModal({
276
277
  })
277
278
  )
278
279
  ) : rootLoading ? (
279
- <div className={pickerCss.note}>加载中…</div>
280
+ <div className={pickerCss.note}>{t('picker.loading')}</div>
280
281
  ) : rootEntries.length === 0 ? (
281
- <div className={pickerCss.note}>(空目录)</div>
282
+ <div className={pickerCss.note}>{t('picker.emptyDir')}</div>
282
283
  ) : (
283
284
  rootEntries.map((entry) => tree(entry, 0))
284
285
  )}
@@ -287,7 +288,7 @@ export function FilePickerModal({
287
288
  <div className={pickerCss.footer}>
288
289
  <div className={pickerCss.chips}>
289
290
  {selectedList.length === 0 ? (
290
- <span className={pickerCss.chipsEmpty}>选择文件或目录(可多选)</span>
291
+ <span className={pickerCss.chipsEmpty}>{t('picker.emptySelection')}</span>
291
292
  ) : (
292
293
  selectedList.map((pick) => (
293
294
  <span key={pick.path} className={pickerCss.chip}>
@@ -296,7 +297,7 @@ export function FilePickerModal({
296
297
  type="button"
297
298
  className={pickerCss.chipX}
298
299
  onClick={() => toggleSelect(pick.path, pick.isDir)}
299
- aria-label="移除"
300
+ aria-label={t('picker.removeAria')}
300
301
  >
301
302
  <CloseIcon size={11} />
302
303
  </button>
@@ -305,14 +306,14 @@ export function FilePickerModal({
305
306
  )}
306
307
  </div>
307
308
  <div className={pickerCss.footerActions}>
308
- <button type="button" className={pickerCss.cancelBtn} onClick={onClose}>取消</button>
309
+ <button type="button" className={pickerCss.cancelBtn} onClick={onClose}>{t('common.cancel')}</button>
309
310
  <button
310
311
  type="button"
311
312
  className={pickerCss.insertBtn}
312
313
  disabled={selectedList.length === 0}
313
314
  onClick={() => onInsert(selectedList)}
314
315
  >
315
- 插入{selectedList.length > 0 ? ` (${selectedList.length})` : ''}
316
+ {t('picker.insert')}{selectedList.length > 0 ? ` (${selectedList.length})` : ''}
316
317
  </button>
317
318
  </div>
318
319
  </div>
@@ -212,7 +212,7 @@ export function PreviewPanel({ stores, onAddFileReference }: { stores: PanelStor
212
212
  role="button"
213
213
  tabIndex={-1}
214
214
  >
215
- 添加到对话
215
+ {t('preview.addToChat')}
216
216
  </div>
217
217
  )}
218
218
  {closingIds !== null && (
@@ -13,6 +13,7 @@
13
13
  import type { FileRead, FsEntry, GitStatusView, PreviewContentType, SearchHit } from '../core/types.ts'
14
14
  import type { PanelApi } from './api.ts'
15
15
  import { detectContentType, isTextType, pdfPreviewUrl, tabIdOf } from './fileType.ts'
16
+ import { t } from './locales.ts'
16
17
  import {
17
18
  createDebounced, evictPreviewScopes, readJson, readStoredNumber, writeJson, writeStoredNumber,
18
19
  } from './persist.ts'
@@ -1133,7 +1134,7 @@ export function createPreviewStore(api: PanelApi): PreviewStore {
1133
1134
  ...item,
1134
1135
  loading: false,
1135
1136
  error: result.error.code === 'write-conflict'
1136
- ? '文件已在磁盘上被修改,保存冲突:请刷新后重试'
1137
+ ? t('preview.saveConflict')
1137
1138
  : result.error.message,
1138
1139
  }
1139
1140
  }
@@ -188,85 +188,13 @@
188
188
  }
189
189
  }
190
190
 
191
- /* Drag handles: 2px visual line, 6px + brand on hover/active. */
191
+ /* Drag handles: invisible, col-resize cursor only (like DSH native sidebar). */
192
192
  :global(.filemgr-explorer-handle),
193
193
  :global(.filemgr-preview-handle) {
194
194
  touch-action: none;
195
- }
196
- :global(.filemgr-explorer-handle::after),
197
- :global(.filemgr-preview-handle::after) {
198
- content: "";
199
- position: absolute;
200
- top: 0;
201
- bottom: 0;
202
- left: 0;
203
- width: 2px;
204
- border-radius: 9999px;
205
- background-color: var(--aion-bg-3);
206
- opacity: 0.9;
207
- transition: width 0.15s cubic-bezier(0.4, 0, 0.2, 1),
208
- background-color 0.15s cubic-bezier(0.4, 0, 0.2, 1);
209
- pointer-events: none;
210
- }
211
- :global(.filemgr-explorer-handle:hover::after),
212
- :global(.filemgr-explorer-handle:active::after),
213
- :global(.filemgr-preview-handle:hover::after),
214
- :global(.filemgr-preview-handle:active::after) {
215
- width: 6px;
216
- background-color: var(--aion-brand);
217
- }
218
- /* The preview handle's line sits at its right edge (its hit zone extends
219
- left); the explorer's at its left edge. */
220
- :global(.filemgr-preview-handle::after) {
221
- left: auto;
222
- right: 0;
223
- opacity: 0.3;
224
- }
225
- :global(.filemgr-preview-handle:hover::after),
226
- :global(.filemgr-preview-handle:active::after) {
227
- opacity: 1;
195
+ cursor: col-resize;
228
196
  }
229
197
 
230
- /* Floating expand button (explorer collapsed). Docked at the viewport's
231
- top-right corner, just below the shell header's bottom divider (the
232
- horizontal line under the "Session log" row) — the layout controller
233
- sets an inline top for that, so the button never overlaps the header
234
- chrome (issues #374 / #292). Above the columns (z 30): when the preview
235
- region is open it overlaps the rightmost column track, so anything
236
- lower would be painted under the opaque column and stop receiving
237
- clicks (issue #234 follow-up). Full-screen overlay drawers must render
238
- at the ROOT stacking context (z 100~1000) to cover it.
239
- The top below is only the pre-JS fallback (the chevron row); the
240
- controller overwrites it inline. */
241
- :global(.filemgr-floating-expand) {
242
- position: fixed;
243
- top: calc(6px + env(titlebar-area-height, 0px));
244
- right: 8px;
245
- width: 24px;
246
- height: 24px;
247
- display: none;
248
- align-items: center;
249
- justify-content: center;
250
- border: none;
251
- border-radius: 4px;
252
- background-color: var(--aion-bg-2);
253
- color: var(--aion-text-secondary);
254
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
255
- cursor: pointer;
256
- z-index: 100;
257
- transition: background-color 0.15s cubic-bezier(0.4, 0, 0.2, 1);
258
- }
259
- :global(.filemgr-floating-expand:hover) {
260
- background-color: var(--aion-bg-3);
261
- color: var(--aion-text-primary);
262
- }
263
- :global(.filemgr-floating-expand:active) {
264
- background-color: var(--aion-bg-active);
265
- }
266
- :global(.filemgr-floating-expand:focus-visible) {
267
- outline: 2px solid var(--aion-primary);
268
- outline-offset: 2px;
269
- }
270
198
 
271
199
  /* The collapse chevron inside the explorer tab bar (top-right). It rides
272
200
  the explorer column's stacking context (the column stacks at z 30), so