Skip to content

Commit f4255ba

Browse files
authored
feat: support setting language for the extension
1 parent d40a2f7 commit f4255ba

File tree

9 files changed

+238
-145
lines changed

9 files changed

+238
-145
lines changed

cmd/testdata/stores.yaml

Lines changed: 83 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,88 @@
11
stores:
2-
- name: git
3-
kind:
4-
name: atest-store-git
5-
enabled: true
6-
url: xxx
7-
readonly: false
8-
disabled: false
9-
- name: ai
10-
kind:
11-
name: atest-ext-ai
12-
enabled: true
13-
url: ""
14-
readonly: false
15-
disabled: false
16-
properties:
2+
- name: git
3+
kind:
4+
name: atest-store-git
5+
dependencies: []
6+
url: "unix:///tmp/atest-store-git.sock"
7+
params: []
8+
link: ""
9+
enabled: true
10+
categories: []
11+
description: ""
12+
url: xxx
13+
username: ""
14+
password: ""
15+
readonly: false
16+
disabled: false
17+
properties: {}
18+
- name: ai
19+
kind:
20+
name: atest-ext-ai
21+
dependencies: [] # 无依赖
22+
url: "unix:///tmp/atest-ext-ai.sock"
23+
params:
1724
- key: "provider"
18-
description: "AI provider (local, openai, claude)"
19-
defaultValue: "local"
20-
- key: "model"
21-
description: "AI model name"
22-
defaultValue: "codellama"
25+
description: "AI provider (ollama, openai, deepseek)"
26+
defaultValue: "ollama"
2327
- key: "endpoint"
24-
description: "AI service endpoint"
28+
description: "AI service endpoint URL"
2529
defaultValue: "http://localhost:11434"
26-
plugins:
27-
- name: atest-store-git
28-
url: unix:///tmp/atest-store-git.sock
29-
enabled: true
30-
- name: atest-ext-ai
31-
url: unix:///tmp/atest-ext-ai.sock
30+
- key: "api_key"
31+
description: "API key for OpenAI/Deepseek providers"
32+
defaultValue: ""
33+
- key: "model"
34+
description: "AI model name (auto-discovered for ollama)"
35+
defaultValue: ""
36+
- key: "max_tokens"
37+
description: "Maximum tokens for AI generation"
38+
defaultValue: "4096"
39+
- key: "timeout"
40+
description: "Request timeout duration"
41+
defaultValue: "30s"
42+
link: "https://github.com/LinuxSuRen/atest-ext-ai"
3243
enabled: true
33-
description: "AI Extension Plugin for intelligent SQL generation and execution"
34-
version: "latest"
35-
registry: "ghcr.io/linuxsuren/atest-ext-ai"
44+
categories: ["ai", "sql-generation"]
45+
description: "AI Extension Plugin for natural language to SQL conversion"
46+
url: "unix:///tmp/atest-ext-ai.sock"
47+
username: ""
48+
password: ""
49+
readonly: false
50+
disabled: false
51+
properties:
52+
provider: "ollama"
53+
endpoint: "http://localhost:11434"
54+
api_key: ""
55+
model: ""
56+
max_tokens: "4096"
57+
timeout: "30s"
58+
59+
plugins:
60+
- name: atest-store-git
61+
dependencies: []
62+
url: "unix:///tmp/atest-store-git.sock"
63+
params: []
64+
link: ""
65+
enabled: true
66+
categories: []
67+
- name: atest-ext-ai
68+
dependencies: []
69+
url: "unix:///tmp/atest-ext-ai.sock"
70+
params:
71+
- key: "provider"
72+
description: "AI provider (ollama, openai, deepseek)"
73+
defaultValue: "ollama"
74+
- key: "endpoint"
75+
description: "AI service endpoint"
76+
defaultValue: "http://localhost:11434"
77+
- key: "api_key"
78+
description: "API key for external AI services"
79+
defaultValue: ""
80+
- key: "model"
81+
description: "AI model name (auto-discovered for ollama)"
82+
defaultValue: ""
83+
link: "https://github.com/LinuxSuRen/atest-ext-ai"
84+
enabled: true
85+
categories: ["ai", "sql-generation"]
86+
description: "AI Extension Plugin for natural language to SQL conversion"
87+
version: "v0.1.0"
88+
registry: "ghcr.io/linuxsuren/atest-ext-ai"

console/atest-ui/package-lock.json

Lines changed: 0 additions & 86 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

console/atest-ui/src/App.vue

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ const appVersion = ref('')
4141
const appVersionLink = ref('https://github.com/LinuxSuRen/api-testing')
4242
API.GetVersion((d) => {
4343
appVersion.value = d.version
44-
const version = d.version.match('^v\\d*.\\d*.\\d*')
45-
const dirtyVersion = d.version.match('^v\\d*.\\d*.\\d*-\\d*-g')
44+
const version = d.version.match(String.raw`^v\d*.\d*.\d*`)
45+
const dirtyVersion = d.version.match(String.raw`^v\d*.\d*.\d*-\d*-g`)
4646
4747
if (!version && !dirtyVersion) {
4848
return
@@ -55,16 +55,18 @@ API.GetVersion((d) => {
5555
}
5656
})
5757
58+
const hasLocalStorage = typeof globalThis !== 'undefined' && 'localStorage' in globalThis
59+
const storage = hasLocalStorage ? globalThis.localStorage : undefined
5860
const isCollapse = ref(true)
5961
watch(isCollapse, (v: boolean) => {
60-
window.localStorage.setItem('button.style', v ? 'simple' : '')
62+
storage?.setItem('button.style', v ? 'simple' : '')
6163
})
62-
const lastActiveMenu = window.localStorage.getItem('activeMenu')
64+
const lastActiveMenu = storage?.getItem('activeMenu') ?? 'welcome'
6365
const activeMenu = ref(lastActiveMenu === '' ? 'welcome' : lastActiveMenu)
6466
const panelName = ref(activeMenu)
6567
const handleSelect = (key: string) => {
6668
panelName.value = key
67-
window.localStorage.setItem('activeMenu', key)
69+
storage?.setItem('activeMenu', key)
6870
}
6971
7072
const locale = ref(Cache.GetPreference().language)
@@ -178,7 +180,7 @@ API.GetMenus((menus) => {
178180
<WelcomePage v-else-if="panelName === 'welcome' || panelName === ''" />
179181

180182
<span v-for="menu in extensionMenus" :key="menu.index" :index="menu.index">
181-
<Extension v-if="panelName === menu.index" :name="menu.name" />
183+
<Extension v-if="panelName === menu.index" :name="menu.index" />
182184
</span>
183185
</el-main>
184186

console/atest-ui/src/views/Extension.vue

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,76 @@
11
<script setup lang="ts">
2-
import { ref } from 'vue'
3-
import { API } from './net';
2+
import { ref, watch } from 'vue'
3+
import { useI18n } from 'vue-i18n'
4+
import { API } from './net'
45
56
interface Props {
67
name: string
78
}
89
const props = defineProps<Props>()
10+
const { locale } = useI18n()
11+
12+
let pluginInstance: { setLocale?: (value: string) => void } | undefined
13+
914
const loading = ref(true)
1015
const loadPlugin = async (): Promise<void> => {
1116
try {
17+
// First load CSS
1218
API.GetPageOfCSS(props.name, (d) => {
1319
const style = document.createElement('style');
1420
style.textContent = d.message;
1521
document.head.appendChild(style);
1622
});
1723
24+
// Then load JS and mount plugin
1825
API.GetPageOfJS(props.name, (d) => {
1926
const script = document.createElement('script');
2027
script.type = 'text/javascript';
2128
script.textContent = d.message;
2229
document.head.appendChild(script);
2330
24-
const plugin = window.ATestPlugin;
25-
26-
if (plugin && plugin.mount) {
27-
console.log('extension load success');
28-
const container = document.getElementById("plugin-container");
29-
if (container) {
30-
container.innerHTML = ''; // Clear previous content
31-
plugin.mount(container);
31+
// Implement retry mechanism with exponential backoff
32+
const checkPluginLoad = (retries = 0, maxRetries = 10) => {
33+
const globalScope = globalThis as { ATestPlugin?: { mount?: (el: Element) => void, setLocale?: (value: string) => void } };
34+
const plugin = globalScope.ATestPlugin;
35+
36+
if (plugin && plugin.mount) {
37+
const container = document.getElementById("plugin-container");
38+
if (container) {
39+
container.innerHTML = ''; // Clear previous content
40+
plugin.mount(container);
41+
plugin.setLocale?.(locale.value);
42+
pluginInstance = plugin;
43+
loading.value = false;
44+
} else {
45+
loading.value = false;
46+
}
47+
} else if (retries < maxRetries) {
48+
// Incremental retry mechanism: 50ms, 100ms, 150ms...
49+
const delay = 50 + retries * 50;
50+
setTimeout(() => checkPluginLoad(retries + 1, maxRetries), delay);
51+
} else {
52+
loading.value = false;
3253
}
33-
}
54+
};
55+
56+
// Start the retry mechanism
57+
checkPluginLoad();
3458
});
3559
} catch (error) {
36-
console.log(`extension load error: ${(error as Error).message}`)
37-
} finally {
38-
console.log('extension load finally');
60+
loading.value = false; // Set loading to false on error
61+
console.error('Failed to load extension assets', error);
3962
}
4063
};
41-
try {
42-
loadPlugin();
43-
} catch (error) {
44-
console.error('extension load error:', error);
45-
}
64+
65+
loadPlugin().catch((error) => {
66+
loading.value = false;
67+
console.error('Failed to initialize extension plugin', error);
68+
});
69+
70+
watch(locale, (value) => {
71+
const normalized = value ?? 'en';
72+
pluginInstance?.setLocale?.(normalized);
73+
});
4674
</script>
4775

4876
<template>

0 commit comments

Comments
 (0)