chatgpt-web/src/lib/ChatSettingField.svelte

220 lines
7.7 KiB
Svelte

<script lang="ts">
import { createEventDispatcher } from 'svelte'
// import { getProfile } from './Profiles.svelte'
import { cleanSettingValue, setChatSettingValue } from './Storage.svelte'
import type { Chat, ChatSetting, ChatSettings, ControlAction, FieldControl, SettingPrompt } from './Types.svelte'
import { autoGrowInputOnEvent, errorNotice } from './Util.svelte'
// import { replace } from 'svelte-spa-router'
import Fa from 'svelte-fa/src/fa.svelte'
import { openModal } from 'svelte-modals'
import PromptConfirm from './PromptConfirm.svelte'
export let setting:ChatSetting
export let chatSettings:ChatSettings
export let chat:Chat
export let chatDefaults:Record<string, any>
export let originalProfile:String
const chatId = chat.id
const fieldControls:ControlAction[] = (setting.fieldControls || [] as FieldControl[]).map(fc => {
return fc.getAction(chatId, setting, chatSettings[setting.key])
})
if (originalProfile) {
// eventually...
}
const dispatch = createEventDispatcher()
const refreshSettings = () => {
dispatch('refresh')
}
const settingChecks:Record<string, SettingPrompt[]> = {
profile: [
{
title: 'Unsaved Profile Changes',
message: 'Unsaved changes to the current profile will be lost.\n Continue?',
checkPrompt: (setting, newVal, oldVal) => {
return !!chatSettings.isDirty && newVal !== oldVal
},
passed: false
}
]
}
const resetSettingCheck = (key:keyof ChatSettings) => {
const checks = settingChecks[key]
checks && checks.forEach((c) => { c.passed = false })
}
const queueSettingValueChange = (event: Event, setting: ChatSetting) => {
if (event.target === null) return
const val = chatSettings[setting.key]
const el = (event.target as HTMLInputElement)
const doSet = () => {
try {
(typeof setting.beforeChange === 'function') && setting.beforeChange(chatId, setting, el.checked || el.value) &&
refreshSettings()
} catch (e) {
errorNotice('Unable to change:', e)
}
switch (setting.type) {
case 'boolean':
setChatSettingValue(chatId, setting, el.checked)
refreshSettings()
break
default:
setChatSettingValue(chatId, setting, el.value)
}
const newVal = cleanSettingValue(setting.type, el.checked || el.value)
if (val === newVal) return
try {
if ((typeof setting.afterChange === 'function') && setting.afterChange(chatId, setting, chatSettings[setting.key])) {
// console.log('Refreshed from setting', setting.key, chatSettings[setting.key], val)
refreshSettings()
}
} catch (e) {
setChatSettingValue(chatId, setting, val)
errorNotice('Unable to change:', e)
}
dispatch('change', setting)
}
const checks = settingChecks[setting.key] || []
const newVal = cleanSettingValue(setting.type, el.checked || el.value)
for (let i = 0, l = checks.length; i < l; i++) {
const c = checks[i]
if (c.passed) continue
if (c.checkPrompt(setting, newVal, val)) {
openModal(PromptConfirm, {
title: c.title,
message: c.message,
class: c.class || 'is-warning',
onConfirm: () => {
c.passed = true
if (c.onYes && c.onYes(setting, newVal, val)) {
resetSettingCheck(setting.key)
} else {
queueSettingValueChange(event, setting)
}
},
onCancel: () => {
// roll-back
if (!c.onNo || !c.onNo(setting, newVal, val)) {
resetSettingCheck(setting.key)
setChatSettingValue(chatId, setting, val)
// refresh setting modal, if open
c.onNo && c.onNo(setting, newVal, val)
refreshSettings()
} else {
queueSettingValueChange(event, setting)
}
}
})
} else {
c.passed = true
}
}
// passed all?
if (checks.find(c => !c.passed)) return
resetSettingCheck(setting.key)
doSet()
}
</script>
{#if (typeof setting.hide !== 'function') || !setting.hide(chatId)}
{#if setting.header}
<p class="notification {setting.headerClass}">
{@html setting.header}
</p>
{/if}
<div class="field is-horizontal">
{#if setting.type === 'boolean'}
<div class="field is-normal">
<label class="label" for="settings-{setting.key}" title="{setting.title}">
<input
type="checkbox"
title="{setting.title}"
class="checkbox"
id="settings-{setting.key}"
checked={!!chatSettings[setting.key]}
on:click={e => queueSettingValueChange(e, setting)}
>
{setting.name}
</label>
</div>
{:else if setting.type === 'textarea'}
<div class="field is-normal" style="width:100%">
<label class="label" for="settings-{setting.key}" title="{setting.title}">{setting.name}</label>
<textarea
class="input is-info is-focused chat-input auto-size"
placeholder={setting.placeholder || ''}
rows="1"
on:input={e => autoGrowInputOnEvent(e)}
on:change={e => { queueSettingValueChange(e, setting); autoGrowInputOnEvent(e) }}
>{chatSettings[setting.key]}</textarea>
</div>
{:else}
<div class="field-label is-normal">
<label class="label" for="settings-{setting.key}" title="{setting.title}">{setting.name}</label>
</div>
{/if}
<div class="field-body">
<div class="field" class:has-addons={fieldControls.length}>
{#if setting.type === 'number'}
<input
class="input"
inputmode="decimal"
type={setting.type}
title="{setting.title}"
id="settings-{setting.key}"
value={chatSettings[setting.key]}
min={setting.min}
max={setting.max}
step={setting.step}
placeholder={String(setting.placeholder || chatDefaults[setting.key])}
on:change={e => queueSettingValueChange(e, setting)}
/>
{:else if setting.type === 'select' || setting.type === 'select-number'}
<!-- <div class="select"> -->
<div class="select" class:control={fieldControls.length}>
<select id="settings-{setting.key}" title="{setting.title}" on:change={e => queueSettingValueChange(e, setting) } >
{#each setting.options as option}
<option class:is-default={option.value === chatDefaults[setting.key]} value={option.value} selected={option.value === chatSettings[setting.key]}>{option.text}</option>
{/each}
</select>
</div>
{#each fieldControls as cont}
<div class="control">
<button title={cont.text} on:click={() => { cont.action && cont.action(chatId, setting, chatSettings[setting.key]); refreshSettings() }} class="button {cont.class || ''}">
{#if cont.text}
<span class="text">
<Fa icon={cont.icon} />
</span>
{/if}
{#if cont.icon}
<span class="icon">
<Fa icon={cont.icon} />
</span>
{/if}
</button>
</div>
{/each}
{:else if setting.type === 'text'}
<div class="field">
<input
type="text"
title="{setting.title}"
class="input"
value={chatSettings[setting.key]}
on:change={e => { queueSettingValueChange(e, setting) }}
>
</div>
{/if}
</div>
</div>
</div>
{/if}