Skip to content

Add plugin ztool_developer_box v1.0.0#277

Draft
sjszues616 wants to merge 1 commit into
ZToolsCenter:mainfrom
sjszues616:plugin/ztooldeveloperbox
Draft

Add plugin ztool_developer_box v1.0.0#277
sjszues616 wants to merge 1 commit into
ZToolsCenter:mainfrom
sjszues616:plugin/ztooldeveloperbox

Conversation

@sjszues616

Copy link
Copy Markdown

插件信息

  • 名称: ztool_developer_box
  • 插件ID: ztooldeveloperbox
  • 版本: 1.0.0
  • 描述: 开发工具箱
  • 作者: shikeke
  • 类型: 新增

本次变更

  • init

截图 / 演示

自检清单

  • plugin.json 的 name / title / version / description / author 字段均已检查
  • 已移除调试日志、未使用文件、敏感信息(.env、token、密钥等)
  • 本次 PR 的 diff 仅涉及 plugins/ztooldeveloperbox/ 目录
  • 已在本地 ZTools 客户端实际加载并测试过此插件,主要功能正常
  • 同意以仓库声明的开源协议发布此插件

此 PR 由 ztools-plugin-cli 自动管理:每次 ztools publish 在分支上追加一个 commit,PR 链接保持不变。

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new ZTools developer toolbox plugin built with Vue 3, Vite, and TypeScript, featuring tools for random number and string generation, XML formatting/compression, and file operations. The code review highlights several key issues: an exclusive upper bound bug in the random integer generator, a performance bottleneck in random string generation due to excessive crypto.getRandomValues calls, a bug in the XML formatter that prevents using a zero-space indent, and a silent failure risk in the image-writing preload service when handling invalid Base64 formats.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +41 to +50
const output: string[] = []
for (let i = 0; i < countVal; i++) {
const raw = minVal + Math.random() * (maxVal - minVal)
if (isInteger.value) {
output.push(String(Math.floor(raw)))
} else {
const places = Math.max(0, Math.min(10, Number(decimalPlaces.value) || 0))
output.push(raw.toFixed(places))
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

在生成随机整数时,使用 Math.floor(minVal + Math.random() * (maxVal - minVal)) 会导致最大值 maxVal 永远无法被取到(上界是开区间)。例如,当最小值为 1,最大值为 2 时,生成的随机数永远只能是 1。为了使随机整数的生成范围包含最大值(闭区间),建议修正生成公式。

  const output: string[] = []
  for (let i = 0; i < countVal; i++) {
    if (isInteger.value) {
      const val = Math.floor(Math.random() * (maxVal - minVal + 1)) + minVal
      output.push(String(val))
    } else {
      const raw = minVal + Math.random() * (maxVal - minVal)
      const places = Math.max(0, Math.min(10, Number(decimalPlaces.value) || 0))
      output.push(raw.toFixed(places))
    }
  }

Comment on lines +36 to +82
const pickRandomChar = (pool: string) => {
const randomValues = new Uint32Array(1)
crypto.getRandomValues(randomValues)
return pool[randomValues[0] % pool.length]
}

const generateOne = (pool: string, len: number) => {
let result = ''
for (let i = 0; i < len; i++) {
result += pickRandomChar(pool)
}
return result
}

const generate = () => {
error.value = ''
const len = Number(length.value)
const countVal = Number(count.value)
const pool = charsetPool.value

if (!useDigit.value && !useUpper.value && !useLower.value && !useSpecial.value) {
error.value = '请至少选择一种字符类型'
results.value = []
return
}
if (Number.isNaN(len) || Number.isNaN(countVal)) {
error.value = '请输入有效的数字'
results.value = []
return
}
if (len < 1 || len > 1024 || !Number.isInteger(len)) {
error.value = '字符串长度须为 1 ~ 1024 的整数'
results.value = []
return
}
if (countVal < 1 || countVal > 1000 || !Number.isInteger(countVal)) {
error.value = '生成数量须为 1 ~ 1000 的整数'
results.value = []
return
}

const output: string[] = []
for (let i = 0; i < countVal; i++) {
output.push(generateOne(pool, len))
}
results.value = output
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

在循环中高频调用 crypto.getRandomValues 会带来极大的性能开销。如果用户尝试生成 1000 个长度为 1024 的字符串,将会调用该 API 超过 100 万次,导致界面严重卡顿。建议在生成前一次性分配所需大小的 Uint32Array,通过单次 API 调用获取所有随机值,从而大幅提升性能。

const generate = () => {
  error.value = ''
  const len = Number(length.value)
  const countVal = Number(count.value)
  const pool = charsetPool.value

  if (!useDigit.value && !useUpper.value && !useLower.value && !useSpecial.value) {
    error.value = '请至少选择一种字符类型'
    results.value = []
    return
  }
  if (Number.isNaN(len) || Number.isNaN(countVal)) {
    error.value = '请输入有效的数字'
    results.value = []
    return
  }
  if (len < 1 || len > 1024 || !Number.isInteger(len)) {
    error.value = '字符串长度须为 1 ~ 1024 的整数'
    results.value = []
    return
  }
  if (countVal < 1 || countVal > 1000 || !Number.isInteger(countVal)) {
    error.value = '生成数量须为 1 ~ 1000 的整数'
    results.value = []
    return
  }

  const totalChars = len * countVal
  const randomValues = new Uint32Array(totalChars)
  crypto.getRandomValues(randomValues)
  let valIndex = 0

  const output: string[] = []
  for (let i = 0; i < countVal; i++) {
    let result = ''
    for (let j = 0; j < len; j++) {
      result += pool[randomValues[valIndex++] % pool.length]
    }
    output.push(result)
  }
  results.value = output
}

Comment on lines +233 to +241
const runFormat = () => {
error.value = ''
try {
output.value = formatXml(input.value, ' '.repeat(Math.max(0, Number(indentSize.value) || 2)))
} catch (err) {
error.value = err instanceof Error ? err.message : 'XML 格式化失败'
output.value = ''
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

runFormat 中,使用 Number(indentSize.value) || 2 来处理缩进大小。当用户主动将缩进设置为 0 时,Number(0) 的结果为 0(假值),这会导致表达式回退到默认值 2。因此,用户将无法生成无缩进的格式化 XML。建议使用更精确的空值合并或类型判断来保留 0 缩进。

const runFormat = () => {
  error.value = ''
  try {
    const indent = typeof indentSize.value === 'number' && !Number.isNaN(indentSize.value) ? Math.max(0, indentSize.value) : 2
    output.value = formatXml(input.value, ' '.repeat(indent))
  } catch (err) {
    error.value = err instanceof Error ? err.message : 'XML 格式化失败'
    output.value = ''
  }
}

Comment on lines +16 to +26
// 图片写入到下载目录
writeImageFile(base64Url) {
const matchs = /^data:image\/([a-z]{1,20});base64,/i.exec(base64Url)
if (!matchs) return
const filePath = path.join(
window.ztools.getPath('downloads'),
Date.now().toString() + '.' + matchs[1]
)
fs.writeFileSync(filePath, base64Url.substring(matchs[0].length), { encoding: 'base64' })
return filePath
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

writeImageFile 中,如果传入的 base64Url 格式不匹配正则表达式(例如不包含 data:image/... 前缀),函数会直接返回 undefined。这会导致调用方 Write/index.vue 无法检测到错误(因为没有抛出异常),从而静默退出插件,给用户造成保存成功的假象。建议在格式不匹配时抛出异常,以便触发调用方的错误捕获和通知机制。

  // 图片写入到下载目录
  writeImageFile(base64Url) {
    const matchs = /^data:image\/([a-z]{1,20});base64,/i.exec(base64Url)
    if (!matchs) {
      throw new Error('无效的图片 Base64 格式')
    }
    const filePath = path.join(
      window.ztools.getPath('downloads'),
      Date.now().toString() + '.' + matchs[1]
    )
    fs.writeFileSync(filePath, base64Url.substring(matchs[0].length), { encoding: 'base64' })
    return filePath
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant