@yorha2b-lab/autodev 5.6.2 → 5.7.0
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/bin/autodev.js +4 -3
- package/config.js +4 -0
- package/package.json +2 -1
- package/src/ai/index.js +6 -6
- package/src/commands/watch.js +8 -18
- package/src/dialogs/DE-DE.js +26 -25
- package/src/dialogs/EN-US.js +5 -2
- package/src/dialogs/ES-ES.js +21 -17
- package/src/dialogs/FR-FR.js +23 -22
- package/src/dialogs/ID-ID.js +23 -23
- package/src/dialogs/IT-IT.js +22 -22
- package/src/dialogs/JA-JP.js +18 -15
- package/src/dialogs/KO-KR.js +23 -23
- package/src/dialogs/PT-BR.js +23 -23
- package/src/dialogs/RU-RU.js +25 -24
- package/src/dialogs/TR-TR.js +24 -24
- package/src/dialogs/ZH-CN.js +5 -2
- package/src/dialogs/ZH-TW.js +9 -9
- package/src/framework/react/generator/index.js +108 -32
- package/src/framework/react/handlebars/partials/hookBlock.hbs +1 -1
- package/src/handlers/page.js +4 -6
- package/src/handlers/part.js +3 -2
- package/src/handlers/{api.js → response.js} +1 -1
- package/src/kernel/queue.js +2 -2
- package/src/labs/index.js +5 -1
- package/src/labs/scout.js +110 -0
- package/src/labs/tower.js +2 -2
- package/src/utils/foundation.js +7 -0
- package/src/utils/generator.js +3 -77
- package/src/utils/ux.js +17 -7
- /package/src/framework/react/prompts/{api.js → response.js} +0 -0
package/src/dialogs/ZH-TW.js
CHANGED
|
@@ -5,18 +5,21 @@ module.exports = {
|
|
|
5
5
|
dataCamouflage: fileName => `正在突破目標防火牆... 執行資料偽裝程序 [${fileName}]`,
|
|
6
6
|
},
|
|
7
7
|
pod042: {
|
|
8
|
+
startScout: '啟動【右鍵一鍵快照】模式...',
|
|
9
|
+
capturedToast: '圖像情報已擷取,開始傳輸...',
|
|
8
10
|
extractingUiMetadata: '正在從神經雲網路提取 UI 中繼資料...',
|
|
9
11
|
simulate: '攔截到實際請求。正在空投標準模擬包: example.json',
|
|
10
|
-
queueEmpty: '佇列已清空,正在向 Bunker 同步 Menu 路由設定...',
|
|
11
|
-
uploadVisualMetadata: '正在上傳視覺中繼資料至司令部,執行語意分析...',
|
|
12
|
-
visualPartCaptured: '偵測到局部視覺樣本。正在執行「碎片構築協議」...',
|
|
13
12
|
partialConstruction: time => `報告:局部構築完成。耗時: ${time}s`,
|
|
13
|
+
visualPartCaptured: '偵測到局部視覺樣本。正在執行「碎片構築協議」...',
|
|
14
14
|
partialRecommendation: '│ 建議:請手動將上述程式碼區塊整合至目標檔案',
|
|
15
|
+
uploadVisualMetadata: '正在上傳視覺中繼資料至司令部,執行語意分析...',
|
|
16
|
+
scoutReady: '【右鍵快照模式就緒】在瀏覽器頁面任意位置點擊【滑鼠右鍵】即可秒截屏',
|
|
15
17
|
constructionAborted: virus => `構築中斷。偵測到邏輯病毒或連線逾時:${virus}`,
|
|
18
|
+
capturedPage: pageName => `擷取頁面【${pageName}】-> 已投遞至 screenShot 目錄`,
|
|
16
19
|
partialConstructionAborted: virus => `[警告] 碎片構築失敗。系統偵測到邏輯干擾:${virus}`,
|
|
17
|
-
visualCaptured: fileName => `視覺感測器捕捉完成: [${fileName}]。正在調配 Bunker 資源進行物理構築...`,
|
|
18
20
|
intercept: fileName => `模組 [${fileName}] 已存在於 Bunker 資料庫中。為避免資料覆蓋,構築協議已跳過`,
|
|
19
|
-
|
|
21
|
+
visualCaptured: fileName => `視覺感測器捕捉完成: [${fileName}]。正在調配 Bunker 資源進行物理構築...`,
|
|
22
|
+
assemblyComplete: (fileName, time) => `構築協議執行完成。模組 [${fileName}] 已完成物理裝配。耗時: ${time}s,開始同步 Menu 路由設定...`,
|
|
20
23
|
},
|
|
21
24
|
pod153: {
|
|
22
25
|
signalLinkFault: virus => `訊號鏈路故障: ${virus}`,
|
|
@@ -55,10 +58,7 @@ module.exports = {
|
|
|
55
58
|
frameworkNotSupported: template => `暫不支援 [${template}] 框架。歡迎提交 Pull Request 貢獻`,
|
|
56
59
|
strategicThreshold: (target, current) => `物理複製總數已超越 ${target} 戰略閾值!目前戰力:${current}`,
|
|
57
60
|
initComplete: cmd => `Bunker 部署完成,為更快速進入戰場,建議手動將 ${cmd} 加入 package.json scripts`,
|
|
58
|
-
isLegendary: (unlockAchievement, current, target) =>
|
|
59
|
-
unlockAchievement
|
|
60
|
-
? '恭喜您指揮官,您的構築協議已成為人類榮光的一部分'
|
|
61
|
-
: `目前構築總數:${current}。距離 ${target} 徽章還剩 ${target - current} 次`,
|
|
61
|
+
isLegendary: (unlockAchievement, current, target) => unlockAchievement ? '恭喜您指揮官,您的構築協議已成為人類榮光的一部分' : `目前構築總數:${current}。距離 ${target} 徽章還剩 ${target - current} 次`,
|
|
62
62
|
},
|
|
63
63
|
operator6O: {
|
|
64
64
|
call2B: '呼叫 2B,Bunker 全頻道聯動監控已準備完成!',
|
|
@@ -1,10 +1,116 @@
|
|
|
1
1
|
module.exports = ({ utils, engine }) => {
|
|
2
2
|
|
|
3
3
|
const { indexTpl, resourceTpl } = engine
|
|
4
|
+
const { wrapCode, cleanCode, contextStringify } = utils.generator
|
|
4
5
|
const { needMock, responseSuccess } = utils.foundation.getConfig()
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
const formatFormItemAndColumns = ({ pageConfig }) => {
|
|
8
|
+
|
|
9
|
+
const codePresets = {
|
|
10
|
+
money: 'text => moneyRender(text)',
|
|
11
|
+
date: 'text => timeRender({time: text})',
|
|
12
|
+
index: '(_, record, index) => index + 1',
|
|
13
|
+
tag: `text => <Tag color='magenta'>{text}</Tag>`,
|
|
14
|
+
badge: `text => <Badge status='success' text={text} />`,
|
|
15
|
+
enum: dataIndex => `text => ${dataIndex}Options.find(item => item.value === text)?.label ?? text`,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const columns = pageConfig.table?.columns ?? pageConfig?.columns ?? []
|
|
19
|
+
|
|
20
|
+
const tableDicts = columns?.filter(item => item.type === 'enum')?.map(item => `${item.dataIndex}Options`) ?? []
|
|
21
|
+
const formDicts = pageConfig.formItems?.filter(item => item.type === 'select')?.map(item => `${item.name}Options`) ?? []
|
|
22
|
+
const dictBlocks = Array.from(new Set([...formDicts, ...tableDicts]))
|
|
23
|
+
|
|
24
|
+
const formItems = pageConfig.formItems?.map(item => {
|
|
25
|
+
if (item.type === 'text') {
|
|
26
|
+
delete item.type
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
...item,
|
|
30
|
+
...(item.type === 'select' ? { options: wrapCode(`${item.name}Options`) } : {})
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const processedColumns = columns?.map(col => {
|
|
35
|
+
if (col.type === 'text') {
|
|
36
|
+
delete col.type
|
|
37
|
+
}
|
|
38
|
+
if (['image'].includes(col.type)) {
|
|
39
|
+
return { ...col, renderAction: true }
|
|
40
|
+
}
|
|
41
|
+
if (col.type && codePresets[col.type]) {
|
|
42
|
+
const renderCode = typeof codePresets[col.type] === 'function' ? codePresets[col.type](col.dataIndex) : codePresets[col.type]
|
|
43
|
+
return { ...col, render: wrapCode(renderCode) }
|
|
44
|
+
}
|
|
45
|
+
return col
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return { formItems, dictBlocks, processedColumns }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const generateSmartImports = ({ module, hasTabs, bodyCode, hasFormItems }) => {
|
|
52
|
+
|
|
53
|
+
const hooksLib = ['useTableQuery']
|
|
54
|
+
const utilsLib = ['timeRender', 'moneyRender']
|
|
55
|
+
const reactLib = ['useState', 'useEffect', 'useRef', 'useMemo']
|
|
56
|
+
const componentsLib = ['MyTable', 'MyImage', 'MyModalForm', 'MySearchForm']
|
|
57
|
+
const antdLib = ['Tag', 'Card', 'Badge', 'Space', 'Modal', 'Alert', 'Image', 'Table', 'Input', 'Select', 'Button']
|
|
58
|
+
|
|
59
|
+
const usedAntd = antdLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
60
|
+
const usedUtils = utilsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
61
|
+
const usedHooks = hooksLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
62
|
+
const usedReact = reactLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
63
|
+
const usedComps = componentsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
64
|
+
|
|
65
|
+
const imports = [
|
|
66
|
+
usedReact.length && `import { ${usedReact.join(', ')} } from 'react'`,
|
|
67
|
+
usedAntd.length && `import { ${hasFormItems && module === 'index' ? 'Form, ' : ''}${usedAntd.join(', ')} } from 'antd'`,
|
|
68
|
+
...usedHooks.map(hook => `import { ${hook} } from '../../hooks/${hook}'`),
|
|
69
|
+
...usedComps.map(comp => `import { ${comp} } from '../../components/${comp}'`),
|
|
70
|
+
...(module === 'index' ? [
|
|
71
|
+
`import { request } from '../../utils/request'`,
|
|
72
|
+
`import { formatQuery } from '../../utils/utils'`,
|
|
73
|
+
`import { ${hasTabs ? 'tabs, ' : ''}${hasFormItems ? 'formItems, ' : ''}modalItems, tableColumns} from './resource'`
|
|
74
|
+
] : [
|
|
75
|
+
usedUtils.length && `import { ${usedUtils.join(', ')} } from '../../utils/utils'`
|
|
76
|
+
]),
|
|
77
|
+
].sort((a, b) => a.length - b.length)
|
|
78
|
+
|
|
79
|
+
return imports.filter(Boolean).join('\n')
|
|
80
|
+
}
|
|
6
81
|
|
|
7
82
|
return {
|
|
83
|
+
formatFormItemAndColumns,
|
|
84
|
+
resource: ({ pageConfig }) => {
|
|
85
|
+
|
|
86
|
+
const { formItems, processedColumns, dictBlocks } = formatFormItemAndColumns({ pageConfig })
|
|
87
|
+
|
|
88
|
+
const hasTabs = pageConfig.tabs?.length > 0
|
|
89
|
+
const hasFormItems = pageConfig.formItems?.length > 0
|
|
90
|
+
const tabKeys = pageConfig.tabs?.map(tab => tab.key).sort((a, b) => a.length - b.length)
|
|
91
|
+
const optionDict = pageConfig.optionList?.reduce((acc, item) => ({ ...acc, [item.name]: item.options }), {})
|
|
92
|
+
|
|
93
|
+
const columnsData = processedColumns.filter(item => !['操作', 'operation'].includes(item.title?.toLowerCase()))?.map(item => {
|
|
94
|
+
delete item.type
|
|
95
|
+
return item
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const viewData = {
|
|
99
|
+
hasTabs,
|
|
100
|
+
columnsData,
|
|
101
|
+
hasFormItems,
|
|
102
|
+
tabs: pageConfig.tabs,
|
|
103
|
+
dictBlocks: dictBlocks.map(item => ({ name: item, data: optionDict[item] ?? [] })),
|
|
104
|
+
tabColumns: contextStringify({ context: Object.fromEntries(tabKeys.map(tab => [tab, wrapCode('commonColumns')])), maxLength: 100 }),
|
|
105
|
+
formItemsData: !hasTabs ?
|
|
106
|
+
`export const formItems = ${contextStringify({ context: formItems, maxLength: 120 })}` :
|
|
107
|
+
`const searchItems = ${contextStringify({ context: formItems, maxLength: 120 })}\n\nexport const formItems = ${contextStringify({ context: Object.fromEntries(tabKeys.map(tab => [tab, wrapCode('searchItems')])), maxLength: 100 })}`,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const bodyCode = resourceTpl(viewData)
|
|
111
|
+
const importsStr = generateSmartImports({ module: 'resource', hasTabs, bodyCode, hasFormItems })
|
|
112
|
+
return cleanCode(`${importsStr}\n\n${bodyCode}`)
|
|
113
|
+
},
|
|
8
114
|
index: ({ fileName, pageConfig }) => {
|
|
9
115
|
const hasTabs = pageConfig.tabs?.length > 0
|
|
10
116
|
const hasPagination = pageConfig.table.pagination
|
|
@@ -21,7 +127,7 @@ module.exports = ({ utils, engine }) => {
|
|
|
21
127
|
columnsValue = `${columnsValue}.concat(operate)`
|
|
22
128
|
}
|
|
23
129
|
|
|
24
|
-
const imgAction = Object.fromEntries(pageConfig.table?.columns?.filter(item => item.type === 'image')?.map(item => [item.dataIndex, `
|
|
130
|
+
const imgAction = Object.fromEntries(pageConfig.table?.columns?.filter(item => item.type === 'image')?.map(item => [item.dataIndex, wrapCode(`(_, record) => <a onClick={() => showImg(record, '${item.dataIndex}')}>查看图片</a>`)]))
|
|
25
131
|
const renderAction = `{${contextStringify({ context: imgAction, maxLength: 120 })}}`.replace(/\n/g, '\n ')
|
|
26
132
|
|
|
27
133
|
const viewData = {
|
|
@@ -50,35 +156,5 @@ module.exports = ({ utils, engine }) => {
|
|
|
50
156
|
const importsStr = generateSmartImports({ module: 'index', hasTabs, bodyCode, hasFormItems })
|
|
51
157
|
return cleanCode(`${importsStr}\n\n${bodyCode}`)
|
|
52
158
|
},
|
|
53
|
-
resource: ({ pageConfig }) => {
|
|
54
|
-
|
|
55
|
-
const { formItems, processedColumns, dictBlocks } = formatFormItemAndColumns({ pageConfig })
|
|
56
|
-
|
|
57
|
-
const hasTabs = pageConfig.tabs?.length > 0
|
|
58
|
-
const hasFormItems = pageConfig.formItems?.length > 0
|
|
59
|
-
const tabKeys = pageConfig.tabs.map(tab => tab.key).sort((a, b) => a.length - b.length)
|
|
60
|
-
const optionDict = pageConfig.optionList?.reduce((acc, item) => ({ ...acc, [item.name]: item.options }), {})
|
|
61
|
-
|
|
62
|
-
const columnsData = processedColumns.map(item => {
|
|
63
|
-
delete item.type
|
|
64
|
-
return item
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
const viewData = {
|
|
68
|
-
hasTabs,
|
|
69
|
-
columnsData,
|
|
70
|
-
hasFormItems,
|
|
71
|
-
tabs: pageConfig.tabs,
|
|
72
|
-
dictBlocks: dictBlocks.map(item => ({ name: item, data: optionDict[item] ?? [] })),
|
|
73
|
-
tabColumns: contextStringify({ context: Object.fromEntries(tabKeys.map(tab => [tab, '_CODE_commonColumns_CODE_'])), maxLength: 100 }),
|
|
74
|
-
formItemsData: !hasTabs ?
|
|
75
|
-
`export const formItems = ${contextStringify({ context: formItems, maxLength: 120 })}` :
|
|
76
|
-
`const searchItems = ${contextStringify({ context: formItems, maxLength: 120 })}\n\nexport const formItems = ${contextStringify({ context: Object.fromEntries(tabKeys.map(tab => [tab, '_CODE_searchItems_CODE_'])), maxLength: 100 })}`,
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const bodyCode = resourceTpl(viewData)
|
|
80
|
-
const importsStr = generateSmartImports({ module: 'resource', hasTabs, bodyCode, hasFormItems })
|
|
81
|
-
return cleanCode(`${importsStr}\n\n${bodyCode}`)
|
|
82
|
-
}
|
|
83
159
|
}
|
|
84
160
|
}
|
|
@@ -9,7 +9,7 @@ const { total, columns, loading, dataSource, search, setSearch, setDataSource, r
|
|
|
9
9
|
cols:tableColumns,
|
|
10
10
|
formatResponse: response => {
|
|
11
11
|
return {
|
|
12
|
-
data: response?.data ?? [],
|
|
12
|
+
data: response?.data?.datas ?? [],
|
|
13
13
|
total: response?.data?.total ?? 0,
|
|
14
14
|
columns: undefined,//可选,解析接口动态列;优先级高于cols
|
|
15
15
|
}
|
package/src/handlers/page.js
CHANGED
|
@@ -12,11 +12,10 @@ module.exports = {
|
|
|
12
12
|
const { index, resource } = generator
|
|
13
13
|
const { generateMock, recognizePage } = llm
|
|
14
14
|
const { contextStringify } = utils.generator
|
|
15
|
-
const { useDemo, pagesDir, needMock } = utils.foundation.getConfig()
|
|
15
|
+
const { useDemo, pagesDir, utilsDir, needMock } = utils.foundation.getConfig()
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
const startTime = Date.now()
|
|
19
|
-
const menus = utils.foundation.getExistingMenus(pagesDir)
|
|
20
19
|
const fileName = path.basename(filePath, path.extname(filePath))
|
|
21
20
|
const mockDir = path.join(process.cwd(), 'mock')
|
|
22
21
|
const targetDir = path.join(process.cwd(), pagesDir, fileName)
|
|
@@ -44,10 +43,6 @@ module.exports = {
|
|
|
44
43
|
fs.writeFileSync(path.join(targetDir, 'resource.js'), resource({ pageConfig }))
|
|
45
44
|
fs.writeFileSync(path.join(targetDir, 'index.js'), index({ fileName, pageConfig }))
|
|
46
45
|
|
|
47
|
-
if (!menus.find(m => m.key === fileName)) {
|
|
48
|
-
menus.push({ label: fileName, key: fileName })
|
|
49
|
-
}
|
|
50
|
-
|
|
51
46
|
if (needMock) {
|
|
52
47
|
nineS.update(spinner, dialog.nineS.dataCamouflage(fileName))
|
|
53
48
|
const rawContent = await generateMock({ columns: pageConfig.table.columns, fileName })
|
|
@@ -60,6 +55,9 @@ module.exports = {
|
|
|
60
55
|
|
|
61
56
|
if (fs.existsSync(filePath)) {
|
|
62
57
|
fs.unlinkSync(filePath)
|
|
58
|
+
const cwdUtilsDir = path.join(process.cwd(), utilsDir)
|
|
59
|
+
if (!fs.existsSync(cwdUtilsDir)) fs.mkdirSync(cwdUtilsDir, { recursive: true })
|
|
60
|
+
fs.writeFileSync(path.join(cwdUtilsDir, 'menus.js'), `export const menus = ${contextStringify({ context: utils.foundation.getExistingMenus(pagesDir), maxLength: 50 })}`)
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
} catch (error) {
|
package/src/handlers/part.js
CHANGED
|
@@ -5,12 +5,13 @@ module.exports = {
|
|
|
5
5
|
async handle(filePath) {
|
|
6
6
|
|
|
7
7
|
const chalk = require('chalk')
|
|
8
|
-
const { llm, utils, yorha, dialog, prompts } = require('../bootstrap').get()
|
|
8
|
+
const { llm, utils, yorha, dialog, prompts, generator } = require('../bootstrap').get()
|
|
9
9
|
|
|
10
10
|
const { pod042 } = yorha
|
|
11
11
|
const { recognizePage } = llm
|
|
12
12
|
const { part: prompt } = prompts
|
|
13
|
-
const {
|
|
13
|
+
const { formatFormItemAndColumns } = generator
|
|
14
|
+
const { cleanCode, contextStringify } = utils.generator
|
|
14
15
|
|
|
15
16
|
const startTime = Date.now()
|
|
16
17
|
|
|
@@ -80,7 +80,7 @@ module.exports = {
|
|
|
80
80
|
console.log(chalk.magenta(`└────────────────────────────────────────────────────┘`))
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
if (enumParams) {
|
|
83
|
+
if (enumParams !== '{}') {
|
|
84
84
|
console.log(chalk.magenta(`\n┌──────── [ YoRHa Physical Assembly: Enum Genomes ] ────────┐`))
|
|
85
85
|
console.log(chalk.gray(dialog.bunker.copyEnum))
|
|
86
86
|
console.log(chalk.white(cleanCode(enumParams)))
|
package/src/kernel/queue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module.exports = (concurrency = 1) => {
|
|
1
|
+
module.exports = (concurrency = 1, { yorha, dialog }) => {
|
|
2
2
|
|
|
3
3
|
let running = 0
|
|
4
4
|
let queue = []
|
|
@@ -14,7 +14,7 @@ module.exports = (concurrency = 1) => {
|
|
|
14
14
|
try {
|
|
15
15
|
await task()
|
|
16
16
|
} catch (err) {
|
|
17
|
-
|
|
17
|
+
yorha.pod153.report(dialog.pod153.signalLinkFault(err.message), 'red')
|
|
18
18
|
} finally {
|
|
19
19
|
running--
|
|
20
20
|
next()
|
package/src/labs/index.js
CHANGED
|
@@ -2,7 +2,7 @@ module.exports = accessPoint => {
|
|
|
2
2
|
|
|
3
3
|
const labs = {}
|
|
4
4
|
|
|
5
|
-
const { apiDoc, needMock, proxyTarget, enableAutoAlignment } = accessPoint.utils.foundation.getConfig()
|
|
5
|
+
const { apiDoc, remains, needMock, proxyTarget, enableAutoAlignment } = accessPoint.utils.foundation.getConfig()
|
|
6
6
|
|
|
7
7
|
if (!needMock && proxyTarget && enableAutoAlignment) {
|
|
8
8
|
labs.tower = require('../labs/tower')(accessPoint)
|
|
@@ -12,5 +12,9 @@ module.exports = accessPoint => {
|
|
|
12
12
|
labs.council = require('../labs/council')(accessPoint)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
if (remains) {
|
|
16
|
+
require('../labs/scout')(accessPoint).archeology(remains)
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
return labs
|
|
16
20
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
let globalBrowser = null
|
|
5
|
+
|
|
6
|
+
module.exports = ({ yorha, dialog }) => {
|
|
7
|
+
|
|
8
|
+
const { pod042 } = yorha
|
|
9
|
+
|
|
10
|
+
const isHttpURL = str => {
|
|
11
|
+
try {
|
|
12
|
+
const url = new URL(str)
|
|
13
|
+
return ['http:', 'https:'].includes(url.protocol)
|
|
14
|
+
} catch {
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const derivePageNameFromUrl = rawUrl => {
|
|
20
|
+
const parsed = new URL(rawUrl)
|
|
21
|
+
let routePath = parsed.hash ? parsed.hash.replace(/^#/, '') : parsed.pathname
|
|
22
|
+
routePath = routePath.split('?')[0].replace(/^\/+|\/+$/g, '')
|
|
23
|
+
if (!routePath) return 'Home'
|
|
24
|
+
const parts = routePath.split(/[/\-_]/).filter(Boolean)
|
|
25
|
+
return parts.at(-1) ?? 'capturedPage'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
archeology: async targetUrl => {
|
|
30
|
+
|
|
31
|
+
if (!isHttpURL(targetUrl)) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
pod042.report(dialog.pod042.startScout, 'green')
|
|
36
|
+
|
|
37
|
+
const puppeteer = require('puppeteer')
|
|
38
|
+
const screenshotDir = path.join(process.cwd(), 'bunker', 'screenShot')
|
|
39
|
+
const sessionDir = path.join(process.cwd(), 'bunker', '.chrome_session')
|
|
40
|
+
|
|
41
|
+
let isBrowserAlive = false
|
|
42
|
+
if (globalBrowser) {
|
|
43
|
+
try {
|
|
44
|
+
await globalBrowser.pages()
|
|
45
|
+
isBrowserAlive = true
|
|
46
|
+
} catch (e) {
|
|
47
|
+
globalBrowser = null
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!isBrowserAlive) {
|
|
52
|
+
|
|
53
|
+
['SingletonLock', 'SingletonSocket', 'SingletonCookie'].forEach(file => {
|
|
54
|
+
try { fs.unlinkSync(path.join(sessionDir, file)) } catch (e) { }
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
globalBrowser = await puppeteer.launch({
|
|
58
|
+
headless: false,
|
|
59
|
+
handleSIGINT: false,
|
|
60
|
+
defaultViewport: null,
|
|
61
|
+
userDataDir: sessionDir,
|
|
62
|
+
args: ['--start-maximized']
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const pages = await globalBrowser.pages()
|
|
67
|
+
const page = pages.length > 0 ? pages[0] : await globalBrowser.newPage()
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
await page.exposeFunction('nodeTakeScreenshot', async () => {
|
|
71
|
+
const currentUrl = await page.url()
|
|
72
|
+
const pageName = derivePageNameFromUrl(currentUrl)
|
|
73
|
+
const savePath = path.join(screenshotDir, `${pageName}.png`)
|
|
74
|
+
await page.screenshot({ path: savePath, fullPage: true })
|
|
75
|
+
pod042.report(dialog.pod042.capturedPage(pageName), 'green')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
await page.evaluateOnNewDocument(() => {
|
|
79
|
+
if (window.__hasBunkerListener__) return
|
|
80
|
+
window.__hasBunkerListener__ = true
|
|
81
|
+
window.addEventListener('contextmenu', (e) => {
|
|
82
|
+
e.preventDefault()
|
|
83
|
+
if (window.nodeTakeScreenshot) {
|
|
84
|
+
window.nodeTakeScreenshot()
|
|
85
|
+
}
|
|
86
|
+
const toast = document.createElement('div')
|
|
87
|
+
toast.innerText = dialog.pod042.capturedToast
|
|
88
|
+
toast.style.cssText = `
|
|
89
|
+
position: fixed; top: 20px; right: 20px; z-index: 999999;
|
|
90
|
+
background: rgba(0, 0, 0, 0.85); color: #00ffcc;
|
|
91
|
+
border: 1px solid #00ffcc; padding: 12px 24px;
|
|
92
|
+
font-size: 15px; font-family: monospace; border-radius: 4px;
|
|
93
|
+
box-shadow: 0 0 15px rgba(0, 255, 204, 0.5);
|
|
94
|
+
pointer-events: none; transition: opacity 0.4s ease;
|
|
95
|
+
`
|
|
96
|
+
document.body.appendChild(toast)
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
toast.style.opacity = '0'
|
|
99
|
+
setTimeout(() => toast.remove(), 400)
|
|
100
|
+
}, 1200)
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
} catch (error) { }
|
|
104
|
+
|
|
105
|
+
await page.goto(targetUrl)
|
|
106
|
+
pod042.report(dialog.pod042.scoutReady, 'green')
|
|
107
|
+
return globalBrowser
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/labs/tower.js
CHANGED
|
@@ -54,8 +54,8 @@ module.exports = ({ utils, yorha, dialog, handlers }) => {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
pod153.report(dialog.pod153.capturedRuntimeSignal(fileName))
|
|
57
|
-
// 💡 直接调用
|
|
58
|
-
await handlers.
|
|
57
|
+
// 💡 直接调用 response-handler 物理更新 resource.js
|
|
58
|
+
await handlers.response.handle(null, { fileName, data: json })
|
|
59
59
|
hackedRegistry.set(fileName, fingerprint)
|
|
60
60
|
} catch (e) {
|
|
61
61
|
// 非 JSON 信号,保持静默
|
package/src/utils/foundation.js
CHANGED
|
@@ -12,6 +12,12 @@ const getConfig = () => {
|
|
|
12
12
|
return config
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
const findRoute = (routes, filePath) => {
|
|
16
|
+
const relative = path.relative(path.resolve('bunker'), path.resolve(filePath))
|
|
17
|
+
const root = relative.split(path.sep)[0]
|
|
18
|
+
return routes.find(route => root === route.watch)
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
const getExistingMenus = (dir = 'src/pages') => {
|
|
16
22
|
const pagesDir = path.join(process.cwd(), dir)
|
|
17
23
|
if (!fs.existsSync(pagesDir)) return []
|
|
@@ -36,6 +42,7 @@ const copyTemplateDir = (template, templateSubDir, targetSubDir) => {
|
|
|
36
42
|
|
|
37
43
|
module.exports = {
|
|
38
44
|
getConfig,
|
|
45
|
+
findRoute,
|
|
39
46
|
copyTemplateDir,
|
|
40
47
|
getExistingMenus,
|
|
41
48
|
}
|
package/src/utils/generator.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const stringify = require('json-stringify-pretty-compact')
|
|
2
2
|
|
|
3
|
+
const wrapCode = code => `_CODE_${code}_CODE_`
|
|
4
|
+
|
|
3
5
|
const cleanCode = str => str
|
|
4
6
|
.replace(/```[a-z]*\n?/gi, '')// 物理超度 Markdown 糖衣(解决```json 报错)
|
|
5
7
|
.replace(/```/g, '')
|
|
@@ -13,86 +15,10 @@ const cleanCode = str => str
|
|
|
13
15
|
.replace(/^\s+/, '') // 去掉文件头部的空行
|
|
14
16
|
.trim() + '\n'
|
|
15
17
|
|
|
16
|
-
const formatFormItemAndColumns = ({ pageConfig }) => {
|
|
17
|
-
|
|
18
|
-
const codePresets = {
|
|
19
|
-
money: 'text => moneyRender(text)',
|
|
20
|
-
date: 'text => timeRender({time: text})',
|
|
21
|
-
index: '(_, record, index) => index + 1',
|
|
22
|
-
tag: `text => <Tag color='magenta'>{text}</Tag>`,
|
|
23
|
-
badge: `text => <Badge status='success' text={text} />`,
|
|
24
|
-
enum: dataIndex => `text => ${dataIndex}Options.find(item => item.value === text)?.label ?? text`,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const columns = pageConfig.table?.columns ?? pageConfig?.columns ?? []
|
|
28
|
-
|
|
29
|
-
const tableDicts = columns?.filter(item => item.type === 'enum')?.map(item => `${item.dataIndex}Options`) ?? []
|
|
30
|
-
const formDicts = pageConfig.formItems?.filter(item => item.type === 'select')?.map(item => `${item.name}Options`) ?? []
|
|
31
|
-
const dictBlocks = Array.from(new Set([...formDicts, ...tableDicts]))
|
|
32
|
-
|
|
33
|
-
const formItems = pageConfig.formItems?.map(item => {
|
|
34
|
-
if (item.type === 'text') {
|
|
35
|
-
delete item.type
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
...item,
|
|
39
|
-
...(item.type === 'select' ? { options: `_CODE_${item.name}Options_CODE_` } : {})
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
const processedColumns = columns?.map(col => {
|
|
44
|
-
if (col.type === 'text') {
|
|
45
|
-
delete col.type
|
|
46
|
-
}
|
|
47
|
-
if (['image'].includes(col.type)) {
|
|
48
|
-
return { ...col, renderAction: true }
|
|
49
|
-
}
|
|
50
|
-
if (col.type && codePresets[col.type]) {
|
|
51
|
-
const renderCode = typeof codePresets[col.type] === 'function' ? codePresets[col.type](col.dataIndex) : codePresets[col.type]
|
|
52
|
-
return { ...col, render: `_CODE_${renderCode}_CODE_` }
|
|
53
|
-
}
|
|
54
|
-
return col
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
return { formItems, dictBlocks, processedColumns }
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const generateSmartImports = ({ module, hasTabs, bodyCode, hasFormItems }) => {
|
|
61
|
-
|
|
62
|
-
const hooksLib = ['useTableQuery']
|
|
63
|
-
const utilsLib = ['timeRender', 'moneyRender']
|
|
64
|
-
const reactLib = ['useState', 'useEffect', 'useRef', 'useMemo']
|
|
65
|
-
const componentsLib = ['MyTable', 'MyImage', 'MyModalForm', 'MySearchForm']
|
|
66
|
-
const antdLib = ['Tag', 'Card', 'Badge', 'Space', 'Modal', 'Alert', 'Image', 'Table', 'Input', 'Select', 'Button']
|
|
67
|
-
|
|
68
|
-
const usedAntd = antdLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
69
|
-
const usedUtils = utilsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
70
|
-
const usedHooks = hooksLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
71
|
-
const usedReact = reactLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
72
|
-
const usedComps = componentsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
73
|
-
|
|
74
|
-
const imports = [
|
|
75
|
-
usedReact.length && `import { ${usedReact.join(', ')} } from 'react'`,
|
|
76
|
-
usedAntd.length && `import { ${hasFormItems && module === 'index' ? 'Form, ' : ''}${usedAntd.join(', ')} } from 'antd'`,
|
|
77
|
-
...usedHooks.map(hook => `import { ${hook} } from '../../hooks/${hook}'`),
|
|
78
|
-
...usedComps.map(comp => `import { ${comp} } from '../../components/${comp}'`),
|
|
79
|
-
...(module === 'index' ? [
|
|
80
|
-
`import { request } from '../../utils/request'`,
|
|
81
|
-
`import { formatQuery } from '../../utils/utils'`,
|
|
82
|
-
`import { ${hasTabs ? 'tabs, ' : ''}${hasFormItems ? 'formItems, ' : ''}modalItems, tableColumns} from './resource'`
|
|
83
|
-
] : [
|
|
84
|
-
usedUtils.length && `import { ${usedUtils.join(', ')} } from '../../utils/utils'`
|
|
85
|
-
]),
|
|
86
|
-
].sort((a, b) => a.length - b.length)
|
|
87
|
-
|
|
88
|
-
return imports.filter(Boolean).join('\n')
|
|
89
|
-
}
|
|
90
|
-
|
|
91
18
|
const contextStringify = ({ context, indent = 4, maxLength = 200 }) => stringify.default(context, { indent, maxLength })
|
|
92
19
|
|
|
93
20
|
module.exports = {
|
|
21
|
+
wrapCode,
|
|
94
22
|
cleanCode,
|
|
95
23
|
contextStringify,
|
|
96
|
-
generateSmartImports,
|
|
97
|
-
formatFormItemAndColumns,
|
|
98
24
|
}
|
package/src/utils/ux.js
CHANGED
|
@@ -38,6 +38,13 @@ const yorha = () => {
|
|
|
38
38
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
const matchDialog = (locale, dialogs) => {
|
|
42
|
+
if (dialogs[locale]) return dialogs[locale]
|
|
43
|
+
const lang = locale.split('-')[0] // 取 ZH-HANS-CN → ZH
|
|
44
|
+
const matched = Object.keys(dialogs).find(key => key.startsWith(lang + '-'))
|
|
45
|
+
return matched ? dialogs[matched] : null
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
const bootSequence = async (version, local) => {
|
|
42
49
|
|
|
43
50
|
const lines = [
|
|
@@ -58,18 +65,20 @@ const bootSequence = async (version, local) => {
|
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
|
|
61
|
-
const matrixEffect = async (duration = 1500, dialog) => {
|
|
68
|
+
const matrixEffect = async (duration = 1500, dialog, result) => {
|
|
62
69
|
|
|
63
70
|
let currentTotal = 0
|
|
64
71
|
const MIRROR_URL = 'https://cdn.jsdelivr.net/gh/yorha2b-lab/auto-crud-copilot@github-repo-stats/bunker-stats.json'
|
|
65
72
|
|
|
66
73
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
if (result.utils.foundation.getConfig().fetchClone) {
|
|
75
|
+
const controller = new AbortController()
|
|
76
|
+
const timeoutId = setTimeout(() => controller.abort(), 800)
|
|
77
|
+
const response = await fetch(MIRROR_URL, { signal: controller.signal })
|
|
78
|
+
const stats = await response.json()
|
|
79
|
+
currentTotal = stats.total_clones || 0
|
|
80
|
+
clearTimeout(timeoutId)
|
|
81
|
+
}
|
|
73
82
|
} catch (e) {
|
|
74
83
|
currentTotal = 0
|
|
75
84
|
}
|
|
@@ -130,6 +139,7 @@ const matrixEffect = async (duration = 1500, dialog) => {
|
|
|
130
139
|
|
|
131
140
|
module.exports = {
|
|
132
141
|
yorha,
|
|
142
|
+
matchDialog,
|
|
133
143
|
bootSequence,
|
|
134
144
|
matrixEffect
|
|
135
145
|
}
|
|
File without changes
|