diff --git a/src/components/dialog/content/ExecutionErrorDialogContent.vue b/src/components/dialog/content/ExecutionErrorDialogContent.vue
index e513b24c8..7cee141ed 100644
--- a/src/components/dialog/content/ExecutionErrorDialogContent.vue
+++ b/src/components/dialog/content/ExecutionErrorDialogContent.vue
@@ -38,7 +38,6 @@
diff --git a/src/constants/serverConfig.ts b/src/constants/serverConfig.ts
index 032150369..0eb8a273a 100644
--- a/src/constants/serverConfig.ts
+++ b/src/constants/serverConfig.ts
@@ -10,15 +10,17 @@ import {
VramManagement
} from '@/types/serverArgs'
+export type ServerConfigValue = string | number | true | null | undefined
+
export interface ServerConfig extends FormItem {
id: string
defaultValue: T
category?: string[]
// Override the default value getter with a custom function.
- getValue?: (value: T) => Record
+ getValue?: (value: T) => Record
}
-export const WEB_ONLY_CONFIG_ITEMS: ServerConfig[] = [
+export const WEB_ONLY_CONFIG_ITEMS: ServerConfig[] = [
// We only need these settings in the web version. Desktop app manages them already.
{
id: 'listen',
@@ -43,21 +45,21 @@ export const SERVER_CONFIG_ITEMS: ServerConfig[] = [
name: 'TLS Key File: Path to TLS key file for HTTPS',
category: ['Network'],
type: 'text',
- defaultValue: undefined
+ defaultValue: ''
},
{
id: 'tls-certfile',
name: 'TLS Certificate File: Path to TLS certificate file for HTTPS',
category: ['Network'],
type: 'text',
- defaultValue: undefined
+ defaultValue: ''
},
{
id: 'enable-cors-header',
name: 'Enable CORS header: Use "*" for all origins or specify domain',
category: ['Network'],
type: 'text',
- defaultValue: undefined
+ defaultValue: ''
},
{
id: 'max-upload-size',
@@ -97,7 +99,7 @@ export const SERVER_CONFIG_ITEMS: ServerConfig[] = [
name: 'CUDA device index to use',
category: ['CUDA'],
type: 'number',
- defaultValue: undefined
+ defaultValue: null
},
{
id: 'cuda-malloc',
@@ -253,7 +255,7 @@ export const SERVER_CONFIG_ITEMS: ServerConfig[] = [
name: 'DirectML device index',
category: ['Memory'],
type: 'number',
- defaultValue: undefined
+ defaultValue: null
},
{
id: 'disable-ipex-optimize',
@@ -295,10 +297,10 @@ export const SERVER_CONFIG_ITEMS: ServerConfig[] = [
},
{
id: 'cache-lru',
- name: 'Use LRU caching with a maximum of N node results cached. (0 to disable).',
+ name: 'Use LRU caching with a maximum of N node results cached.',
category: ['Cache'],
type: 'number',
- defaultValue: 0,
+ defaultValue: null,
tooltip: 'May use more RAM/VRAM.'
},
@@ -366,7 +368,7 @@ export const SERVER_CONFIG_ITEMS: ServerConfig[] = [
name: 'Reserved VRAM (GB)',
category: ['Memory'],
type: 'number',
- defaultValue: undefined,
+ defaultValue: null,
tooltip:
'Set the amount of vram in GB you want to reserve for use by your OS/other software. By default some amount is reverved depending on your OS.'
},
diff --git a/src/hooks/clipboardHooks.ts b/src/hooks/clipboardHooks.ts
new file mode 100644
index 000000000..c2d06b325
--- /dev/null
+++ b/src/hooks/clipboardHooks.ts
@@ -0,0 +1,37 @@
+import { useClipboard } from '@vueuse/core'
+import { useToast } from 'primevue/usetoast'
+
+export function useCopyToClipboard() {
+ const { copy, isSupported } = useClipboard()
+ const toast = useToast()
+
+ const copyToClipboard = async (text: string) => {
+ if (isSupported) {
+ try {
+ await copy(text)
+ toast.add({
+ severity: 'success',
+ summary: 'Success',
+ detail: 'Copied to clipboard',
+ life: 3000
+ })
+ } catch (err) {
+ toast.add({
+ severity: 'error',
+ summary: 'Error',
+ detail: 'Failed to copy report'
+ })
+ }
+ } else {
+ toast.add({
+ severity: 'error',
+ summary: 'Error',
+ detail: 'Clipboard API not supported in your browser'
+ })
+ }
+ }
+
+ return {
+ copyToClipboard
+ }
+}
diff --git a/src/stores/serverConfigStore.ts b/src/stores/serverConfigStore.ts
index cf07fc5ec..980b3acb0 100644
--- a/src/stores/serverConfigStore.ts
+++ b/src/stores/serverConfigStore.ts
@@ -1,4 +1,4 @@
-import { ServerConfig } from '@/constants/serverConfig'
+import { ServerConfig, ServerConfigValue } from '@/constants/serverConfig'
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
@@ -14,18 +14,26 @@ export type ServerConfigWithValue = ServerConfig & {
}
export const useServerConfigStore = defineStore('serverConfig', () => {
- const serverConfigById = ref>>({})
+ const serverConfigById = ref<
+ Record>
+ >({})
const serverConfigs = computed(() => {
return Object.values(serverConfigById.value)
})
- const modifiedConfigs = computed[]>(() => {
- return serverConfigs.value.filter((config) => {
- return config.initialValue !== config.value
- })
- })
-
+ const modifiedConfigs = computed[]>(
+ () => {
+ return serverConfigs.value.filter((config) => {
+ return config.initialValue !== config.value
+ })
+ }
+ )
+ const revertChanges = () => {
+ for (const config of modifiedConfigs.value) {
+ config.value = config.initialValue
+ }
+ }
const serverConfigsByCategory = computed<
- Record[]>
+ Record[]>
>(() => {
return serverConfigs.value.reduce(
(acc, config) => {
@@ -34,15 +42,17 @@ export const useServerConfigStore = defineStore('serverConfig', () => {
acc[category].push(config)
return acc
},
- {} as Record[]>
+ {} as Record[]>
)
})
- const serverConfigValues = computed>(() => {
+ const serverConfigValues = computed>(() => {
return Object.fromEntries(
serverConfigs.value.map((config) => {
return [
config.id,
- config.value === config.defaultValue || !config.value
+ config.value === config.defaultValue ||
+ config.value === null ||
+ config.value === undefined
? undefined
: config.value
]
@@ -50,10 +60,18 @@ export const useServerConfigStore = defineStore('serverConfig', () => {
)
})
const launchArgs = computed>(() => {
- return Object.assign(
+ const args: Record<
+ string,
+ Omit
+ > = Object.assign(
{},
...serverConfigs.value.map((config) => {
- if (config.value === config.defaultValue || !config.value) {
+ // Filter out configs that have the default value or undefined | null value
+ if (
+ config.value === config.defaultValue ||
+ config.value === null ||
+ config.value === undefined
+ ) {
return {}
}
return config.getValue
@@ -61,11 +79,29 @@ export const useServerConfigStore = defineStore('serverConfig', () => {
: { [config.id]: config.value }
})
)
+
+ // Convert true to empty string
+ // Convert number to string
+ return Object.fromEntries(
+ Object.entries(args).map(([key, value]) => {
+ if (value === true) {
+ return [key, '']
+ }
+ return [key, value.toString()]
+ })
+ ) as Record
+ })
+ const commandLineArgs = computed(() => {
+ return Object.entries(launchArgs.value)
+ .map(([key, value]) => [`--${key}`, value])
+ .flat()
+ .filter((arg: string) => arg !== '')
+ .join(' ')
})
function loadServerConfig(
- configs: ServerConfig[],
- values: Record
+ configs: ServerConfig[],
+ values: Record
) {
for (const config of configs) {
const value = values[config.id] ?? config.defaultValue
@@ -84,6 +120,8 @@ export const useServerConfigStore = defineStore('serverConfig', () => {
serverConfigsByCategory,
serverConfigValues,
launchArgs,
+ commandLineArgs,
+ revertChanges,
loadServerConfig
}
})
diff --git a/tests-ui/tests/fast/store/serverConfigStore.test.ts b/tests-ui/tests/fast/store/serverConfigStore.test.ts
index 79f5ebe49..d5d4a5dea 100644
--- a/tests-ui/tests/fast/store/serverConfigStore.test.ts
+++ b/tests-ui/tests/fast/store/serverConfigStore.test.ts
@@ -170,21 +170,80 @@ describe('useServerConfigStore', () => {
const configs: ServerConfig[] = [
{ ...dummyFormItem, id: 'test.config1', defaultValue: 'default1' },
{ ...dummyFormItem, id: 'test.config2', defaultValue: 'default2' },
- { ...dummyFormItem, id: 'test.config3', defaultValue: 'default3' }
+ { ...dummyFormItem, id: 'test.config3', defaultValue: 'default3' },
+ { ...dummyFormItem, id: 'test.config4', defaultValue: null }
]
store.loadServerConfig(configs, {
'test.config1': undefined,
'test.config2': null,
- 'test.config3': ''
+ 'test.config3': '',
+ 'test.config4': 0
})
- expect(Object.keys(store.launchArgs)).toHaveLength(0)
- expect(Object.keys(store.serverConfigValues)).toEqual([
- 'test.config1',
- 'test.config2',
- 'test.config3'
+ expect(Object.keys(store.launchArgs)).toEqual([
+ 'test.config3',
+ 'test.config4'
])
+ expect(Object.values(store.launchArgs)).toEqual(['', '0'])
+ expect(store.serverConfigById['test.config3'].value).toBe('')
+ expect(store.serverConfigById['test.config4'].value).toBe(0)
+ expect(Object.values(store.serverConfigValues)).toEqual([
+ undefined,
+ undefined,
+ '',
+ 0
+ ])
+ })
+
+ it('should convert true to empty string in launch arguments', () => {
+ store.loadServerConfig(
+ [
+ {
+ ...dummyFormItem,
+ id: 'test.config1',
+ defaultValue: 0
+ }
+ ],
+ {
+ 'test.config1': true
+ }
+ )
+ expect(store.launchArgs['test.config1']).toBe('')
+ expect(store.commandLineArgs).toBe('--test.config1')
+ })
+
+ it('should convert number to string in launch arguments', () => {
+ store.loadServerConfig(
+ [
+ {
+ ...dummyFormItem,
+ id: 'test.config1',
+ defaultValue: 1
+ }
+ ],
+ {
+ 'test.config1': 123
+ }
+ )
+ expect(store.launchArgs['test.config1']).toBe('123')
+ expect(store.commandLineArgs).toBe('--test.config1 123')
+ })
+
+ it('should drop nullish values in launch arguments', () => {
+ store.loadServerConfig(
+ [
+ {
+ ...dummyFormItem,
+ id: 'test.config1',
+ defaultValue: 1
+ }
+ ],
+ {
+ 'test.config1': null
+ }
+ )
+ expect(Object.keys(store.launchArgs)).toHaveLength(0)
})
it('should track modified configs', () => {