feat: 常驻项目彻底去特殊化——变更视图通用(views覆盖)+真删除(恢复默认不复活/可重新添加)+去↑↓箭头
This commit is contained in:
+100
-67
@@ -43,8 +43,10 @@ type ProjectsState = {
|
|||||||
nameOverrides: Record<string, string>
|
nameOverrides: Record<string, string>
|
||||||
/** 图标覆盖(点图标换 emoji;含入驻插件项目 id → emoji)。 */
|
/** 图标覆盖(点图标换 emoji;含入驻插件项目 id → emoji)。 */
|
||||||
iconOverrides: Record<string, string>
|
iconOverrides: Record<string, string>
|
||||||
/** 已从工作台删除的入驻项目 id(插件不卸载,仅不再展示;恢复默认找回)。 */
|
/** 已从工作台删除的入驻项目 id(真删除:恢复默认不复活;在「已删除的项目」里可重新添加)。 */
|
||||||
removed: string[]
|
removed: string[]
|
||||||
|
/** 入驻项目的视图覆盖(id → LayoutSpec):变更视图后按此打开,替换项目自声明的布局。 */
|
||||||
|
views: Record<string, LayoutSpec>
|
||||||
/** 本地快捷方式条目。 */
|
/** 本地快捷方式条目。 */
|
||||||
shortcuts: Shortcut[]
|
shortcuts: Shortcut[]
|
||||||
/** 用户自建的布局条目(「+」新建工作区保存的 LayoutSpec)。 */
|
/** 用户自建的布局条目(「+」新建工作区保存的 LayoutSpec)。 */
|
||||||
@@ -59,9 +61,7 @@ const SNAP_PX = 32
|
|||||||
const MARKET_URL = 'https://github.com/hikariming/dshfind'
|
const MARKET_URL = 'https://github.com/hikariming/dshfind'
|
||||||
/** 已报到卡片的 CSS order 偏移:未上报(order=0)的 v1 卡片永远排在已上报卡片之前。 */
|
/** 已报到卡片的 CSS order 偏移:未上报(order=0)的 v1 卡片永远排在已上报卡片之前。 */
|
||||||
const ORDER_OFFSET = 1000
|
const ORDER_OFFSET = 1000
|
||||||
/** 自带分栏实现的遗留项目(如 travelatlas,未接入 openSplit 引擎):埋点用冷却去重,避免每次点击置顶 */
|
/** 非引擎项目埋点冷却(毫秒):同 id 两次计使用的最小间隔 */
|
||||||
const LEGACY_SPLIT_IDS = new Set(['travelatlas'])
|
|
||||||
/** 遗留项目埋点冷却(毫秒):同 id 两次计使用的最小间隔 */
|
|
||||||
const LEGACY_BUMP_COOLDOWN = 15000
|
const LEGACY_BUMP_COOLDOWN = 15000
|
||||||
/** 落点判定余量:松手时指针越出有效落点区(超出底部/顶部/侧边)即视为「无有效落点」。 */
|
/** 落点判定余量:松手时指针越出有效落点区(超出底部/顶部/侧边)即视为「无有效落点」。 */
|
||||||
const OVER_BOTTOM_PX = 24
|
const OVER_BOTTOM_PX = 24
|
||||||
@@ -190,6 +190,7 @@ const DEFAULT_PROJECTS: ProjectsState = {
|
|||||||
nameOverrides: {},
|
nameOverrides: {},
|
||||||
iconOverrides: {},
|
iconOverrides: {},
|
||||||
removed: [],
|
removed: [],
|
||||||
|
views: {},
|
||||||
shortcuts: [],
|
shortcuts: [],
|
||||||
layouts: [],
|
layouts: [],
|
||||||
}
|
}
|
||||||
@@ -229,6 +230,7 @@ function loadProjects(): ProjectsState {
|
|||||||
nameOverrides: p.nameOverrides && typeof p.nameOverrides === 'object' ? p.nameOverrides : {},
|
nameOverrides: p.nameOverrides && typeof p.nameOverrides === 'object' ? p.nameOverrides : {},
|
||||||
iconOverrides: p.iconOverrides && typeof p.iconOverrides === 'object' ? p.iconOverrides : {},
|
iconOverrides: p.iconOverrides && typeof p.iconOverrides === 'object' ? p.iconOverrides : {},
|
||||||
removed: Array.isArray(p.removed) ? p.removed.filter((x: unknown): x is string => typeof x === 'string') : [],
|
removed: Array.isArray(p.removed) ? p.removed.filter((x: unknown): x is string => typeof x === 'string') : [],
|
||||||
|
views: p.views && typeof p.views === 'object' ? p.views : {},
|
||||||
shortcuts: Array.isArray(p.shortcuts)
|
shortcuts: Array.isArray(p.shortcuts)
|
||||||
? p.shortcuts.filter((s: any) => s && typeof s.id === 'string' && typeof s.name === 'string' && typeof s.href === 'string')
|
? p.shortcuts.filter((s: any) => s && typeof s.id === 'string' && typeof s.name === 'string' && typeof s.href === 'string')
|
||||||
: [],
|
: [],
|
||||||
@@ -484,22 +486,21 @@ function WorktableSection(props: any) {
|
|||||||
} else if (knownEngine) {
|
} else if (knownEngine) {
|
||||||
// 引擎项目:本次是关闭/重复点击 → 不计
|
// 引擎项目:本次是关闭/重复点击 → 不计
|
||||||
return
|
return
|
||||||
} else if (LEGACY_SPLIT_IDS.has(id)) {
|
} else {
|
||||||
// 遗留自带分栏的项目:冷却去重(打开计一次;快速开关/关闭点击不重复计)
|
// 非引擎项目(自带分栏实现等):冷却去重(打开计一次;快速开关/关闭点击不重复计)
|
||||||
if (now - (lastLegacyBumpRef.current[id] ?? 0) > LEGACY_BUMP_COOLDOWN) {
|
if (now - (lastLegacyBumpRef.current[id] ?? 0) > LEGACY_BUMP_COOLDOWN) {
|
||||||
lastLegacyBumpRef.current[id] = now
|
lastLegacyBumpRef.current[id] = now
|
||||||
bump()
|
bump()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
bump() // 无判定依据的普通项目:保持原行为
|
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
/** 分栏工作区入口(M1 引擎):项目卡片调用 openSplit(spec) 打开声明式布局 */
|
/** 分栏工作区入口(M1 引擎):项目卡片调用 openSplit(spec) 打开声明式布局;
|
||||||
|
* 若该 id 存在视图覆盖(用户在设置里变更过视图),用覆盖布局替换打开。 */
|
||||||
const openSplit = useCallback((spec: LayoutSpec) => {
|
const openSplit = useCallback((spec: LayoutSpec) => {
|
||||||
engineIdsRef.current.add(spec.id)
|
engineIdsRef.current.add(spec.id)
|
||||||
splitStore.open(spec)
|
splitStore.open(projects.views[spec.id] ?? spec)
|
||||||
}, [])
|
}, [projects.views])
|
||||||
|
|
||||||
// ── 有效排序 ──
|
// ── 有效排序 ──
|
||||||
// 手动:持久化 order(过滤已卸载 id)→ 新注册 id 与布局 id 追加尾部;
|
// 手动:持久化 order(过滤已卸载 id)→ 新注册 id 与布局 id 追加尾部;
|
||||||
@@ -598,17 +599,7 @@ function WorktableSection(props: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 编辑模式动作 ──
|
// ── 编辑模式动作(排序只用左缘 ≡ 抓手拖拽,无 ↑↓ 按钮) ──
|
||||||
const moveBy = (id: string, delta: number) => {
|
|
||||||
const list = [...effectiveOrder]
|
|
||||||
const i = list.indexOf(id)
|
|
||||||
const j = clamp(i + delta, 0, list.length - 1)
|
|
||||||
if (i < 0 || i === j) return
|
|
||||||
list.splice(i, 1)
|
|
||||||
list.splice(j, 0, id)
|
|
||||||
persistProjects({ order: list })
|
|
||||||
}
|
|
||||||
|
|
||||||
const moveTo = (id: string, targetId: string) => {
|
const moveTo = (id: string, targetId: string) => {
|
||||||
if (id === targetId) return
|
if (id === targetId) return
|
||||||
const list = [...effectiveOrder]
|
const list = [...effectiveOrder]
|
||||||
@@ -638,6 +629,7 @@ function WorktableSection(props: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 恢复默认:还原排序/隐藏/改名/图标/视图覆盖;已删除的项目不复活(真删除,可在「已删除的项目」重新添加)
|
||||||
const resetProjects = () => {
|
const resetProjects = () => {
|
||||||
persistProjects((prev) => ({
|
persistProjects((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
@@ -645,7 +637,15 @@ function WorktableSection(props: any) {
|
|||||||
hidden: [],
|
hidden: [],
|
||||||
nameOverrides: {},
|
nameOverrides: {},
|
||||||
iconOverrides: {},
|
iconOverrides: {},
|
||||||
removed: [],
|
views: {},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重新添加已删除的入驻项目(删除 ↔ 重新添加 完全可逆) */
|
||||||
|
const readdProject = (id: string) => {
|
||||||
|
persistProjects((prev) => ({
|
||||||
|
...prev,
|
||||||
|
removed: prev.removed.filter((x) => x !== id),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,16 +670,20 @@ function WorktableSection(props: any) {
|
|||||||
persistProjects((prev) => ({ ...prev, layouts: prev.layouts.filter((l) => l.id !== id) }))
|
persistProjects((prev) => ({ ...prev, layouts: prev.layouts.filter((l) => l.id !== id) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 变更视图:按预设重建布局拓扑,现有窗内容(标签)按序迁入,不丢失 ──
|
// ── 变更视图:所有项目通用。布局项目 = 重建其布局条目;入驻项目 = 建立/更新视图覆盖。
|
||||||
|
// 现有窗内容(标签)按序迁入新拓扑,不丢失。 ──
|
||||||
const applyLayoutChange = (id: string, presetId: string) => {
|
const applyLayoutChange = (id: string, presetId: string) => {
|
||||||
const layout = projects.layouts.find((l) => l.id === id)
|
const layout = projects.layouts.find((l) => l.id === id)
|
||||||
if (!layout) return
|
const meta = metas[id]
|
||||||
const sources = [...(layout.top ?? []), ...layout.main]
|
const current = layout ?? projects.views[id]
|
||||||
.map((pp) => pp.tabs ?? [])
|
const sources = current
|
||||||
.filter((tabs) => tabs.length > 0)
|
? [...(current.top ?? []), ...current.main]
|
||||||
const next = buildLayout(presetId, layout.title)
|
.map((pp) => pp.tabs ?? [])
|
||||||
next.id = layout.id
|
.filter((tabs) => tabs.length > 0)
|
||||||
next.icon = layout.icon
|
: []
|
||||||
|
const next = buildLayout(presetId, layout ? layout.title : (meta?.name ?? id))
|
||||||
|
next.id = id
|
||||||
|
next.icon = layout ? layout.icon : (projects.iconOverrides[id] ?? meta?.icon)
|
||||||
const targets = [...(next.left ? [next.left] : []), ...(next.top ?? []), ...next.main]
|
const targets = [...(next.left ? [next.left] : []), ...(next.top ?? []), ...next.main]
|
||||||
let si = 0
|
let si = 0
|
||||||
for (const pane of targets) {
|
for (const pane of targets) {
|
||||||
@@ -696,11 +700,10 @@ function WorktableSection(props: any) {
|
|||||||
last.tabs = [...(last.tabs ?? []), ...overflow]
|
last.tabs = [...(last.tabs ?? []), ...overflow]
|
||||||
last.active = 0
|
last.active = 0
|
||||||
}
|
}
|
||||||
persistProjects((prev) => ({
|
persistProjects((prev) => layout
|
||||||
...prev,
|
? { ...prev, layouts: prev.layouts.map((l) => (l.id === id ? next : l)) }
|
||||||
layouts: prev.layouts.map((l) => (l.id === id ? next : l)),
|
: { ...prev, views: { ...prev.views, [id]: next } })
|
||||||
}))
|
// 该视图当前打开时:关旧开新,工作区即时变为新视图
|
||||||
// 该布局当前打开时:关旧开新,工作区即时变为新视图
|
|
||||||
const wasOpen = splitStore.active && splitStore.spec?.id === id
|
const wasOpen = splitStore.active && splitStore.spec?.id === id
|
||||||
if (wasOpen) {
|
if (wasOpen) {
|
||||||
splitStore.close()
|
splitStore.close()
|
||||||
@@ -765,44 +768,66 @@ function WorktableSection(props: any) {
|
|||||||
persistProjects((prev) => ({ ...prev, iconOverrides: { ...prev.iconOverrides, [id]: icon } }))
|
persistProjects((prev) => ({ ...prev, iconOverrides: { ...prev.iconOverrides, [id]: icon } }))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 入驻项目卡片图标覆盖(DOM 层,不改动对方插件代码)
|
// ── 入驻项目卡片通用 DOM 桥(不写死任何项目名/类名) ──
|
||||||
// 1) 属性同步:iconOverrides[id] -> 卡片 icon 元素 data-wt-icon(CSS 用 attr() 换显示);
|
// 卡片按子座位注册顺序渲染,与 aliveRegisteredIds 一一对应:
|
||||||
// 2) 委托点击:捕获阶段拦截卡片 icon 点击 -> 打开图标选择器(阻止卡片自身打开项目)。
|
// 1) 位置映射:给每张卡片标 data-wt-id,图标覆盖写到卡片第一个子元素(通用结构约定);
|
||||||
|
// 2) 委托点击(捕获阶段):卡片图标 → 图标选择器;有视图覆盖的项目 → 用引擎打开该视图。
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ROOT_TO_ID = [['.ta_card', 'travelatlas'], ['.pr_card', 'planreview']]
|
|
||||||
const sync = () => {
|
const sync = () => {
|
||||||
for (const [rootSel, pid] of ROOT_TO_ID) {
|
const box = document.querySelector<HTMLElement>('.dsh-wt_projects')
|
||||||
const cards = document.querySelectorAll('.dsh-wt_projects ' + rootSel)
|
if (!box) return
|
||||||
cards.forEach((card) => {
|
// 子座位把所有卡片包在一个无类容器里:直接取卡片按钮(DOM 序 = 注册序),排除自渲染的布局卡
|
||||||
const icon = card.querySelector('.ta_cardIcon, .pr_cardIcon')
|
const cards = Array.from(box.querySelectorAll<HTMLElement>('button:not(.dsh-wt_layout)'))
|
||||||
if (!icon) return
|
cards.forEach((el, i) => {
|
||||||
const ovr = projects.iconOverrides[pid]
|
const id = aliveRegisteredIds[i]
|
||||||
if (ovr) icon.setAttribute('data-wt-icon', ovr)
|
if (id) {
|
||||||
else icon.removeAttribute('data-wt-icon')
|
el.setAttribute('data-wt-id', id)
|
||||||
})
|
const icon = el.children[0] as HTMLElement | null
|
||||||
}
|
if (icon) {
|
||||||
|
const ovr = projects.iconOverrides[id]
|
||||||
|
if (ovr) icon.setAttribute('data-wt-icon', ovr)
|
||||||
|
else icon.removeAttribute('data-wt-icon')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
el.removeAttribute('data-wt-id')
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
sync()
|
sync()
|
||||||
const mo = new MutationObserver(sync)
|
const mo = new MutationObserver(sync)
|
||||||
mo.observe(document.body, { childList: true, subtree: true })
|
mo.observe(document.body, { childList: true, subtree: true })
|
||||||
return () => mo.disconnect()
|
return () => mo.disconnect()
|
||||||
}, [projects.iconOverrides])
|
}, [aliveRegisteredIds, projects.iconOverrides])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onDocClick = (e) => {
|
const onDocClick = (e: MouseEvent) => {
|
||||||
const target = e.target
|
const target = e.target as HTMLElement | null
|
||||||
const icon = target && target.closest ? target.closest('.ta_cardIcon, .pr_cardIcon') : null
|
const card = target && target.closest
|
||||||
if (!icon || !icon.closest('.dsh-wt_projects')) return
|
? (target.closest('.dsh-wt_projects [data-wt-id]') as HTMLElement | null)
|
||||||
const card = icon.closest('.ta_card, .pr_card')
|
: null
|
||||||
const pid = card && card.classList.contains('ta_card') ? 'travelatlas' : card && card.classList.contains('pr_card') ? 'planreview' : null
|
if (!card) return
|
||||||
|
const pid = card.getAttribute('data-wt-id')
|
||||||
if (!pid) return
|
if (!pid) return
|
||||||
e.stopPropagation()
|
// 图标点击(卡片第一个子元素内) → 打开图标选择器(阻止卡片自身打开项目)
|
||||||
e.preventDefault()
|
const first = card.children[0] as HTMLElement | null
|
||||||
openIconPick('project', pid, icon)
|
if (first && target && first.contains(target)) {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
openIconPick('project', pid, first)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 有视图覆盖 → 用引擎打开该视图(替换卡片自带行为)
|
||||||
|
const view = projects.views[pid]
|
||||||
|
if (view) {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
openSplit(view)
|
||||||
|
reportUsed(pid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
document.addEventListener('click', onDocClick, true)
|
document.addEventListener('click', onDocClick, true)
|
||||||
return () => document.removeEventListener('click', onDocClick, true)
|
return () => document.removeEventListener('click', onDocClick, true)
|
||||||
}, [])
|
}, [projects.views, openSplit, reportUsed])
|
||||||
|
|
||||||
const query = view.query.trim()
|
const query = view.query.trim()
|
||||||
const queryLower = query.toLowerCase()
|
const queryLower = query.toLowerCase()
|
||||||
@@ -1087,14 +1112,10 @@ function WorktableSection(props: any) {
|
|||||||
placeholder={t('manage.renamePh')}
|
placeholder={t('manage.renamePh')}
|
||||||
onChange={(e) => renameProject(id, e.target.value)}
|
onChange={(e) => renameProject(id, e.target.value)}
|
||||||
/>
|
/>
|
||||||
<button type="button" className="dsh-wt_manageBtn" title={t('manage.up')} onClick={() => moveBy(id, -1)}>↑</button>
|
|
||||||
<button type="button" className="dsh-wt_manageBtn" title={t('manage.down')} onClick={() => moveBy(id, 1)}>↓</button>
|
|
||||||
<button type="button" className="dsh-wt_manageBtn" title={isHidden ? t('manage.show') : t('manage.hide')} onClick={() => toggleHidden(id)}>
|
<button type="button" className="dsh-wt_manageBtn" title={isHidden ? t('manage.show') : t('manage.hide')} onClick={() => toggleHidden(id)}>
|
||||||
{isHidden ? '🙈' : '👁'}
|
{isHidden ? '🙈' : '👁'}
|
||||||
</button>
|
</button>
|
||||||
{layout && (
|
<button type="button" className="dsh-wt_manageBtn" title={t('manage.changeView')} onClick={() => setViewPickFor(id)}>🧩</button>
|
||||||
<button type="button" className="dsh-wt_manageBtn" title={t('manage.changeView')} onClick={() => setViewPickFor(id)}>🧩</button>
|
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="dsh-wt_manageBtn"
|
className="dsh-wt_manageBtn"
|
||||||
@@ -1119,6 +1140,18 @@ function WorktableSection(props: any) {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<button type="button" className="dsh-wt_manageReset" onClick={resetProjects}>{t('manage.reset')}</button>
|
<button type="button" className="dsh-wt_manageReset" onClick={resetProjects}>{t('manage.reset')}</button>
|
||||||
|
{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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1128,7 +1161,7 @@ function WorktableSection(props: any) {
|
|||||||
<span className="dsh-wt_menuLabel">{t('viewPick.title')}</span>
|
<span className="dsh-wt_menuLabel">{t('viewPick.title')}</span>
|
||||||
<div className="dsh-wt_presets">
|
<div className="dsh-wt_presets">
|
||||||
{PRESET_DEFS.map((def) => {
|
{PRESET_DEFS.map((def) => {
|
||||||
const cur = projects.layouts.find((l) => l.id === viewPickFor)
|
const cur = projects.layouts.find((l) => l.id === viewPickFor) ?? projects.views[viewPickFor]
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={def.id}
|
key={def.id}
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ export const zh = {
|
|||||||
'manage.renamePh': '显示名',
|
'manage.renamePh': '显示名',
|
||||||
'manage.hide': '隐藏',
|
'manage.hide': '隐藏',
|
||||||
'manage.show': '显示',
|
'manage.show': '显示',
|
||||||
'manage.up': '上移',
|
'manage.removed': '已删除的项目',
|
||||||
'manage.down': '下移',
|
'manage.readd': '重新添加',
|
||||||
'manage.deleteShortcut': '删除快捷方式',
|
'manage.deleteShortcut': '删除快捷方式',
|
||||||
'manage.reset': '恢复默认',
|
'manage.reset': '恢复默认',
|
||||||
'shortcut.badge': '本地',
|
'shortcut.badge': '本地',
|
||||||
@@ -52,7 +52,7 @@ export const zh = {
|
|||||||
'confirm.delete': '删除',
|
'confirm.delete': '删除',
|
||||||
'confirm.layoutBody': '确定删除「{name}」?该工作区及其布局将被移除,此操作不可恢复。',
|
'confirm.layoutBody': '确定删除「{name}」?该工作区及其布局将被移除,此操作不可恢复。',
|
||||||
'confirm.shortcutBody': '确定删除快捷方式「{name}」?此操作不可恢复。',
|
'confirm.shortcutBody': '确定删除快捷方式「{name}」?此操作不可恢复。',
|
||||||
'confirm.projectBody': '确定从工作台删除「{name}」?插件本身不会被卸载,之后可在「管理项目」→「恢复默认」找回。',
|
'confirm.projectBody': '确定从工作台删除「{name}」?插件本身不会被卸载,「恢复默认」也不会复活它;之后可在设置里「已删除的项目」中重新添加。',
|
||||||
'pane.browser': '浏览器',
|
'pane.browser': '浏览器',
|
||||||
'pane.explorer': '资源管理器',
|
'pane.explorer': '资源管理器',
|
||||||
'pane.scm': '源代码管理',
|
'pane.scm': '源代码管理',
|
||||||
@@ -114,8 +114,8 @@ export const en = {
|
|||||||
'manage.renamePh': 'Display name',
|
'manage.renamePh': 'Display name',
|
||||||
'manage.hide': 'Hide',
|
'manage.hide': 'Hide',
|
||||||
'manage.show': 'Show',
|
'manage.show': 'Show',
|
||||||
'manage.up': 'Move up',
|
'manage.removed': 'Removed projects',
|
||||||
'manage.down': 'Move down',
|
'manage.readd': 'Re-add',
|
||||||
'manage.deleteShortcut': 'Delete shortcut',
|
'manage.deleteShortcut': 'Delete shortcut',
|
||||||
'manage.reset': 'Reset defaults',
|
'manage.reset': 'Reset defaults',
|
||||||
'shortcut.badge': 'Local',
|
'shortcut.badge': 'Local',
|
||||||
@@ -132,7 +132,7 @@ export const en = {
|
|||||||
'confirm.delete': 'Delete',
|
'confirm.delete': 'Delete',
|
||||||
'confirm.layoutBody': 'Delete "{name}"? Its workspace and layout will be removed. This cannot be undone.',
|
'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.shortcutBody': 'Delete shortcut "{name}"? This cannot be undone.',
|
||||||
'confirm.projectBody': 'Remove "{name}" from the worktable? The plugin stays installed; restore it later via Manage → Reset defaults.',
|
'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.',
|
||||||
'pane.browser': 'Browser',
|
'pane.browser': 'Browser',
|
||||||
'pane.explorer': 'Explorer',
|
'pane.explorer': 'Explorer',
|
||||||
'pane.scm': 'Source control',
|
'pane.scm': 'Source control',
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ export const splitStore: SplitState = {
|
|||||||
}
|
}
|
||||||
this.close()
|
this.close()
|
||||||
}
|
}
|
||||||
// 兼容桥:travelatlas 尚未接入共享引擎(不改动其代码),打开本引擎布局前运行时点击其关闭按钮
|
// 跨插件互操作桥:自带分栏实现的入驻插件(未接入共享引擎)打开时,运行时点击其关闭按钮让位
|
||||||
try {
|
try {
|
||||||
const taClose = document.querySelector<HTMLElement>('.ta_splitClose')
|
const taClose = document.querySelector<HTMLElement>('.ta_splitClose')
|
||||||
taClose?.click()
|
taClose?.click()
|
||||||
@@ -751,8 +751,8 @@ export const splitStore: SplitState = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 兼容桥:travelatlas 分栏浮层(.ta_split)出现 = 其引擎打开 → 本引擎让位。
|
/** 跨插件互操作桥:自带分栏实现的插件其浮层(.ta_split)出现 = 其引擎打开 → 本引擎让位。
|
||||||
* 不改动 travelatlas 代码,仅在 DOM 层观察其浮层挂载。 */
|
* 不改动对方插件代码,仅在 DOM 层观察其浮层挂载。 */
|
||||||
if (typeof document !== 'undefined' && document.body) {
|
if (typeof document !== 'undefined' && document.body) {
|
||||||
const taObserver = new MutationObserver(() => {
|
const taObserver = new MutationObserver(() => {
|
||||||
if (!splitStore.active) return
|
if (!splitStore.active) return
|
||||||
|
|||||||
@@ -130,12 +130,12 @@ export const css = [
|
|||||||
'.dsh-wt_layoutText{flex:1;min-width:0;display:flex;flex-direction:column;gap:0}',
|
'.dsh-wt_layoutText{flex:1;min-width:0;display:flex;flex-direction:column;gap:0}',
|
||||||
'.dsh-wt_layoutName{font-size:12px;font-weight:400;line-height:17px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
'.dsh-wt_layoutName{font-size:12px;font-weight:400;line-height:17px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
||||||
'.dsh-wt_layoutArrow{flex:none;color:var(--dsw-alias-label-tertiary,#6e7683);font-size:14px;line-height:20px}',
|
'.dsh-wt_layoutArrow{flex:none;color:var(--dsw-alias-label-tertiary,#6e7683);font-size:14px;line-height:20px}',
|
||||||
// 项目卡片统一:入驻插件卡片(travelatlas/planreview)与布局卡片同款常显框(静止态可见边框+底)
|
// 项目卡片统一:所有入驻项目卡片(data-wt-id 通用标记)与布局卡片同款常显框
|
||||||
'.dsh-wt_projects .ta_card,.dsh-wt_projects .pr_card{border-color:var(--dsw-alias-border-l1,#262b36);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.02))}',
|
'.dsh-wt_projects [data-wt-id]{border-color:var(--dsw-alias-border-l1,#262b36);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.02))}',
|
||||||
'.dsh-wt_projects .ta_card:hover,.dsh-wt_projects .pr_card:hover{border-color:var(--dsw-alias-state-accent-primary,#4f8ef7);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.05))}',
|
'.dsh-wt_projects [data-wt-id]:hover{border-color:var(--dsw-alias-state-accent-primary,#4f8ef7);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.05))}',
|
||||||
'.dsh-wt_projects .ta_card[data-on=true],.dsh-wt_projects .pr_card[data-on=true]{border-color:var(--dsw-alias-state-accent-primary,#4f8ef7);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.05))}',
|
'.dsh-wt_projects [data-wt-id][data-on=true]{border-color:var(--dsw-alias-state-accent-primary,#4f8ef7);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.05))}',
|
||||||
// 标签精简:项目卡片只留名称一行,小字描述行去掉
|
// 标签精简:项目卡片只留名称一行,小字描述行去掉
|
||||||
'.dsh-wt_projects .ta_cardDesc,.dsh-wt_projects .pr_cardDesc{display:none}',
|
'.dsh-wt_projects [data-wt-id] > :nth-child(2) > :nth-child(2){display:none}',
|
||||||
// 图标选择弹窗(固定定位,点击项目/布局/快捷方式的图标唤出)
|
// 图标选择弹窗(固定定位,点击项目/布局/快捷方式的图标唤出)
|
||||||
'.dsh-wt_iconPop{position:fixed;z-index:81;width:256px;padding:8px;border:1px solid var(--dsw-alias-border-l2,#3a4150);border-radius:10px;background:var(--dsw-alias-bg-base,#0b0e14);box-shadow:var(--dsw-shadow-lv2,0 8px 24px rgba(0,0,0,.4))}',
|
'.dsh-wt_iconPop{position:fixed;z-index:81;width:256px;padding:8px;border:1px solid var(--dsw-alias-border-l2,#3a4150);border-radius:10px;background:var(--dsw-alias-bg-base,#0b0e14);box-shadow:var(--dsw-shadow-lv2,0 8px 24px rgba(0,0,0,.4))}',
|
||||||
'.dsh-wt_iconPopTitle{font-size:10px;letter-spacing:.05em;color:var(--dsw-alias-label-tertiary,#6e7683);padding:1px 4px 6px}',
|
'.dsh-wt_iconPopTitle{font-size:10px;letter-spacing:.05em;color:var(--dsw-alias-label-tertiary,#6e7683);padding:1px 4px 6px}',
|
||||||
@@ -147,13 +147,13 @@ export const css = [
|
|||||||
'.dsh-wt_iconPick{flex:none;display:inline-flex;align-items:center;justify-content:center;padding:1px;border:none;background:transparent;cursor:pointer;border-radius:4px}',
|
'.dsh-wt_iconPick{flex:none;display:inline-flex;align-items:center;justify-content:center;padding:1px;border:none;background:transparent;cursor:pointer;border-radius:4px}',
|
||||||
'.dsh-wt_iconPick:hover{background:var(--dsw-alias-fill-l1,rgba(255,255,255,.08))}',
|
'.dsh-wt_iconPick:hover{background:var(--dsw-alias-fill-l1,rgba(255,255,255,.08))}',
|
||||||
// 四类项目卡片统一竖向高度(常驻×2 / 布局 / 快捷方式;新建条目自动继承)
|
// 四类项目卡片统一竖向高度(常驻×2 / 布局 / 快捷方式;新建条目自动继承)
|
||||||
'.dsh-wt_projects .ta_card,.dsh-wt_projects .pr_card,.dsh-wt_layout,.dsh-wt_shortcut{height:34px;box-sizing:border-box}',
|
'.dsh-wt_projects [data-wt-id],.dsh-wt_layout,.dsh-wt_shortcut{height:34px;box-sizing:border-box}',
|
||||||
// 入驻项目卡片图标覆盖:data-wt-icon 存在时用 attr() 替换显示(原字符字号压到 0)
|
// 入驻项目卡片图标覆盖:data-wt-icon 存在时用 attr() 替换显示(原字符字号压到 0)
|
||||||
'.dsh-wt_projects .ta_cardIcon[data-wt-icon],.dsh-wt_projects .pr_cardIcon[data-wt-icon]{font-size:0;line-height:20px}',
|
'.dsh-wt_projects [data-wt-id] > :first-child[data-wt-icon]{font-size:0;line-height:20px}',
|
||||||
'.dsh-wt_projects .ta_cardIcon[data-wt-icon]::before,.dsh-wt_projects .pr_cardIcon[data-wt-icon]::before{content:attr(data-wt-icon);font-size:15px;line-height:20px}',
|
'.dsh-wt_projects [data-wt-id] > :first-child[data-wt-icon]::before{content:attr(data-wt-icon);font-size:15px;line-height:20px}',
|
||||||
// 常驻项目卡片与新建项目统一:emoji 字号 15px、名称不加粗
|
// 常驻项目卡片与新建项目统一:emoji 字号 15px、名称不加粗
|
||||||
'.dsh-wt_projects .ta_cardIcon,.dsh-wt_projects .pr_cardIcon{font-size:15px;line-height:20px}',
|
'.dsh-wt_projects [data-wt-id] > :first-child{font-size:15px;line-height:20px}',
|
||||||
'.dsh-wt_projects .ta_cardName,.dsh-wt_projects .pr_cardName{font-weight:400}',
|
'.dsh-wt_projects [data-wt-id] > :nth-child(2) > :first-child{font-weight:400}',
|
||||||
// 头部按钮内的官方 SVG 图标
|
// 头部按钮内的官方 SVG 图标
|
||||||
'.dsh-wt_iconBtn svg{display:block}',
|
'.dsh-wt_iconBtn svg{display:block}',
|
||||||
// 窗内容:选择器 / 自定义 / 浏览器 / 占位
|
// 窗内容:选择器 / 自定义 / 浏览器 / 占位
|
||||||
|
|||||||
@@ -639,19 +639,27 @@
|
|||||||
原生视图内部是浏览器扩展页,插件注入不了按钮、拦不到鼠标,也驱动不了内部滚动——
|
原生视图内部是浏览器扩展页,插件注入不了按钮、拦不到鼠标,也驱动不了内部滚动——
|
||||||
向用户如实说明后按指示回退);② 视图选项改名「设置」,点开直接内嵌「排序方式 + 管理项目
|
向用户如实说明后按指示回退);② 视图选项改名「设置」,点开直接内嵌「排序方式 + 管理项目
|
||||||
展开列表」,不再二次点击;③ 管理项目里给现有项目加「变更视图」:换预设拓扑,
|
展开列表」,不再二次点击;③ 管理项目里给现有项目加「变更视图」:换预设拓扑,
|
||||||
自定义内容(标签)自动迁入新视图。
|
## 补记(常驻项目彻底去特殊化:变更视图通用 / 真删除 / 去箭头)
|
||||||
|
|
||||||
|
- 用户反馈(四条):① 两个常驻项目也要变更视图按钮;② 怀疑底层代码里写死了这两个项目的
|
||||||
|
特殊内容——应该完全不特殊;③ 删除常驻项目后「恢复默认」不应复活它们(真删除),
|
||||||
|
但整个流程可逆(可重新添加回来);④ 管理行去掉 ↑↓ 箭头(左缘 ≡ 抓手拖拽已够用)。
|
||||||
- 处理:
|
- 处理:
|
||||||
① PDF 回退:删 PdfViewer/pdfjs 依赖/worker banner/CSS/locale pdf.*/fixture;
|
① 变更视图通用化:projects.v1 新增 views(id → LayoutSpec 视图覆盖);管理行 🧩 按钮
|
||||||
FileViewer pdf → 原生 iframe(服务端 application/pdf MIME 保留,供原生内嵌渲染);
|
所有项目显示;applyLayoutChange 对布局项目重建条目、对入驻项目写 views[id];
|
||||||
bundle 从 2.6MB 回到 747KB;build.mjs 保留 esbuild JS API;
|
openSplit 打开时用 views[spec.id] 替换自声明布局;卡片点击拦截(有覆盖时用引擎打开),
|
||||||
② 设置面板:managing 状态并入 viewOptionsOpen;设置弹窗 = 排序项 + 分隔 + 管理标题/完成 +
|
常驻项目一样可变更视图;
|
||||||
管理行列表 + 恢复默认,宽 316;「管理项目…」菜单项与独立管理弹窗删除;卡区弱化跟随设置开关;
|
② 去硬编码:删除 LEGACY_SPLIT_IDS/ta_card/pr_card 类名映射;改为通用 DOM 桥——
|
||||||
③ 变更视图:布局行加 🧩 按钮 → 弹 6 预设缩略图选择器(当前拓扑高亮);applyLayoutChange
|
子座位卡片按注册序 ↔ aliveRegisteredIds 位置映射(button:not(.dsh-wt_layout)),
|
||||||
按预设重建拓扑(保留 id/名称/图标),现有窗标签按序迁入新窗格、多余并入末窗不丢失;
|
标记 data-wt-id;图标覆盖写到卡片第一个子元素;CSS 全部换成 [data-wt-id] 通用选择器
|
||||||
当前打开的布局即时关旧开新生效。locale:menu.viewOptions→设置/Settings,
|
(框/高度/字号/字重/描述行隐藏);reportUsed 冷却对全部非引擎项目通用;
|
||||||
新增 manage.changeView/viewPick.title。
|
split.tsx 仅保留跨插件互操作桥(.ta_split 浮层让位,注释中性化);
|
||||||
- 验证(functional-diag STEP8/9 重写 + STEP10 适配):STEP8 设置弹窗 fixed/left=288/
|
③ 删除语义:resetProjects 不再清空 removed(恢复默认不复活);设置面板新增「已删除的项目」
|
||||||
sortItems=2/rows=4 直接展开;STEP9 布局行 5 按钮 → 🧩 → 6 预设 → 选 l2 →
|
区块,逐条「↺ 重新添加」(删除 ↔ 重新添加完全可逆);confirm.projectBody 文案同步;
|
||||||
newTop=1/newMain=1/chatFull=true + migrated=1(标签迁入顶行);STEP10 删除确认流程在新面板
|
④ 管理行删除 ↑/↓ 按钮与 moveBy 函数,排序只靠 ≡ 拖拽;locale 删 manage.up/down,
|
||||||
内全部保持;STEP11 MD/TXT 不变;ERRORS_COUNT: 0。
|
新增 manage.removed/readd。
|
||||||
|
- 验证(functional-diag STEP9/10 重写):STEP9 常驻行 3 按钮(无箭头+🧩)→ 变更视图
|
||||||
|
views['planreview'] 三栏拓扑 ✓;布局行迁移不变 ✓;STEP10 删除后 reset removedAfterReset
|
||||||
|
仍含 id(不复活)→ removedSection=true → ↺ 重新添加 removed=[] 且卡片回归 ✓;
|
||||||
|
通用桥回归:框/高度/字重/图标覆盖/desc 隐藏全 ✓;ERRORS_COUNT: 0。
|
||||||
- 备注:纯客户端改动,F5 即生效,无需重启 dsh web。
|
- 备注:纯客户端改动,F5 即生效,无需重启 dsh web。
|
||||||
|
|||||||
@@ -167,21 +167,36 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|||||||
})()`);
|
})()`);
|
||||||
console.log('STEP8:', JSON.stringify(step8));
|
console.log('STEP8:', JSON.stringify(step8));
|
||||||
|
|
||||||
// 变更视图:布局行 🧩 → 选择新预设 → 拓扑重建 + 内容标签自动迁入
|
// 变更视图:常驻项目与布局项目通用 🧩 → 选择新预设 → 拓扑重建 + 内容标签自动迁入
|
||||||
const step9 = await evaluate(`(async function(){
|
const step9 = await evaluate(`(async function(){
|
||||||
var out={};
|
var out={};
|
||||||
var btn=document.querySelector('.dsh-wt_actions .dsh-wt_iconBtn:nth-child(2)');
|
var btn=document.querySelector('.dsh-wt_actions .dsh-wt_iconBtn:nth-child(2)');
|
||||||
if(btn){ btn.click(); }
|
if(btn){ btn.click(); }
|
||||||
await new Promise(function(r){setTimeout(r,300)});
|
await new Promise(function(r){setTimeout(r,300)});
|
||||||
var rows=document.querySelectorAll('.dsh-wt_settings .dsh-wt_manageRow:not(.dsh-wt_manageRowSc)');
|
var rows=document.querySelectorAll('.dsh-wt_settings .dsh-wt_manageRow:not(.dsh-wt_manageRowSc)');
|
||||||
var layoutRow=rows[rows.length-1];
|
out.rowsCount=rows.length;
|
||||||
if(layoutRow){ out.layoutRowBtns=layoutRow.querySelectorAll('.dsh-wt_manageBtn').length; }
|
out.row0Btns=rows[0]?rows[0].querySelectorAll('.dsh-wt_manageBtn').length:0;
|
||||||
|
var resPuzzle=rows[0]&&rows[0].querySelectorAll('.dsh-wt_manageBtn')[rows[0].querySelectorAll('.dsh-wt_manageBtn').length-2];
|
||||||
|
if(resPuzzle){ resPuzzle.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,300)});
|
||||||
|
out.viewPicker1=!!document.querySelector('.dsh-wt_pop .dsh-wt_presets');
|
||||||
|
var presets1=document.querySelectorAll('.dsh-wt_presets .dsh-wt_preset');
|
||||||
|
if(presets1[1]){ presets1[1].click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,400)});
|
||||||
|
try{
|
||||||
|
var vv=JSON.parse(localStorage.getItem('dsh.worktable.projects.v1')).views||{};
|
||||||
|
out.viewsKeys=Object.keys(vv);
|
||||||
|
var rv=vv[out.viewsKeys[0]];
|
||||||
|
if(rv){ out.resTop=(rv.top||[]).length; out.resMain=rv.main.length; out.resChatFull=rv.chatFullHeight===true; out.resId=rv.id; }
|
||||||
|
}catch(e){ out.resErr=String(e) }
|
||||||
|
var rows2=document.querySelectorAll('.dsh-wt_settings .dsh-wt_manageRow:not(.dsh-wt_manageRowSc)');
|
||||||
|
var layoutRow=rows2[rows2.length-1];
|
||||||
|
out.layoutRowBtns=layoutRow?layoutRow.querySelectorAll('.dsh-wt_manageBtn').length:0;
|
||||||
var puzzle=layoutRow&&layoutRow.querySelectorAll('.dsh-wt_manageBtn')[layoutRow.querySelectorAll('.dsh-wt_manageBtn').length-2];
|
var puzzle=layoutRow&&layoutRow.querySelectorAll('.dsh-wt_manageBtn')[layoutRow.querySelectorAll('.dsh-wt_manageBtn').length-2];
|
||||||
if(puzzle){ puzzle.click(); }
|
if(puzzle){ puzzle.click(); }
|
||||||
await new Promise(function(r){setTimeout(r,300)});
|
await new Promise(function(r){setTimeout(r,300)});
|
||||||
out.viewPicker=!!document.querySelector('.dsh-wt_pop .dsh-wt_presets');
|
|
||||||
out.presetCount=document.querySelectorAll('.dsh-wt_presets .dsh-wt_preset').length;
|
|
||||||
var presets=document.querySelectorAll('.dsh-wt_presets .dsh-wt_preset');
|
var presets=document.querySelectorAll('.dsh-wt_presets .dsh-wt_preset');
|
||||||
|
out.presetCount=presets.length;
|
||||||
if(presets[2]){ presets[2].click(); }
|
if(presets[2]){ presets[2].click(); }
|
||||||
await new Promise(function(r){setTimeout(r,400)});
|
await new Promise(function(r){setTimeout(r,400)});
|
||||||
try{
|
try{
|
||||||
@@ -239,7 +254,7 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|||||||
out.cardsAfter={ta:document.querySelectorAll('.dsh-wt_projects .ta_card').length,pr:document.querySelectorAll('.dsh-wt_projects .pr_card').length};
|
out.cardsAfter={ta:document.querySelectorAll('.dsh-wt_projects .ta_card').length,pr:document.querySelectorAll('.dsh-wt_projects .pr_card').length};
|
||||||
try{ out.removed=JSON.parse(localStorage.getItem('dsh.worktable.projects.v1')).removed }catch(e){ out.removed='ERR' }
|
try{ out.removed=JSON.parse(localStorage.getItem('dsh.worktable.projects.v1')).removed }catch(e){ out.removed='ERR' }
|
||||||
out.rowsAfterDelete=document.querySelectorAll('.dsh-wt_settings .dsh-wt_manageRow').length;
|
out.rowsAfterDelete=document.querySelectorAll('.dsh-wt_settings .dsh-wt_manageRow').length;
|
||||||
var rows3=document.querySelectorAll('.dsh-wt_settings .dsh-wt_manageRow:not(.dsh-wt_manageRowSc)');
|
var rows3=document.querySelectorAll('.dsh-wt_settings .dsh-wt_manageRow:not(.dsh-wt_manageRowSc):not(.dsh-wt_manageRowRemoved)');
|
||||||
var lastRow=rows3[rows3.length-1];
|
var lastRow=rows3[rows3.length-1];
|
||||||
var xBtn3=lastRow&&lastRow.querySelectorAll('.dsh-wt_manageBtn')[lastRow.querySelectorAll('.dsh-wt_manageBtn').length-1];
|
var xBtn3=lastRow&&lastRow.querySelectorAll('.dsh-wt_manageBtn')[lastRow.querySelectorAll('.dsh-wt_manageBtn').length-1];
|
||||||
if(xBtn3){ xBtn3.click(); }
|
if(xBtn3){ xBtn3.click(); }
|
||||||
@@ -249,6 +264,17 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|||||||
await new Promise(function(r){setTimeout(r,300)});
|
await new Promise(function(r){setTimeout(r,300)});
|
||||||
out.layoutGoneAfterDelete=!document.querySelector('.dsh-wt_layout');
|
out.layoutGoneAfterDelete=!document.querySelector('.dsh-wt_layout');
|
||||||
try{ out.layoutsLeft=JSON.parse(localStorage.getItem('dsh.worktable.projects.v1')).layouts.length }catch(e){ out.layoutsLeft='ERR' }
|
try{ out.layoutsLeft=JSON.parse(localStorage.getItem('dsh.worktable.projects.v1')).layouts.length }catch(e){ out.layoutsLeft='ERR' }
|
||||||
|
// 恢复默认不应复活已删除项目;「已删除的项目」里重新添加可逆
|
||||||
|
var resetBtn=document.querySelector('.dsh-wt_manageReset');
|
||||||
|
if(resetBtn){ resetBtn.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,250)});
|
||||||
|
try{ out.removedAfterReset=JSON.parse(localStorage.getItem('dsh.worktable.projects.v1')).removed }catch(e){ out.removedAfterReset='ERR' }
|
||||||
|
out.removedSection=!!document.querySelector('.dsh-wt_settings .dsh-wt_manageRowRemoved');
|
||||||
|
var readdBtn=document.querySelector('.dsh-wt_settings .dsh-wt_manageRowRemoved .dsh-wt_manageBtn');
|
||||||
|
if(readdBtn){ readdBtn.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,300)});
|
||||||
|
try{ out.removedAfterReadd=JSON.parse(localStorage.getItem('dsh.worktable.projects.v1')).removed }catch(e){ out.removedAfterReadd='ERR' }
|
||||||
|
out.prBackAfterReadd=document.querySelectorAll('.dsh-wt_projects .pr_card').length;
|
||||||
var done=document.querySelector('.dsh-wt_manageDone');
|
var done=document.querySelector('.dsh-wt_manageDone');
|
||||||
if(done){ done.click(); }
|
if(done){ done.click(); }
|
||||||
await new Promise(function(r){setTimeout(r,200)});
|
await new Promise(function(r){setTimeout(r,200)});
|
||||||
|
|||||||
Reference in New Issue
Block a user