feat(V0.2): 版本方案落地 —— 前端/外壳解绑, build 标识统一, 外壳版本展示
Docker Build and Push / build-image (push) Successful in 1m5s
Docker Build and Push / build-image (push) Successful in 1m5s
- check-version: 分组校验 (前端 package.json / 外壳 tauri.conf+Cargo), 两组不再要求相等; 分支前缀校验只针对前端版本 - build-ota: build_id 改为 YYYYMMDDHHmmss (北京时间, 与前端 BUILD_ID 同格式); sequence 保持毫秒仅用于判新 - 我的页: 当前版本恢复 v0.2.0 · build <原始BUILD_ID>; 新版本附 OTA build_id; 底部替换为外壳版本 (Tauri getVersion, Web 端 —) - README: 合并遗留 readme 全部内容并按决议重写版本管理章节; 新增 CHANGELOG.md (PATCH 仅明确提出时自增)
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
// CDP 触摸手势驱动: 长按 400ms 激活 + 水平拖动, 观察 useRowDrag 行为
|
||||
import WebSocket from 'ws'
|
||||
import { spawn } from 'node:child_process'
|
||||
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0
|
||||
const pending = new Map()
|
||||
const send = (method, params = {}, sessionId) => new Promise((res) => {
|
||||
const i = ++id
|
||||
pending.set(i, res)
|
||||
ws.send(JSON.stringify({ id: i, method, params, sessionId }))
|
||||
})
|
||||
ws.on('message', (d) => {
|
||||
const m = JSON.parse(d)
|
||||
if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) }
|
||||
if (m.method === 'Runtime.consoleAPICalled') {
|
||||
console.log('[console]', m.params.type, m.params.args.map((a) => a.value ?? a.description).join(' '))
|
||||
}
|
||||
})
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
|
||||
const { targetId } = await send('Target.createTarget', { url: 'about:blank' })
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
await S('Runtime.enable')
|
||||
await S('Page.enable')
|
||||
await S('Emulation.setDeviceMetricsOverride', { width: 400, height: 800, deviceScaleFactor: 2, mobile: true })
|
||||
await S('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 })
|
||||
await S('Page.navigate', { url: 'http://127.0.0.1:5199/dragtest.html' })
|
||||
await new Promise((r) => setTimeout(r, 3000))
|
||||
|
||||
const sleep = (ms) => new Promise((res) => setTimeout(res, ms))
|
||||
const ev = async (expr) => (await S('Runtime.evaluate', { expression: expr, returnByValue: true })).result.value
|
||||
let rect = null
|
||||
for (let i = 0; i < 30 && !rect; i++) {
|
||||
await sleep(1000)
|
||||
const v = await ev(`(() => { const el = document.querySelector('[data-task-id="3"]'); return el ? JSON.stringify(el.getBoundingClientRect()) : null })()`)
|
||||
if (v) rect = JSON.parse(v)
|
||||
}
|
||||
if (!rect) { console.log('page never loaded'); ws.close(); process.exit(1) }
|
||||
console.log('row rect', JSON.stringify(rect))
|
||||
|
||||
const r = rect
|
||||
const x0 = r.left + r.width / 2
|
||||
const y0 = r.top + r.height / 2
|
||||
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', {
|
||||
type,
|
||||
touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }],
|
||||
})
|
||||
|
||||
// 1) touchStart + 按住 500ms (激活长按)
|
||||
await touch('touchStart', x0, y0)
|
||||
console.log('--- touchStart, holding 500ms')
|
||||
await sleep(500)
|
||||
|
||||
// 2) 水平拖动 +200px, 每步 +25px / 40ms (纯水平)
|
||||
for (let dx = 25; dx <= 200; dx += 25) {
|
||||
await touch('touchMove', x0 + dx, y0)
|
||||
await sleep(40)
|
||||
}
|
||||
console.log('--- moved to +200px (pure horizontal)')
|
||||
|
||||
// 3) 带纵向分量的拖动 (模拟真实手指): +160px 横 / +40px 纵
|
||||
await touch('touchMove', x0 + 60, y0 + 12)
|
||||
await sleep(40)
|
||||
await touch('touchMove', x0 + 120, y0 + 26)
|
||||
await sleep(40)
|
||||
await touch('touchMove', x0 + 160, y0 + 40)
|
||||
await sleep(40)
|
||||
|
||||
await touch('touchEnd')
|
||||
await sleep(600)
|
||||
|
||||
console.log('--- events on page:')
|
||||
console.log((await ev('window.__ev.join("\\n")')))
|
||||
ws.close()
|
||||
@@ -0,0 +1,77 @@
|
||||
// CDP 触摸手势驱动 v2: 把手立即拖动 + 长按路径, 组内/跨组落点
|
||||
import WebSocket from 'ws'
|
||||
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0
|
||||
const pending = new Map()
|
||||
const send = (method, params = {}, sessionId) => new Promise((res) => {
|
||||
const i = ++id
|
||||
pending.set(i, res)
|
||||
ws.send(JSON.stringify({ id: i, method, params, sessionId }))
|
||||
})
|
||||
ws.on('message', (d) => {
|
||||
const m = JSON.parse(d)
|
||||
if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) }
|
||||
})
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
|
||||
const { targetId } = await send('Target.createTarget', { url: 'about:blank' })
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
await S('Runtime.enable'); await S('Page.enable')
|
||||
await S('Emulation.setDeviceMetricsOverride', { width: 400, height: 800, deviceScaleFactor: 2, mobile: true })
|
||||
await S('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 })
|
||||
await S('Page.navigate', { url: 'http://127.0.0.1:5199/dragtest.html' })
|
||||
|
||||
const sleep = (ms) => new Promise((res) => setTimeout(res, ms))
|
||||
const ev = async (expr) => (await S('Runtime.evaluate', { expression: expr, returnByValue: true })).result.value
|
||||
|
||||
const center = async (sel) => {
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('${sel}').getBoundingClientRect())`))
|
||||
return { x: r.left + r.width / 2, y: r.top + r.height / 2 }
|
||||
}
|
||||
for (let i = 0; i < 30; i++) { await sleep(1000); if (await ev(`!!document.querySelector('[data-habit-id="1"]')`)) break }
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', {
|
||||
type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }],
|
||||
})
|
||||
|
||||
// 用例 1: 按住 habit-1 的把手, 立即垂直拖到 habit-4 中部, 松手 → 应 DROP before/after 4
|
||||
let p = await center('[data-habit-id="1"] .grip')
|
||||
await touch('touchStart', p.x, p.y)
|
||||
await sleep(60)
|
||||
const t = await center('[data-habit-id="4"]')
|
||||
await touch('touchMove', p.x, p.y + 20)
|
||||
await sleep(30)
|
||||
await touch('touchMove', t.x, t.y)
|
||||
await sleep(60)
|
||||
const cls1 = await ev(`document.querySelector('[data-habit-id="4"]').className`)
|
||||
console.log('case1 target class:', cls1)
|
||||
await touch('touchEnd')
|
||||
await sleep(300)
|
||||
|
||||
// 用例 2: 按住 habit-1 把手拖到每周组 (habit-5) → 跨组, 应无落点无 DROP
|
||||
p = await center('[data-habit-id="1"] .grip')
|
||||
await touch('touchStart', p.x, p.y)
|
||||
await sleep(60)
|
||||
const w = await center('[data-habit-id="5"]')
|
||||
await touch('touchMove', w.x, w.y)
|
||||
await sleep(60)
|
||||
const cls2 = await ev(`document.querySelector('[data-habit-id="5"]').className`)
|
||||
console.log('case2 target class (应无 dragover):', cls2)
|
||||
await touch('touchEnd')
|
||||
await sleep(300)
|
||||
|
||||
// 用例 3: 长按卡片主体 500ms 后拖到 habit-3 下半 → DROP after 3
|
||||
p = await center('[data-habit-id="2"]')
|
||||
await touch('touchStart', p.x, p.y)
|
||||
await sleep(520)
|
||||
const t3 = await center('[data-habit-id="3"]')
|
||||
await touch('touchMove', t3.x, t3.y + 20)
|
||||
await sleep(60)
|
||||
await touch('touchEnd')
|
||||
await sleep(300)
|
||||
|
||||
console.log('--- events:')
|
||||
console.log(await ev('window.__ev.join("\\n")'))
|
||||
ws.close()
|
||||
@@ -0,0 +1,27 @@
|
||||
import WebSocket from 'ws'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const targets = await send('Target.getTargets')
|
||||
const t = targets.targetInfos.find((x) => x.url.includes('dragtest'))
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId: t.targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
// 长按 habit-2 主体, 分别在 450ms 后检查激活态, 再拖动
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="2"]').getBoundingClientRect())`))
|
||||
const x = r.left + r.width / 2, y = r.top + r.height / 2
|
||||
await S('Input.dispatchTouchEvent', { type: 'touchStart', touchPoints: [{ x, y, id: 1 }] })
|
||||
await sleep(460)
|
||||
console.log('after 460ms class:', await ev(`document.querySelector('[data-habit-id="2"]').className`))
|
||||
const r3 = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="3"]').getBoundingClientRect())`))
|
||||
await S('Input.dispatchTouchEvent', { type: 'touchMove', touchPoints: [{ x: r3.left + r3.width / 2, y: r3.top + r3.height - 8, id: 1 }] })
|
||||
await sleep(80)
|
||||
console.log('class of 3 after move:', await ev(`document.querySelector('[data-habit-id="3"]').className`))
|
||||
await S('Input.dispatchTouchEvent', { type: 'touchEnd', touchPoints: [] })
|
||||
await sleep(300)
|
||||
console.log('events:', await ev('window.__ev.join(" | ")'))
|
||||
ws.close()
|
||||
@@ -0,0 +1,32 @@
|
||||
import WebSocket from 'ws'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const { targetId } = await send('Target.createTarget', { url: 'about:blank' })
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
await S('Runtime.enable'); await S('Page.enable')
|
||||
await S('Emulation.setDeviceMetricsOverride', { width: 400, height: 800, deviceScaleFactor: 2, mobile: true })
|
||||
await S('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 })
|
||||
await S('Page.navigate', { url: 'http://127.0.0.1:5199/dragtest.html' })
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
for (let i = 0; i < 30; i++) { await sleep(1000); if (await ev(`!!document.querySelector('[data-task-id="101"]')`)) break }
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', { type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }] })
|
||||
|
||||
// 用例1: 任务B(102) 把手拖到任务D(104) 下半 → 期望 REORDER 102 after 104
|
||||
const g = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-task-id="102"] .grip').getBoundingClientRect())`))
|
||||
await touch('touchStart', g.left + g.width / 2, g.top + g.height / 2)
|
||||
await sleep(60)
|
||||
const d = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-task-id="104"]').getBoundingClientRect())`))
|
||||
await touch('touchMove', d.left + d.width / 2, d.top + d.height - 10)
|
||||
await sleep(120)
|
||||
console.log('row104 class:', await ev(`document.querySelector('[data-task-id="104"]').className`))
|
||||
console.log('drag state:', await ev(`JSON.stringify({drag: !!document.querySelector('.task-row.dragging'), ghost: !!document.querySelector('.drag-ghost')})`))
|
||||
await touch('touchEnd')
|
||||
await sleep(200)
|
||||
console.log('events:', await ev('window.__ev.join(" | ")'))
|
||||
ws.close()
|
||||
@@ -0,0 +1,40 @@
|
||||
import WebSocket from 'ws'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const targets = await send('Target.getTargets')
|
||||
const t = targets.targetInfos.find((x) => x.url.includes('dragtest'))
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId: t.targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', { type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }] })
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="2"] .grip').getBoundingClientRect())`))
|
||||
const x = r.left + r.width / 2, y = r.top + r.height / 2
|
||||
await touch('touchStart', x, y)
|
||||
await sleep(60)
|
||||
const t4 = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="4"]').getBoundingClientRect())`))
|
||||
const fx = t4.left + t4.width / 2, fy = t4.top + t4.height / 2
|
||||
await touch('touchMove', fx, fy)
|
||||
await sleep(150)
|
||||
const scan = await ev(`(() => {
|
||||
const out = []
|
||||
document.querySelectorAll('*').forEach((el) => {
|
||||
const cs = getComputedStyle(el)
|
||||
const b = el.getBoundingClientRect()
|
||||
if (b.width === 0 || b.height === 0) return
|
||||
const bg = cs.backgroundColor
|
||||
// 找带蓝色背景的元素
|
||||
if (/rgb\\((22|2[0-9]|1[0-9]), ?1[0-9]{2}/.test(bg) || bg.includes('22, 119, 255') || bg.includes('0, 149, 255')) {
|
||||
out.push({ tag: el.tagName, cls: el.className && String(el.className).slice(0, 40), bg, rect: [Math.round(b.left), Math.round(b.top), Math.round(b.width), Math.round(b.height)], text: (el.textContent || '').slice(0, 12) })
|
||||
}
|
||||
})
|
||||
return JSON.stringify(out)
|
||||
})()`)
|
||||
console.log('blue elements:', scan)
|
||||
console.log('ghost children:', await ev(`(() => { const g = document.querySelector('.drag-ghost'); return g ? g.children.length + ' kids, inner w ' + g.getBoundingClientRect().width : 'none' })()`))
|
||||
await touch('touchEnd')
|
||||
ws.close()
|
||||
@@ -0,0 +1,35 @@
|
||||
import WebSocket from 'ws'
|
||||
import fs from 'node:fs'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const { targetId } = await send('Target.createTarget', { url: 'about:blank' })
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
await S('Runtime.enable'); await S('Page.enable')
|
||||
await S('Emulation.setDeviceMetricsOverride', { width: 400, height: 800, deviceScaleFactor: 2, mobile: true })
|
||||
await S('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 })
|
||||
await S('Page.navigate', { url: 'http://127.0.0.1:5199/dragtest.html' })
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
for (let i = 0; i < 30; i++) { await sleep(1000); if (await ev(`!!document.querySelector('[data-habit-id="1"]')`)) break }
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', { type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }] })
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="2"] .grip').getBoundingClientRect())`))
|
||||
const x = r.left + r.width / 2, y = r.top + r.height / 2
|
||||
await touch('touchStart', x, y)
|
||||
await sleep(60)
|
||||
const t = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="4"]').getBoundingClientRect())`))
|
||||
await touch('touchMove', t.x ?? t.left + t.width / 2, t.top + t.height / 2)
|
||||
await sleep(120)
|
||||
// 拖动中截图
|
||||
const shot = await S('Page.captureScreenshot', { format: 'png' })
|
||||
fs.writeFileSync(process.env.USERPROFILE + '\\AppData\\Local\\Temp\\drag-mid.png', Buffer.from(shot.data, 'base64'))
|
||||
console.log('shot saved; ghost exists:', await ev(`!!document.querySelector('.drag-ghost')`))
|
||||
console.log('ghost html head:', (await ev(`document.querySelector('.drag-ghost') ? document.querySelector('.drag-ghost').outerHTML.slice(0, 200) : 'NONE'`)))
|
||||
await touch('touchEnd')
|
||||
await sleep(200)
|
||||
console.log('events:', await ev('window.__ev.join(" | ")'))
|
||||
ws.close()
|
||||
@@ -0,0 +1,36 @@
|
||||
import WebSocket from 'ws'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const { targetId } = await send('Target.createTarget', { url: 'about:blank' })
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
await S('Runtime.enable'); await S('Page.enable')
|
||||
await S('Emulation.setDeviceMetricsOverride', { width: 400, height: 800, deviceScaleFactor: 2, mobile: true })
|
||||
await S('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 })
|
||||
await S('Page.navigate', { url: 'http://127.0.0.1:5199/dragtest.html' })
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
for (let i = 0; i < 30; i++) { await sleep(1000); if (await ev(`!!document.querySelector('[data-task-id="3"]')`)) break }
|
||||
await sleep(1000) // 等延迟数据加载完
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', { type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }] })
|
||||
|
||||
// 用例: 按住任务3的把手 → 水平拖 +200px (带 +30 纵向漂移, 复现真机抖动) → 松手
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-task-id="3"] .grip').getBoundingClientRect())`))
|
||||
let x = r.left + r.width / 2, y = r.top + r.height / 2
|
||||
console.log('grip rect', JSON.stringify(r))
|
||||
await touch('touchStart', x, y)
|
||||
await sleep(60)
|
||||
for (const [dx, dy] of [[25, 4], [60, 10], [110, 18], [160, 26], [200, 30]]) {
|
||||
await touch('touchMove', x + dx, y + dy)
|
||||
await sleep(50)
|
||||
}
|
||||
console.log('row class after moves:', await ev(`document.querySelector('[data-task-id="3"]').className`))
|
||||
console.log('meta:', await ev(`document.querySelector('[data-task-id="3"] .task-meta').textContent`))
|
||||
await touch('touchEnd')
|
||||
await sleep(300)
|
||||
console.log('events:', await ev('window.__ev.join(" | ")'))
|
||||
ws.close()
|
||||
@@ -0,0 +1,30 @@
|
||||
import WebSocket from 'ws'
|
||||
import fs from 'node:fs'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const targets = await send('Target.getTargets')
|
||||
const t = targets.targetInfos.find((x) => x.url.includes('dragtest'))
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId: t.targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
await S('Page.reload')
|
||||
await sleep(2500)
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', { type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }] })
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="2"] .grip').getBoundingClientRect())`))
|
||||
const x = r.left + r.width / 2, y = r.top + r.height / 2
|
||||
await touch('touchStart', x, y)
|
||||
await sleep(60)
|
||||
const t4 = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="4"]').getBoundingClientRect())`))
|
||||
await touch('touchMove', t4.left + t4.width / 2, t4.top + t4.height / 2)
|
||||
await sleep(150)
|
||||
console.log('ghost bg:', await ev(`getComputedStyle(document.querySelector('.drag-ghost')).backgroundColor`))
|
||||
console.log('ghost rect:', await ev(`JSON.stringify(document.querySelector('.drag-ghost').getBoundingClientRect())`))
|
||||
const shot = await S('Page.captureScreenshot', { format: 'png' })
|
||||
fs.writeFileSync(process.env.USERPROFILE + '\\AppData\\Local\\Temp\\drag-fixed.png', Buffer.from(shot.data, 'base64'))
|
||||
await touch('touchEnd')
|
||||
ws.close()
|
||||
@@ -0,0 +1,8 @@
|
||||
fix(V0.2): 拖动浮层残留蓝色背景 —— 类名与遗留 .drag-ghost 规则冲突
|
||||
|
||||
globals.css 里有一条无人使用的旧 .drag-ghost 规则 (蓝色气泡,
|
||||
max-width 180px, 看板早期方案遗留), 排在新增规则之后同特异性胜出,
|
||||
克隆卡片容器被套上蓝色背景并被压缩到 180px 宽, 即真机上手下面的
|
||||
蓝色 div。删除遗留规则; 新规则显式清零背景/内边距并禁用
|
||||
tap-highlight/user-select (克隆脱离 .mv-view/.task-content 祖先后
|
||||
需自带防选中)。CDP 实测拖动中容器背景已透明、宽度正常。
|
||||
@@ -0,0 +1,4 @@
|
||||
const s = '问题,;、!'
|
||||
console.log(JSON.stringify(s.replace(/[\s\p{P}]+$/u, '')))
|
||||
console.log(JSON.stringify('正常标题'.replace(/[\s\p{P}]+$/u, '')))
|
||||
console.log(JSON.stringify('a-b_c'.replace(/[\s\p{P}]+$/u, '')))
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
Exception during registration:
|
||||
System.ComponentModel.Win32Exception (0x80004005): 系统找不到指定的文件。
|
||||
在 System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
|
||||
在 System.CommandLine.Invocation.Process.StartProcess(String command, String args, String workingDir, Action`1 stdOut, Action`1 stdErr, ValueTuple`2[] environmentVariables)
|
||||
在 System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<RegisterWithDotnetSuggest>b__5_1>d.MoveNext()
|
||||
Reference in New Issue
Block a user