dsh-design-studio 0.13.0 → 0.13.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.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/lib/client.js +40 -9
- package/lib/index.js +16 -8
- package/lib/look.js +32 -2
- package/lib/picker.js +31 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -42,7 +42,7 @@ If DSH can't reach the npm registry, it falls back to a mirror in China. To upda
|
|
|
42
42
|
- **Open what you have:** click "Browse…" on the empty page to pick an HTML file, or drop an HTML file or folder onto the sidebar (desktop app); you can also type a path or a dev-server port.
|
|
43
43
|
- **Design check:** one click finds overflowing text, low contrast, small tap targets and tiny text, marked right on the page. In Design mode the model also checks its own work and fixes what it finds.
|
|
44
44
|
- **Versions and compare:** every turn is saved; drag a slider to compare, and go back to any version.
|
|
45
|
-
- **Export and phone preview:** showcase images, PDF and PowerPoint (links between screens still work); scan a QR code to see it on your phone at real size.
|
|
45
|
+
- **Export and phone preview:** showcase images, PDF and PowerPoint (links between screens still work); scan a QR code to see it on your phone at real size. Slides and posters larger than the default canvas now use the page's real size. You can also set it by adding `WIDTHxHEIGHT` after the type, for example `<meta name="dsh-design" content="slides 1440x810">`.
|
|
46
46
|
- **Design library and gallery:** everything you've designed, plus saved references and styles, lives in the Library. Browse templates, design systems and style cards others shared, and "Start from this" copies one for you to keep editing. Share your own with "Share to the gallery": it's checked, zipped and submitted on a web page, and listed once reviewed. The gallery catalog comes from npm mirrors and is used only when its signature and checksums match; to keep it offline, turn on "Design library offline" on the Plugins page.
|
|
47
47
|
- **Your brand, and real code:** give a site URL to extract a brand design system and design with it from then on; turn a finished design into React / Vue / HTML (mini programs straight into uni-app or native WeChat code, HBuilderX projects included), compared screen by screen against the design.
|
|
48
48
|
|
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
- **打开已有的页面**:首页输入框左边的「+」→「打开已有的 HTML…」选一个,或把 HTML 文件 / 文件夹拖进右侧栏就能预览(桌面版);也能输路径或开发服务器端口。
|
|
43
43
|
- **设计体检**:一键查文字溢出、对比度不够、按钮太小、字太小,问题直接标在页面上;模型做完也会自己查一遍再改。
|
|
44
44
|
- **版本和对比**:每轮自动存一版,左右拖动对比,随时回到任意一版。
|
|
45
|
-
- **导出和手机上看**:导出展示图、PDF、PPT
|
|
45
|
+
- **导出和手机上看**:导出展示图、PDF、PPT(屏和屏之间的跳转照样能点);扫码在手机上按真机尺寸看。页面比默认画幅大的幻灯片、海报,画幅自动跟页面走;也可以在类型后面跟 `宽x高` 指定,比如 `<meta name="dsh-design" content="slides 1440x810">`。
|
|
46
46
|
- **设计库和设计市场**:以前做过的设计、收的参考图和风格都在「设计库」里;市场里逛大家分享的模板、设计系统和风格卡,点「用这个开始」复制一份接着改。自己做的点「分享到设计市场」,查过打成 zip,在网页里交上去,审过就上架。市场目录从 npm 镜像取,签了名、核对过才用;不想让它联网,在插件页打开「设计库不联网」。
|
|
47
47
|
- **照品牌风格做、转成代码**:给官网地址,提一套品牌设计系统,以后都照它做;做好的设计转成 React / Vue / HTML,小程序直接转成 uni-app 或原生小程序代码(HBuilderX 建的项目也认得出),逐屏和设计稿比对。
|
|
48
48
|
|
package/lib/client.js
CHANGED
|
@@ -2057,6 +2057,7 @@ window.__ModuleLoader__.load({
|
|
|
2057
2057
|
.ddp-home.over{position:absolute;inset:0;z-index:10;background-color:var(--dsw-alias-bg-base,var(--dsw-alias-bg-layer-1));animation:ddp-homein .28s ${EASE} both}
|
|
2058
2058
|
.ddp-home.over.out{animation:ddp-homeout .19s ease both;pointer-events:none}
|
|
2059
2059
|
.ddp-home .ddp-start>*{animation:ddp-rise .5s ${EASE} both}
|
|
2060
|
+
.ddp-home .ddp-start>.ddp-askwrap{position:relative;z-index:5}
|
|
2060
2061
|
.ddp-home .ddp-start>:nth-child(2){animation-delay:.05s}
|
|
2061
2062
|
.ddp-home .ddp-start>:nth-child(3){animation-delay:.1s}
|
|
2062
2063
|
.ddp-home .ddp-start>:nth-child(4){animation-delay:.15s}
|
|
@@ -4300,6 +4301,28 @@ ${Object.entries(CHIP_ICON).map(([k, d]) => `[data-composer-chip="${chipSrc(k)}"
|
|
|
4300
4301
|
return KIND_SIZE[kind] || KIND_SIZE.web
|
|
4301
4302
|
}
|
|
4302
4303
|
|
|
4304
|
+
// 定死画面的类型:页面比默认画幅大(写死了 1440×810 又没写进 meta)就按页面实际大小。比例不像一页的(几页竖着堆的)不算
|
|
4305
|
+
const AUTO_RATIO = { slides: [1.2, 2.4], poster: [0.45, 2.5] }
|
|
4306
|
+
|
|
4307
|
+
/** 各屏量到的实际大小 → 'WxH'(都没比默认大:'')。和 look.js 的 autoSize 一样 */
|
|
4308
|
+
function autoSize(list, kind) {
|
|
4309
|
+
const range = AUTO_RATIO[kind]
|
|
4310
|
+
if (!range) return ''
|
|
4311
|
+
const [dw, dh] = KIND_SIZE[kind]
|
|
4312
|
+
let W = 0, H = 0
|
|
4313
|
+
for (const n of list) {
|
|
4314
|
+
if (!Array.isArray(n)) continue
|
|
4315
|
+
const w = Math.ceil(+n[0]), h = Math.ceil(+n[1])
|
|
4316
|
+
if (!(w >= 200 && h >= 200 && w <= 4000 && h <= 6000)) continue
|
|
4317
|
+
if ((w <= dw && h <= dh) || w / h < range[0] || w / h > range[1]) continue
|
|
4318
|
+
W = Math.max(W, w)
|
|
4319
|
+
H = Math.max(H, h)
|
|
4320
|
+
}
|
|
4321
|
+
if (!W) return ''
|
|
4322
|
+
// 一组屏共用一个画幅:别的屏量不出比默认大(正好默认大小的也报不出来),画幅不能比默认小,不然它们被裁
|
|
4323
|
+
return list.length > 1 ? `${Math.max(W, dw)}x${Math.max(H, dh)}` : `${W}x${H}`
|
|
4324
|
+
}
|
|
4325
|
+
|
|
4303
4326
|
/** 外框加上去之后的整块大小 */
|
|
4304
4327
|
function deviceBox(kind, size) {
|
|
4305
4328
|
const f = FRAME[kind] || FRAME.web
|
|
@@ -5085,7 +5108,12 @@ ${Object.entries(CHIP_ICON).map(([k, d]) => `[data-composer-chip="${chipSrc(k)}"
|
|
|
5085
5108
|
if (!f || !f.contentWindow) return
|
|
5086
5109
|
// 远程看:沙盒里的页面是不透明的源,只能发给 *(发的都是界面指令,没有要保密的)
|
|
5087
5110
|
if (f.hasAttribute('srcdoc')) f.contentWindow.postMessage({ dshDesign: 1, ...msg }, '*')
|
|
5088
|
-
else
|
|
5111
|
+
else {
|
|
5112
|
+
// 按这一帧自己的地址发:单看时换到别的设计目录,还是同一个组件,api 里留着的旧闭包记的是旧地址,消息会被浏览器丢掉(导出一直等到超时)
|
|
5113
|
+
let to = ''
|
|
5114
|
+
try { to = new URL(f.getAttribute('src')).origin } catch { /* 地址不对就不发 */ }
|
|
5115
|
+
if (to) f.contentWindow.postMessage({ dshDesign: 1, ...msg }, to)
|
|
5116
|
+
}
|
|
5089
5117
|
}
|
|
5090
5118
|
|
|
5091
5119
|
// 在后台载一帧新的;正在载就记一笔,等它换上来再载
|
|
@@ -5205,7 +5233,7 @@ ${Object.entries(CHIP_ICON).map(([k, d]) => `[data-composer-chip="${chipSrc(k)}"
|
|
|
5205
5233
|
L.title = d.title || ''
|
|
5206
5234
|
if (p.dark) postTo(id, { type: 'theme', dark: true })
|
|
5207
5235
|
if (p.tweak) postTo(id, { type: 'tweak', vars: p.tweak.vars, attrs: p.tweak.attrs })
|
|
5208
|
-
if (p.onMeta) p.onMeta(d.meta || '', d.title || '', Array.isArray(d.tokens) ? d.tokens : [], !!d.dark, Array.isArray(d.links) ? d.links : [])
|
|
5236
|
+
if (p.onMeta) p.onMeta(d.meta || '', d.title || '', Array.isArray(d.tokens) ? d.tokens : [], !!d.dark, Array.isArray(d.links) ? d.links : [], d.natural)
|
|
5209
5237
|
if (p.onHref && d.href) p.onHref(d.href)
|
|
5210
5238
|
if (isCur) {
|
|
5211
5239
|
postTo(id, { type: 'reveal' })
|
|
@@ -7578,7 +7606,7 @@ ${Object.entries(CHIP_ICON).map(([k, d]) => `[data-composer-chip="${chipSrc(k)}"
|
|
|
7578
7606
|
const meta = parseMeta(main.meta || (st && st.draft && st.draft.meta) || (playR.current.end ? '' : playR.current.meta) || '') // 第一屏还在放草稿(真的没摆出来):按草稿开头写的类型
|
|
7579
7607
|
const tokens = main.tokens || []
|
|
7580
7608
|
const kind = (st && st.kind) || meta.kind || 'web'
|
|
7581
|
-
const size = sizeOf((st && st.size) || meta.size, kind)
|
|
7609
|
+
const size = sizeOf((st && st.size) || meta.size || autoSize(screens.map((sc) => (infoOf(ik(sc)) || {}).natural), kind), kind)
|
|
7582
7610
|
const view = (st && st.view) || (screens.length >= 2 && screens.length <= 12 ? 'canvas' : 'single')
|
|
7583
7611
|
const canvas = view === 'canvas'
|
|
7584
7612
|
const writingNew = !!(st && st.writing) && /\.html?$/i.test(st.writing) && !screens.some((s) => s.key === st.writing)
|
|
@@ -7764,11 +7792,14 @@ ${Object.entries(CHIP_ICON).map(([k, d]) => `[data-composer-chip="${chipSrc(k)}"
|
|
|
7764
7792
|
showFlash(t('born', { name: title || labelOf(sc) }))
|
|
7765
7793
|
}
|
|
7766
7794
|
|
|
7767
|
-
const remember = (key, m, title, toks, dk, links) => {
|
|
7795
|
+
const remember = (key, m, title, toks, dk, links, nat) => {
|
|
7768
7796
|
const k = site + '|' + key
|
|
7769
7797
|
const sig = JSON.stringify([toks || [], links || []])
|
|
7770
|
-
setInfo((cur) =>
|
|
7771
|
-
|
|
7798
|
+
setInfo((cur) => {
|
|
7799
|
+
const natural = nat || (cur[k] && cur[k].natural) || null // 画幅放大后页面不再比框大、报空:还按上回量的
|
|
7800
|
+
return cur[k] && cur[k].meta === m && cur[k].title === title && cur[k].sig === sig && cur[k].dark === dk && String(cur[k].natural) === String(natural) ? cur
|
|
7801
|
+
: { ...cur, [k]: { meta: m, title, tokens: toks || [], links: links || [], sig, dark: dk, natural } }
|
|
7802
|
+
})
|
|
7772
7803
|
}
|
|
7773
7804
|
|
|
7774
7805
|
const rememberLinks = (key, links) => {
|
|
@@ -8068,7 +8099,7 @@ ${Object.entries(CHIP_ICON).map(([k, d]) => `[data-composer-chip="${chipSrc(k)}"
|
|
|
8068
8099
|
onEdited: (d) => onEdited(sc, d),
|
|
8069
8100
|
onEditDone: () => setTextEdit(false),
|
|
8070
8101
|
onNote: (n) => note(n, sc),
|
|
8071
|
-
onMeta: (m, title, toks, dk, links) => remember(ik(sc), m, title, toks, dk, links),
|
|
8102
|
+
onMeta: (m, title, toks, dk, links, nat) => remember(ik(sc), m, title, toks, dk, links, nat),
|
|
8072
8103
|
onLinks: (links) => rememberLinks(ik(sc), links),
|
|
8073
8104
|
onTop: (dk) => setTop(ik(sc), dk),
|
|
8074
8105
|
onBuilt: (title) => onBuilt(sc, title),
|
|
@@ -9031,7 +9062,7 @@ ${Object.entries(CHIP_ICON).map(([k, d]) => `[data-composer-chip="${chipSrc(k)}"
|
|
|
9031
9062
|
onNav: isTop ? demoGo : undefined,
|
|
9032
9063
|
onHref: isTop ? (href) => demoHref(l.id, href) : undefined,
|
|
9033
9064
|
onShown: () => demoShown(l.id),
|
|
9034
|
-
onMeta: (m, title, toks, dk, links) => remember(l.key, m, title, toks, dk, links),
|
|
9065
|
+
onMeta: (m, title, toks, dk, links, nat) => remember(l.key, m, title, toks, dk, links, nat),
|
|
9035
9066
|
onLinks: (links) => rememberLinks(l.key, links),
|
|
9036
9067
|
onTop: (dk) => setTop(l.key, dk),
|
|
9037
9068
|
})))))
|
|
@@ -9366,7 +9397,7 @@ ${Object.entries(CHIP_ICON).map(([k, d]) => `[data-composer-chip="${chipSrc(k)}"
|
|
|
9366
9397
|
// 注意:这里是客户端**服务名**(短的),不是包名
|
|
9367
9398
|
exports.inject = INJECT
|
|
9368
9399
|
// 测试用
|
|
9369
|
-
exports._test = { pulseStep, MarketDetail, dragKind, droppedOf, pickInfo, pickLabel, pickRanges, pickRule, chipOf, chipRanges, makeT, ZH, EN, formatCheck, formatTweaks, markHit, markPath, STYLES, formatStyle, accentUse, autoStyle, styleLook, padInsert, pageOf, pxOf, styleChanges, formatStyleAsk, takesOf, formatCheckTool, poller, frameOf, paletteOf, formatRef, layoutRow, keyOfPath, flowsOf, flowLines, flowMarks, formatPick, whereOf, parseMeta, sizeOf, layoutGrid, fitCam, tokenKind, formatToken, splitTitles, pickColors, hhmm, formatTpl, formatPrep, styleFromTokens, myStyleOf, isZipFile }
|
|
9400
|
+
exports._test = { autoSize, pulseStep, MarketDetail, dragKind, droppedOf, pickInfo, pickLabel, pickRanges, pickRule, chipOf, chipRanges, makeT, ZH, EN, formatCheck, formatTweaks, markHit, markPath, STYLES, formatStyle, accentUse, autoStyle, styleLook, padInsert, pageOf, pxOf, styleChanges, formatStyleAsk, takesOf, formatCheckTool, poller, frameOf, paletteOf, formatRef, layoutRow, keyOfPath, flowsOf, flowLines, flowMarks, formatPick, whereOf, parseMeta, sizeOf, layoutGrid, fitCam, tokenKind, formatToken, splitTitles, pickColors, hhmm, formatTpl, formatPrep, styleFromTokens, myStyleOf, isZipFile }
|
|
9370
9401
|
return module.exports
|
|
9371
9402
|
},
|
|
9372
9403
|
})
|
package/lib/index.js
CHANGED
|
@@ -60,7 +60,7 @@ import { createPhone } from './phone.js'
|
|
|
60
60
|
import { qrMatrix } from './qr.js'
|
|
61
61
|
import { makePdf, makePptx, jpegInfo } from './doc.js'
|
|
62
62
|
import { createBrowser } from './browser.js'
|
|
63
|
-
import { designOf, metaOfFile, sizeOf, pickScreens, measureAll, shootAll, cropIssues, formatMeasure, issueSnapshot, formatShots, whereOf, MAX_SCREENS, MAX_SIZES, MAX_SHOTS, MAX_CROPS } from './look.js'
|
|
63
|
+
import { designOf, fitSize, metaOfFile, sizeOf, pickScreens, measureAll, shootAll, cropIssues, formatMeasure, issueSnapshot, formatShots, whereOf, MAX_SCREENS, MAX_SIZES, MAX_SHOTS, MAX_CROPS } from './look.js'
|
|
64
64
|
import { SYSTEMS_DIR, extractSystem, listSystems, setDefault, skillSection } from './system.js'
|
|
65
65
|
import { readTweaks, applyTweaks, lintTweaks } from './tweaks.js'
|
|
66
66
|
import { FRAMEWORKS, detectProject, projectFor, defaultFramework, buildHandoff, compareScreens, implUrl, defaultDest, pickedDest, within } from './handoff.js'
|
|
@@ -176,6 +176,9 @@ export const previewTool = {
|
|
|
176
176
|
},
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/** 无头浏览器用的尺寸:没写尺寸的幻灯片 / 海报按页面实际大小(和右侧栏的画幅一样) */
|
|
180
|
+
const fitted = (b, d, names) => (d.auto && b && b.available() ? fitSize(b, pickScreens(d.snap, names, names && names.length ? 40 : 6).list, d.kind, d.size) : d.size)
|
|
181
|
+
|
|
179
182
|
/** 这个会话在预览的设计:哪几屏、哪类、多大(工具在无头浏览器里量 / 截图用) */
|
|
180
183
|
function designFor(id) {
|
|
181
184
|
const snap = studio.tracker.snapshot(id)
|
|
@@ -236,14 +239,15 @@ export const checkTool = {
|
|
|
236
239
|
const id = toolSession(exec, '体检')
|
|
237
240
|
const a = args || {}
|
|
238
241
|
if (!studio.tracker.snapshot(id).url) return { text: '还没有在预览的页面:先用 preview_page 打开设计文件,再体检。' }
|
|
239
|
-
const
|
|
242
|
+
const design = designFor(id)
|
|
243
|
+
const { snap, kind } = design
|
|
240
244
|
const want = sizesFrom(a.sizes, kind)
|
|
241
245
|
const b = studio.browser
|
|
242
246
|
if (b && b.available()) {
|
|
243
247
|
try {
|
|
244
248
|
const pick = pickScreens(snap, a.screens, MAX_SCREENS)
|
|
245
249
|
if (!pick.list.length) return { text: `没找到要量的屏:${pick.missing.join('、')}。现在有:${(snap.screens || []).map((x) => x.key).join('、') || snap.focus}` }
|
|
246
|
-
const sizes = want.sizes.length ? want.sizes : [
|
|
250
|
+
const sizes = want.sizes.length ? want.sizes : [await fitted(b, design, a.screens)]
|
|
247
251
|
const rows = await measureAll(b, { screens: pick.list, sizes, kind, dark: !!a.dark, locate: (items) => locateRules(studio.tracker.stateOf(id), items) })
|
|
248
252
|
if (rows.some((r) => !r.error)) {
|
|
249
253
|
// 同一个会话上次量的结果:这次比一比修好了几个、新出了几个
|
|
@@ -369,8 +373,9 @@ export const lookTool = {
|
|
|
369
373
|
if (!studio.tracker.snapshot(id).url) return { text: '还没有在预览的页面:先用 preview_page 打开设计文件,再看。' }
|
|
370
374
|
const b = studio.browser
|
|
371
375
|
if (!b || !b.available()) return { text: '这台电脑上没找到 Chrome / Edge(无头浏览器截图要用),看不了。用 check_design 拿测量数字,或者让用户看右侧栏。' }
|
|
372
|
-
const
|
|
373
|
-
const
|
|
376
|
+
const design = designFor(id)
|
|
377
|
+
const { snap, kind } = design
|
|
378
|
+
const want = a.size ? sizeOf(a.size, kind) : await fitted(b, design, a.screens)
|
|
374
379
|
if (!want) return { text: `认不出尺寸「${String(a.size).slice(0, 20)}」:写成 375x667,或只写宽度 375。` }
|
|
375
380
|
const at = ['top', 'bottom', 'full'].includes(a.at) ? a.at : 'top'
|
|
376
381
|
const pick = pickScreens(snap, a.screens, MAX_SHOTS)
|
|
@@ -469,7 +474,9 @@ export const handoffTool = {
|
|
|
469
474
|
if (!snap.url) return { text: '还没有在预览的设计:先用 preview_page 打开设计文件。' }
|
|
470
475
|
const t = studio.tracker.stateOf(id).target
|
|
471
476
|
if (!t || t.kind !== 'file') return { text: '现在预览的是开发服务器上的页面,它已经是代码了:直接在那个项目里改;要比对设计稿就先用 preview_page 打开设计的 HTML 文件。' }
|
|
472
|
-
const
|
|
477
|
+
const design = designFor(id)
|
|
478
|
+
const { kind } = design
|
|
479
|
+
const size = await fitted(studio.browser, design, a.screens)
|
|
473
480
|
const pick = pickScreens(snap, a.screens, 12)
|
|
474
481
|
if (!pick.list.length) return { text: `没找到要转的屏:${pick.missing.join('、')}。现在有:${(snap.screens || []).map((x) => x.key).join('、')}` }
|
|
475
482
|
const cwd = studio.tracker.cwdOf(id)
|
|
@@ -524,8 +531,9 @@ export const compareTool = {
|
|
|
524
531
|
if (!snap.url) return { text: '还没有在预览的设计稿:先用 preview_page 打开设计文件,再比。' }
|
|
525
532
|
const b = studio.browser
|
|
526
533
|
if (!b || !b.available()) return { text: '这台电脑上没找到 Chrome / Edge(比对要用无头浏览器截图),比不了。' }
|
|
527
|
-
const
|
|
528
|
-
const
|
|
534
|
+
const design = designFor(id)
|
|
535
|
+
const { kind } = design
|
|
536
|
+
const want = a.size ? sizeOf(a.size, kind) : await fitted(b, design, typeof a.screen === 'string' && a.screen.trim() ? [a.screen] : null)
|
|
529
537
|
if (!want) return { text: `认不出尺寸「${String(a.size).slice(0, 20)}」:写成 375x667,或只写宽度 375。` }
|
|
530
538
|
// 指定了哪屏就只认那屏,找不到就说(别悄悄拿第一屏比,结果对不上号);没指定才用正在看的 / 第一屏
|
|
531
539
|
const asked = typeof a.screen === 'string' && a.screen.trim()
|
package/lib/look.js
CHANGED
|
@@ -51,6 +51,28 @@ export function sizeOf(v, kind) {
|
|
|
51
51
|
return [w, dh]
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// 定死画面的类型:页面比默认画幅大(写死了 1440×810 又没写进 meta)就按页面实际大小。比例不像一页的(几页竖着堆的)不算
|
|
55
|
+
const AUTO_RATIO = { slides: [1.2, 2.4], poster: [0.45, 2.5] }
|
|
56
|
+
|
|
57
|
+
/** 各屏量到的实际大小 → 'WxH'(都没比默认大:'')。和右侧栏 client.js 的 autoSize 一样 */
|
|
58
|
+
export function autoSize(list, kind) {
|
|
59
|
+
const range = AUTO_RATIO[kind]
|
|
60
|
+
if (!range) return ''
|
|
61
|
+
const [dw, dh] = KIND_SIZE[kind]
|
|
62
|
+
let W = 0, H = 0
|
|
63
|
+
for (const n of list) {
|
|
64
|
+
if (!Array.isArray(n)) continue
|
|
65
|
+
const w = Math.ceil(+n[0]), h = Math.ceil(+n[1])
|
|
66
|
+
if (!(w >= 200 && h >= 200 && w <= 4000 && h <= 6000)) continue
|
|
67
|
+
if ((w <= dw && h <= dh) || w / h < range[0] || w / h > range[1]) continue
|
|
68
|
+
W = Math.max(W, w)
|
|
69
|
+
H = Math.max(H, h)
|
|
70
|
+
}
|
|
71
|
+
if (!W) return ''
|
|
72
|
+
// 一组屏共用一个画幅:别的屏量不出比默认大(正好默认大小的也报不出来),画幅不能比默认小,不然它们被裁
|
|
73
|
+
return list.length > 1 ? `${Math.max(W, dw)}x${Math.max(H, dh)}` : `${W}x${H}`
|
|
74
|
+
}
|
|
75
|
+
|
|
54
76
|
/** 设计稿是哪类、多大:preview_page 给的 → 页面里的 <meta name="dsh-design"> → 默认网页 */
|
|
55
77
|
export function designOf(snap, readHead) {
|
|
56
78
|
let meta = { kind: '', size: '' }
|
|
@@ -58,8 +80,9 @@ export function designOf(snap, readHead) {
|
|
|
58
80
|
try { meta = parseMeta(readHead() || '') } catch { /* 读不到就按默认 */ }
|
|
59
81
|
}
|
|
60
82
|
const kind = KIND_LIST.includes(snap.kind) ? snap.kind : meta.kind || 'web'
|
|
61
|
-
const
|
|
62
|
-
|
|
83
|
+
const named = sizeOf(snap.size, kind) || sizeOf(meta.size, kind)
|
|
84
|
+
const size = named || KIND_SIZE[kind]
|
|
85
|
+
return !named && AUTO_RATIO[kind] ? { kind, size, auto: true } : { kind, size }
|
|
63
86
|
}
|
|
64
87
|
|
|
65
88
|
/** 本地 HTML 文件里 dsh-design 那个 meta 的内容(只读开头一段) */
|
|
@@ -109,6 +132,13 @@ export async function prepare(page, dark) {
|
|
|
109
132
|
return { info: await page.eval('window.__dshDesign.info()'), darkOk }
|
|
110
133
|
}
|
|
111
134
|
|
|
135
|
+
/** 没写尺寸的幻灯片 / 海报:开页面量一下,比默认画幅大就按页面实际大小(和右侧栏的画幅一样)。量不到就还是默认 */
|
|
136
|
+
export async function fitSize(browser, screens, kind, size) {
|
|
137
|
+
const [w, h] = size
|
|
138
|
+
const seen = await Promise.all(screens.map((sc) => browser.withPage({ url: sc.url, w, h }, async (page) => (await prepare(page, false)).info.natural).catch(() => null)))
|
|
139
|
+
return sizeOf(autoSize(seen, kind), kind) || size
|
|
140
|
+
}
|
|
141
|
+
|
|
112
142
|
/**
|
|
113
143
|
* 量:每屏 × 每种尺寸开一个页面跑体检。
|
|
114
144
|
* @returns [{ key, label, w, h, issues, metrics, error }]
|
package/lib/picker.js
CHANGED
|
@@ -993,6 +993,35 @@
|
|
|
993
993
|
|
|
994
994
|
const seedNow = () => ({ href: here(), scroll: { x: scrollX, y: scrollY }, sig: snapshot().map })
|
|
995
995
|
|
|
996
|
+
/**
|
|
997
|
+
* 最外层那块的实际大小 [宽, 高](幻灯片 / 海报常写死 1440×810,比框大的话画幅要跟它走)。
|
|
998
|
+
* 正好和框一样大(自适应的,或画幅已经跟过来了)、几页竖着堆的、还有别的东西往外撑的:null
|
|
999
|
+
*/
|
|
1000
|
+
function naturalSize() {
|
|
1001
|
+
const b = document.body
|
|
1002
|
+
if (!b) return null
|
|
1003
|
+
const skip = (el) => el.hasAttribute('data-dsh-design') || /^(SCRIPT|STYLE|LINK|NOSCRIPT|TEMPLATE)$/.test(el.tagName)
|
|
1004
|
+
let best = null
|
|
1005
|
+
let box = null
|
|
1006
|
+
let area = 0
|
|
1007
|
+
for (const el of [b, ...b.children]) {
|
|
1008
|
+
if (skip(el)) continue
|
|
1009
|
+
const r = el.getBoundingClientRect()
|
|
1010
|
+
if (r.width * r.height > area) { area = r.width * r.height; best = r; box = el }
|
|
1011
|
+
}
|
|
1012
|
+
if (!best) return null
|
|
1013
|
+
// 一样大的几页竖着堆(一个文件里放了几张幻灯片 / 海报):不是一页
|
|
1014
|
+
const kids = [...box.children].filter((k) => !skip(k)).map((k) => k.getBoundingClientRect()).filter((r) => r.width && r.height)
|
|
1015
|
+
if (kids.length >= 2 && kids[0].height >= innerHeight * 0.8 && kids.every((r) => Math.abs(r.width - kids[0].width) < 2 && Math.abs(r.height - kids[0].height) < 2) && kids[1].top >= kids[0].bottom - 1) return null
|
|
1016
|
+
const right = best.right + scrollX
|
|
1017
|
+
const bottom = best.bottom + scrollY
|
|
1018
|
+
const w = Math.ceil(Math.max(best.width, right))
|
|
1019
|
+
const h = Math.ceil(Math.max(best.height, bottom))
|
|
1020
|
+
if (Math.abs(w - innerWidth) <= 1 && Math.abs(h - innerHeight) <= 1) return null
|
|
1021
|
+
const de = document.scrollingElement || document.documentElement
|
|
1022
|
+
return de.scrollWidth > Math.max(innerWidth, right) + 40 || de.scrollHeight > Math.max(innerHeight, bottom) + 40 ? null : [w, h]
|
|
1023
|
+
}
|
|
1024
|
+
|
|
996
1025
|
function boot(seed) {
|
|
997
1026
|
if (booted) return
|
|
998
1027
|
booted = true
|
|
@@ -1008,6 +1037,7 @@
|
|
|
1008
1037
|
tokens: tokensOf(),
|
|
1009
1038
|
links: firstLinks(),
|
|
1010
1039
|
dark: hasDark(),
|
|
1040
|
+
natural: naturalSize(),
|
|
1011
1041
|
changes: pending ? { count: pending.count, whole: pending.whole } : null,
|
|
1012
1042
|
})
|
|
1013
1043
|
postTop()
|
|
@@ -4011,7 +4041,7 @@ background:rgba(20,20,22,.86);color:#e8e8ea;font:12px/18px -apple-system,system-
|
|
|
4011
4041
|
links: () => linksOf().map(({ path, text }) => ({ path, text })),
|
|
4012
4042
|
info: () => {
|
|
4013
4043
|
const meta = document.querySelector('meta[name="dsh-design"]')
|
|
4014
|
-
return { title: document.title, meta: meta ? String(meta.getAttribute('content') || '').slice(0, 80) : '', page: Math.max(se().scrollHeight, innerHeight), w: innerWidth, h: innerHeight, topDark: topDark() }
|
|
4044
|
+
return { title: document.title, meta: meta ? String(meta.getAttribute('content') || '').slice(0, 80) : '', page: Math.max(se().scrollHeight, innerHeight), w: innerWidth, h: innerHeight, topDark: topDark(), natural: naturalSize() }
|
|
4015
4045
|
},
|
|
4016
4046
|
/** 'end':所有能滚的地方都滚到底;数字 i:主要的那块滚到第 i 屏(相邻两屏留出顶栏、底栏的高度,栏后面的内容下一屏能看到) */
|
|
4017
4047
|
// 滚完等页面跟上再截图
|