feat(MCN工作台): 数据功能页+图标扁平化+多项UI优化
- 工作台大版本升级:hash 路由 + 6 数据功能页(周榜/热门账号/账号详情/视频详情/AI写脚本/AI写作复盘),只读复用 dsh 库副本 + 抖音榜单 JSON - 数据库隔离:复制 dsh 库到 mcn-work-shop/,与 dsh 原库各访问各的(已 gitignore) - 图标全面扁平化:Lucide 线性 SVG,功能图标亮色区分,文字标题去图标 - 首页重构:功能入口卡 + AI写剧本快捷卡片(2列/8个/5行预览) + 独立帮助页 + header 精简 - 新增设计技能 impeccable + taste-skill(生成网页时参考) - 修复:fmtTime 跨 script 全局冲突(P0)、弹窗关闭、外链新标签打开等
This commit is contained in:
1 parent
e1dc14e8a3
commit
d3f98dda77
120 files changed
+44528
-1
No files matched your search
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Browser-side DOM helpers for Impeccable live mode.
|
||||
*
|
||||
* Kept separate from live-browser.js so future browser script parts can share
|
||||
* chrome mounting, lookup, focus, and picker helpers without depending on the
|
||||
* full overlay UI bundle.
|
||||
*/
|
||||
(function (root) {
|
||||
'use strict';
|
||||
if (!root) return;
|
||||
|
||||
function createLiveBrowserDomHelpers({
|
||||
prefix,
|
||||
skipTags,
|
||||
document: doc = root.document,
|
||||
css = root.CSS,
|
||||
crypto = root.crypto,
|
||||
} = {}) {
|
||||
if (!prefix) throw new Error('prefix required');
|
||||
if (!doc) throw new Error('document required');
|
||||
const tagsToSkip = skipTags || new Set();
|
||||
|
||||
function own(el) {
|
||||
return el && (el.id?.startsWith(prefix) || el.closest?.('[id^="' + prefix + '"]'));
|
||||
}
|
||||
|
||||
function pickable(el) {
|
||||
if (!el || el.nodeType !== 1) return false;
|
||||
if (tagsToSkip.has(String(el.tagName || '').toLowerCase())) return false;
|
||||
if (own(el)) return false;
|
||||
const r = el.getBoundingClientRect();
|
||||
return r.width >= 20 && r.height >= 20;
|
||||
}
|
||||
|
||||
function desc(el) {
|
||||
if (!el) return '';
|
||||
let s = el.tagName.toLowerCase();
|
||||
if (el.id) s += '#' + el.id;
|
||||
else if (el.classList.length) s += '.' + [...el.classList].slice(0, 2).join('.');
|
||||
return s;
|
||||
}
|
||||
|
||||
function rectIsUsableAnchor(rect) {
|
||||
return !!rect && rect.width > 0.5 && rect.height > 0.5;
|
||||
}
|
||||
|
||||
function makeFrozenAnchor(el) {
|
||||
if (!el || !el.getBoundingClientRect) return null;
|
||||
const r = el.getBoundingClientRect();
|
||||
if (!rectIsUsableAnchor(r)) return null;
|
||||
const rect = {
|
||||
x: r.x, y: r.y,
|
||||
top: r.top, left: r.left,
|
||||
right: r.right, bottom: r.bottom,
|
||||
width: r.width, height: r.height,
|
||||
};
|
||||
return {
|
||||
__impeccableFrozenAnchor: true,
|
||||
tagName: el.tagName || 'DIV',
|
||||
id: el.id || '',
|
||||
classList: el.classList ? [...el.classList] : [],
|
||||
hasAttribute: () => false,
|
||||
getBoundingClientRect: () => rect,
|
||||
};
|
||||
}
|
||||
|
||||
function id8() {
|
||||
if (crypto?.randomUUID) return crypto.randomUUID().replace(/-/g, '').slice(0, 8);
|
||||
return (Math.random().toString(16).slice(2) + Date.now().toString(16)).slice(0, 8);
|
||||
}
|
||||
|
||||
function cssId(id) {
|
||||
if (css?.escape) return css.escape(id);
|
||||
return String(id).replace(/([ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '\\$1');
|
||||
}
|
||||
|
||||
function liveUiRoot() {
|
||||
const uiRoot = root.__IMPECCABLE_LIVE_UI_ROOT__;
|
||||
if (uiRoot && typeof uiRoot.appendChild === 'function') return uiRoot;
|
||||
return doc.body;
|
||||
}
|
||||
|
||||
function uiAppend(el) {
|
||||
liveUiRoot().appendChild(el);
|
||||
return el;
|
||||
}
|
||||
|
||||
function uiAppendStyle(styleEl) {
|
||||
const uiRoot = liveUiRoot();
|
||||
if (uiRoot && uiRoot !== doc.body) uiRoot.appendChild(styleEl);
|
||||
else doc.head.appendChild(styleEl);
|
||||
return styleEl;
|
||||
}
|
||||
|
||||
function uiGetById(id) {
|
||||
const uiRoot = liveUiRoot();
|
||||
if (uiRoot?.getElementById) {
|
||||
const found = uiRoot.getElementById(id);
|
||||
if (found) return found;
|
||||
}
|
||||
if (uiRoot?.querySelector) {
|
||||
const found = uiRoot.querySelector('#' + cssId(id));
|
||||
if (found) return found;
|
||||
}
|
||||
return doc.getElementById(id);
|
||||
}
|
||||
|
||||
function activeElementDeep() {
|
||||
let active = doc.activeElement;
|
||||
while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
|
||||
return active;
|
||||
}
|
||||
|
||||
function defangOutsideHandlers(rootEl, { setPointerEvents = true } = {}) {
|
||||
if (!rootEl) return;
|
||||
if (setPointerEvents) {
|
||||
rootEl.style.setProperty('pointer-events', 'auto', 'important');
|
||||
}
|
||||
const stop = (e) => e.stopPropagation();
|
||||
rootEl.addEventListener('pointerdown', stop);
|
||||
rootEl.addEventListener('mousedown', stop);
|
||||
rootEl.addEventListener('focusin', stop);
|
||||
}
|
||||
|
||||
return {
|
||||
own,
|
||||
pickable,
|
||||
desc,
|
||||
rectIsUsableAnchor,
|
||||
makeFrozenAnchor,
|
||||
id8,
|
||||
cssId,
|
||||
liveUiRoot,
|
||||
uiAppend,
|
||||
uiAppendStyle,
|
||||
uiGetById,
|
||||
activeElementDeep,
|
||||
defangOutsideHandlers,
|
||||
};
|
||||
}
|
||||
|
||||
root.__IMPECCABLE_LIVE_DOM__ = {
|
||||
version: 1,
|
||||
createLiveBrowserDomHelpers,
|
||||
};
|
||||
})(typeof window !== 'undefined' ? window : globalThis);
|
||||
Reference in new issue
Block a user