Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,20 @@ export default defineConfig({
plugins: [
externalizeDepsPlugin({
exclude: ['mermaid', 'dompurify']
})
}),
{
name: 'generate-i18n-types',
enforce: 'pre',
apply(_config, env) {
if(env.mode === 'development') return true
return false
},
// 执行 generate-i18n-types.js
async buildStart() {
const { exec } = await import('child_process')
exec('node ./scripts/generate-i18n-types.js')
}
}
],
resolve: {
alias: {
Expand Down
62 changes: 62 additions & 0 deletions scripts/generate-i18n-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const i18nDir = path.resolve(__dirname, '../src/renderer/src/i18n/zh-CN')
const outputFile = path.resolve(__dirname, '../src/types/i18n.d.ts')

function safeKey(key) {
return /^[a-zA-Z_\$][a-zA-Z0-9_\$]*$/.test(key) ? key : JSON.stringify(key)
}
Comment on lines +11 to +13
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add type annotations and parameter validation.

The helper functions lack TypeScript type annotations and parameter validation, which could lead to runtime errors.

-function safeKey(key) {
+/**
+ * Converts a key to a safe TypeScript property identifier
+ * @param {string} key - The key to convert
+ * @returns {string} Safe TypeScript identifier or quoted string
+ */
+function safeKey(key) {
+  if (typeof key !== 'string') {
+    throw new Error(`Expected string key, got ${typeof key}`)
+  }
   return /^[a-zA-Z_\$][a-zA-Z0-9_\$]*$/.test(key) ? key : JSON.stringify(key)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function safeKey(key) {
return /^[a-zA-Z_\$][a-zA-Z0-9_\$]*$/.test(key) ? key : JSON.stringify(key)
}
/**
* Converts a key to a safe TypeScript property identifier
* @param {string} key - The key to convert
* @returns {string} Safe TypeScript identifier or quoted string
*/
function safeKey(key) {
if (typeof key !== 'string') {
throw new Error(`Expected string key, got ${typeof key}`)
}
return /^[a-zA-Z_\$][a-zA-Z0-9_\$]*$/.test(key) ? key : JSON.stringify(key)
}
🤖 Prompt for AI Agents
In scripts/generate-i18n-types.js around lines 11 to 13, the safeKey function
lacks TypeScript type annotations and does not validate its parameter. Add a
type annotation to the key parameter, specifying it as a string, and include a
check to ensure the parameter is a string before processing. If the parameter is
invalid, handle it gracefully, for example by throwing an error or returning a
default value.


function mergeKeys(target, source) {
for (const key in source) {
if (
typeof source[key] === 'object' &&
source[key] !== null &&
!Array.isArray(source[key])
) {
if (!target[key]) target[key] = {}
mergeKeys(target[key], source[key])
} else {
target[key] = ''
}
}
}

function genType(obj, indent = 4) {
const space = ' '.repeat(indent)
let result = '{\n'
for (const key in obj) {
const k = safeKey(key)
if (typeof obj[key] === 'object' && obj[key] !== null) {
result += `${space}${k}: ${genType(obj[key], indent + 2)}\n`
} else {
result += `${space}${k}: string\n`
}
}
result += ' '.repeat(indent - 2) + '}'
return result
}
Comment on lines +30 to +43
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add type annotations and parameter validation.

The genType function should have proper type annotations and validation.

-function genType(obj, indent = 4) {
+/**
+ * Generates TypeScript interface definition from object structure
+ * @param {Record<string, any>} obj - Object to generate types from
+ * @param {number} indent - Current indentation level
+ * @returns {string} TypeScript interface definition
+ */
+function genType(obj, indent = 4) {
+  if (typeof obj !== 'object' || obj === null) {
+    throw new Error('Object parameter is required for type generation')
+  }
+  
   const space = ' '.repeat(indent)
   let result = '{\n'
   for (const key in obj) {
     const k = safeKey(key)
     if (typeof obj[key] === 'object' && obj[key] !== null) {
       result += `${space}${k}: ${genType(obj[key], indent + 2)}\n`
     } else {
       result += `${space}${k}: string\n`
     }
   }
   result += ' '.repeat(indent - 2) + '}'
   return result
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function genType(obj, indent = 4) {
const space = ' '.repeat(indent)
let result = '{\n'
for (const key in obj) {
const k = safeKey(key)
if (typeof obj[key] === 'object' && obj[key] !== null) {
result += `${space}${k}: ${genType(obj[key], indent + 2)}\n`
} else {
result += `${space}${k}: string\n`
}
}
result += ' '.repeat(indent - 2) + '}'
return result
}
/**
* Generates TypeScript interface definition from object structure
* @param {Record<string, any>} obj - Object to generate types from
* @param {number} indent - Current indentation level
* @returns {string} TypeScript interface definition
*/
function genType(obj, indent = 4) {
if (typeof obj !== 'object' || obj === null) {
throw new Error('Object parameter is required for type generation')
}
const space = ' '.repeat(indent)
let result = '{\n'
for (const key in obj) {
const k = safeKey(key)
if (typeof obj[key] === 'object' && obj[key] !== null) {
result += `${space}${k}: ${genType(obj[key], indent + 2)}\n`
} else {
result += `${space}${k}: string\n`
}
}
result += ' '.repeat(indent - 2) + '}'
return result
}
🤖 Prompt for AI Agents
In scripts/generate-i18n-types.js around lines 30 to 43, the genType function
lacks type annotations and does not validate its parameters. Add appropriate
type annotations to the function signature to specify the expected input and
output types. Also, include validation to ensure the obj parameter is an object
and indent is a number before processing, throwing errors or handling invalid
inputs gracefully.


async function main() {
const files = fs.readdirSync(i18nDir).filter((f) => f.endsWith('.json'))
const allKeys = {}
for (const file of files) {
const json = JSON.parse(fs.readFileSync(path.join(i18nDir, file), 'utf-8'))
mergeKeys(allKeys, json)
}

const typeDef = `import { DefineLocaleMessage } from 'vue-i18n'\n\ndeclare module 'vue-i18n' {\n interface DefineLocaleMessage ${genType(allKeys, 4)}\n}\n`

fs.writeFileSync(outputFile, typeDef, 'utf-8')
console.log('i18n types file generated:', outputFile)
}

// 仅需要在本地开发时执行
if (import.meta.url === `file://${process.argv[1]}`) {
main()
}
32 changes: 22 additions & 10 deletions src/renderer/src/components/message/MessageItemAssistant.vue
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ const emit = defineEmits<{
fromTop: boolean,
modelInfo: { model_name: string; model_provider: string }
]
scrollToBottom: []
}>()

// 获取当前会话ID
Expand Down Expand Up @@ -252,9 +253,17 @@ const confirmFork = async () => {
}
}

const handleAction = (
action: 'retry' | 'delete' | 'copy' | 'prev' | 'next' | 'copyImage' | 'copyImageFromTop' | 'fork'
) => {
type HandleActionType =
| 'retry'
| 'delete'
| 'copy'
| 'prev'
| 'next'
| 'copyImage'
| 'copyImageFromTop'
| 'fork'

const handleAction = (action: HandleActionType) => {
if (action === 'retry') {
chatStore.retryMessage(currentMessage.value.id)
} else if (action === 'delete') {
Expand Down Expand Up @@ -284,14 +293,17 @@ const handleAction = (
.join('\n')
.trim()
)
} else if (action === 'prev') {
if (currentVariantIndex.value > 0) {
currentVariantIndex.value--
}
} else if (action === 'next') {
if (currentVariantIndex.value < totalVariants.value - 1) {
currentVariantIndex.value++
} else if (action === 'prev' || action === 'next') {
switch (action) {
case 'prev':
currentVariantIndex.value > 0 && currentVariantIndex.value--
break
case 'next':
currentVariantIndex.value < totalVariants.value - 1 && currentVariantIndex.value++
break
}

emit('scrollToBottom')
} else if (action === 'copyImage') {
emit('copyImage', currentMessage.value.id, currentMessage.value.parentId, false, {
model_name: currentMessage.value.model_name,
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/src/components/message/MessageList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
:message="msg as AssistantMessage"
:is-capturing-image="isCapturingImage"
@copy-image="handleCopyImage"
@scroll-to-bottom="scrollToBottom"
/>
<MessageItemUser
v-if="msg.role === 'user'"
:key="index"
:message="msg as UserMessage"
@retry="handleRetry(index)"
@scroll-to-bottom="scrollToBottom"
/>
</template>
</div>
Expand Down
10 changes: 6 additions & 4 deletions src/renderer/src/components/message/MessageToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<span v-show="!loading" class="flex flex-row gap-3">
<!-- Edit mode buttons (save/cancel) -->
<template v-if="isEditMode">
<Tooltip>
<Tooltip :delayDuration="200">
<TooltipTrigger as-child>
<Button
variant="ghost"
Expand All @@ -21,7 +21,7 @@
</TooltipTrigger>
<TooltipContent>{{ t('thread.toolbar.save') }}</TooltipContent>
</Tooltip>
<Tooltip>
<Tooltip :delayDuration="200">
<TooltipTrigger as-child>
<Button
variant="ghost"
Expand All @@ -38,10 +38,11 @@

<!-- Normal mode buttons -->
<template v-else>
<Tooltip>
<Tooltip :delayDuration="200">
<TooltipTrigger as-child>
<Button
v-show="isAssistant && hasVariants"
:disabled="currentVariantIndex === 0"
variant="ghost"
size="icon"
class="w-4 h-4 text-muted-foreground hover:text-primary hover:bg-transparent"
Expand All @@ -56,10 +57,11 @@
{{ currentVariantIndex !== undefined ? currentVariantIndex + 1 : 1 }} /
{{ totalVariants }}
</span>
<Tooltip>
<Tooltip :delayDuration="200">
<TooltipTrigger as-child>
<Button
v-show="isAssistant && hasVariants"
:disabled="hasVariants && totalVariants === (currentVariantIndex ?? 0) + 1"
variant="ghost"
size="icon"
class="w-4 h-4 text-muted-foreground hover:text-primary hover:bg-transparent"
Expand Down
Loading