feat: remove Deleted-projects section (delete = permanent removal, files kept) + refresh button on iframe tab windows
This commit is contained in:
@@ -1352,13 +1352,7 @@ function buildCustomLayoutPrompt(req: string): string {
|
||||
})
|
||||
}
|
||||
|
||||
/** 重新添加已删除的入驻项目(删除 ↔ 重新添加 完全可逆) */
|
||||
const readdProject = (id: string) => {
|
||||
persistProjects((prev) => ({
|
||||
...prev,
|
||||
removed: prev.removed.filter((x) => x !== id),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
// ── 快捷方式(表单已移除,仅保留存量条目的删除能力) ──
|
||||
const removeShortcut = (id: string) => {
|
||||
@@ -1441,13 +1435,20 @@ function buildCustomLayoutPrompt(req: string): string {
|
||||
return def ? def.id : '2h'
|
||||
}
|
||||
|
||||
// ── 删除(全部走二次确认;常驻项目 = 移出工作台,插件不卸载) ──
|
||||
// ── 删除(全部走二次确认;项目彻底移出工作台:对话与项目文件均保留,仅清理本地关联状态) ──
|
||||
const removeProject = (id: string) => {
|
||||
persistProjects((prev) => ({
|
||||
...prev,
|
||||
removed: prev.removed.includes(id) ? prev.removed : [...prev.removed, id],
|
||||
hidden: prev.hidden.filter((x) => x !== id),
|
||||
}))
|
||||
persistProjects((prev) => {
|
||||
const next = {
|
||||
...prev,
|
||||
removed: prev.removed.includes(id) ? prev.removed : [...prev.removed, id],
|
||||
hidden: prev.hidden.filter((x) => x !== id),
|
||||
order: prev.order.filter((x) => x !== id),
|
||||
}
|
||||
if (next.bindings[id]) { next.bindings = { ...next.bindings }; delete next.bindings[id] }
|
||||
if (next.folders[id]) { next.folders = { ...next.folders }; delete next.folders[id] }
|
||||
if (next.views[id]) { next.views = { ...next.views }; delete next.views[id] }
|
||||
return next
|
||||
})
|
||||
}
|
||||
const askDelete = (kind: 'layout' | 'shortcut' | 'project', id: string, name: string) => {
|
||||
setRequestDelete({ kind, id, name })
|
||||
@@ -1925,18 +1926,6 @@ function buildCustomLayoutPrompt(req: string): string {
|
||||
<button type="button" className="dsh-wt_manageBtn" title={t('manage.deleteShortcut')} onClick={() => askDelete('shortcut', s.id, s.name)}>✕</button>
|
||||
</div>
|
||||
))}
|
||||
{projects.removed.length > 0 && (
|
||||
<>
|
||||
<div className="dsh-wt_menuSep" />
|
||||
<span className="dsh-wt_menuLabel">{t('manage.removed')}</span>
|
||||
{projects.removed.map((rid) => (
|
||||
<div key={rid} className="dsh-wt_manageRow dsh-wt_manageRowOff dsh-wt_manageRowRemoved">
|
||||
<span className="dsh-wt_manageScName">{projects.nameOverrides[rid] ?? metas[rid]?.name ?? rid}</span>
|
||||
<button type="button" className="dsh-wt_manageBtn" title={t('manage.readd')} onClick={() => readdProject(rid)}>↺</button>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ export const zh = {
|
||||
'confirm.delete': '删除',
|
||||
'confirm.layoutBody': '确定删除「{name}」?该工作区及其布局将被移除,此操作不可恢复。',
|
||||
'confirm.shortcutBody': '确定删除快捷方式「{name}」?此操作不可恢复。',
|
||||
'confirm.projectBody': '确定从工作台删除「{name}」?插件本身不会被卸载,「恢复默认」也不会复活它;之后可在设置里「已删除的项目」中重新添加。',
|
||||
'confirm.projectBody': '「{name}」将从工作台彻底移除(对话与项目文件都保留,只是不再显示项目卡片)。',
|
||||
'pane.browser': '浏览器',
|
||||
'pane.explorer': '资源管理器',
|
||||
'pane.scm': '源代码管理',
|
||||
@@ -189,7 +189,7 @@ export const en = {
|
||||
'confirm.delete': 'Delete',
|
||||
'confirm.layoutBody': 'Delete "{name}"? Its workspace and layout will be removed. This cannot be undone.',
|
||||
'confirm.shortcutBody': 'Delete shortcut "{name}"? This cannot be undone.',
|
||||
'confirm.projectBody': 'Remove "{name}" from the worktable? The plugin stays installed; Reset defaults will NOT bring it back — re-add it later under Removed projects in Settings.',
|
||||
'confirm.projectBody': '"{name}" will be removed from the worktable permanently (the conversation and project files are kept).',
|
||||
'pane.browser': 'Browser',
|
||||
'pane.explorer': 'Explorer',
|
||||
'pane.scm': 'Source control',
|
||||
|
||||
@@ -891,6 +891,23 @@ function BrowserPane() {
|
||||
)
|
||||
}
|
||||
|
||||
/** iframe 内容标签(网页/站点产物):右上角常驻刷新按钮(重挂载整页刷新,跨域可靠) */
|
||||
function IframePane(props: { url: string; title?: string }) {
|
||||
const [reloadKey, setReloadKey] = useState(0)
|
||||
return (
|
||||
<div className="dsh-wt_frameWrap">
|
||||
<iframe key={reloadKey} className="dsh-wt_paneFrame" src={props.url} title={props.title ?? ''} />
|
||||
<button
|
||||
type="button"
|
||||
className="dsh-wt_frameRefresh"
|
||||
title={T('pane.refresh')}
|
||||
aria-label={T('pane.refresh')}
|
||||
onClick={() => setReloadKey((k) => k + 1)}
|
||||
>↻</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** 动画播放窗:iframe 壳 + 地址栏(站内自带项目/场景列表、播放、画幅切换、导出等全部控件) */
|
||||
function AnimPane() {
|
||||
const [url, setUrl] = useState('')
|
||||
@@ -1534,7 +1551,7 @@ function CustomPane(props: { paneTitle?: string }) {
|
||||
function PaneTabBody(props: { tab: PaneTab; row: PaneRow; index: number; paneTitle?: string }) {
|
||||
const content = props.tab.content
|
||||
if (content.kind === 'iframe') {
|
||||
return <iframe className="dsh-wt_paneFrame" src={content.url} title={props.tab.title} />
|
||||
return <IframePane url={content.url} title={content.title ?? props.tab.title} />
|
||||
}
|
||||
if (content.kind === 'file') {
|
||||
return <FileViewer path={content.path} />
|
||||
|
||||
@@ -138,6 +138,10 @@ export const css = xtermCss + '\n' + [
|
||||
'.dsh-wt_paneBar{flex:none;display:flex;align-items:center;height:22px;padding:0 8px;border-bottom:1px solid var(--dsw-alias-border-l1,#262b36);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.02))}',
|
||||
'.dsh-wt_paneTitle{flex:1;min-width:0;font-size:11px;font-weight:600;color:var(--dsw-alias-label-secondary,#9aa4b2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
||||
'.dsh-wt_paneFrame{flex:1;width:100%;border:0;background:#010409;min-height:0}',
|
||||
// iframe 内容标签:容器 + 右上角常驻刷新按钮
|
||||
'.dsh-wt_frameWrap{position:relative;flex:1;min-height:0;display:flex;flex-direction:column}',
|
||||
'.dsh-wt_frameRefresh{position:absolute;top:6px;right:6px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--dsw-alias-border-l2,#3a4150);border-radius:6px;background:var(--dsw-alias-fill-l2,#171b22);color:var(--dsw-alias-label-primary,#e6e8eb);font-size:13px;line-height:1;cursor:pointer;opacity:.72;z-index:5}',
|
||||
'.dsh-wt_frameRefresh:hover{opacity:1;border-color:var(--dsw-alias-state-accent-primary,#4f8ef7)}',
|
||||
'.dsh-wt_splitDivider{background:var(--dsw-alias-border-l2,#3a4150);cursor:col-resize;touch-action:none;box-sizing:border-box}',
|
||||
'.dsh-wt_splitDividerH{cursor:row-resize}',
|
||||
'.dsh-wt_splitDivider:hover,.dsh-wt_splitDividerH:hover{background:var(--dsw-alias-state-accent-primary,#4f8ef7)}',
|
||||
|
||||
Reference in New Issue
Block a user