reorg constants

This commit is contained in:
vegu-ai-tools
2026-05-15 16:50:34 +03:00
parent 4dcca5b208
commit d0e5161531
36 changed files with 192 additions and 156 deletions

View File

@@ -26,6 +26,7 @@
</template>
<script>
import { VIS_TYPE } from '@/constants/visual';
export default {
name: 'CoverImage',
@@ -110,7 +111,7 @@ export default {
type: 'upload_scene_asset',
scene_cover_image: this.isScene,
character_cover_image: this.isScene ? null : this.target.name,
vis_type: this.isScene ? 'SCENE_COVER' : 'CHARACTER_CARD',
vis_type: this.isScene ? VIS_TYPE.SCENE_CARD : VIS_TYPE.CHARACTER_CARD,
character_name: this.isScene ? null : this.target.name,
content: image_file_base64,
}));

View File

@@ -375,7 +375,7 @@ import VisualReferenceCarousel from './VisualReferenceCarousel.vue';
import ConfirmActionPrompt from './ConfirmActionPrompt.vue';
import VisualAssetsMixin from './VisualAssetsMixin.js';
import RevisionStackMixin from './RevisionStackMixin.js';
import { isVisualAgentReady } from '../constants/visual.js';
import { isVisualAgentReady, VIS_TYPE } from '@/constants/visual';
import { primaryModifierLabel } from '@/utils/keyboardModifiers';
import {
getMessageColor as resolveMessageColor,
@@ -392,7 +392,7 @@ const ASSET_SELECT_TYPES = {
dialogKey: 'avatarSelectDialog',
requiresCharacter: true,
getAssetIds(vm, ctx) {
const avatars = vm.getCharacterAssets(ctx.character, 'CHARACTER_PORTRAIT');
const avatars = vm.getCharacterAssets(ctx.character, VIS_TYPE.CHARACTER_PORTRAIT);
return avatars.map(a => a.id);
},
buildUpdateMessage(dialog) {
@@ -420,7 +420,7 @@ const ASSET_SELECT_TYPES = {
return Object.entries(vm.assetsMap)
.filter(([, asset]) => {
const meta = asset?.meta || {};
return meta.vis_type === 'SCENE_ILLUSTRATION' || meta.vis_type === 'SCENE_BACKGROUND';
return meta.vis_type === VIS_TYPE.SCENE_ILLUSTRATION || meta.vis_type === VIS_TYPE.SCENE_BACKGROUND;
})
.map(([id]) => id);
},
@@ -447,7 +447,7 @@ const ASSET_SELECT_TYPES = {
return Object.entries(vm.assetsMap)
.filter(([, asset]) => {
const meta = asset?.meta || {};
return meta.vis_type === 'CHARACTER_CARD' || meta.vis_type === 'SCENE_CARD';
return meta.vis_type === VIS_TYPE.CHARACTER_CARD || meta.vis_type === VIS_TYPE.SCENE_CARD;
})
.map(([id]) => id);
},

View File

@@ -32,11 +32,11 @@
<v-alert type="warning" density="compact" variant="text" class="mb-3 text-caption">Visual agent is not ready for image generation, will output prompt instead.</v-alert>
</div>
<!-- environment -->
<v-list-item @click="(event) => handleVisualize(null, event, 'SCENE_CARD')" prepend-icon="mdi-image-filter-hdr">
<v-list-item @click="(event) => handleVisualize(null, event, VIS_TYPE.SCENE_CARD)" prepend-icon="mdi-image-filter-hdr">
<v-list-item-title>Visualize Scene (Card)</v-list-item-title>
<v-list-item-subtitle>Generate a cover image of the scene</v-list-item-subtitle>
</v-list-item>
<v-list-item @click="(event) => handleVisualize(null, event, 'SCENE_BACKGROUND')" prepend-icon="mdi-image-filter-hdr">
<v-list-item @click="(event) => handleVisualize(null, event, VIS_TYPE.SCENE_BACKGROUND)" prepend-icon="mdi-image-filter-hdr">
<v-list-item-title>Visualize Scene (Background)</v-list-item-title>
<v-list-item-subtitle>Generate a purely environmental image of the scene</v-list-item-subtitle>
</v-list-item>
@@ -48,18 +48,18 @@
</v-list-item>
</template>
<v-list>
<v-list-item @click="(event) => handleVisualize(character_name, event, 'CHARACTER_CARD')" prepend-icon="mdi-brush">
<v-list-item @click="(event) => handleVisualize(character_name, event, VIS_TYPE.CHARACTER_CARD)" prepend-icon="mdi-brush">
<v-list-item-title>Card</v-list-item-title>
<v-list-item-subtitle>Generate a cover image portrait</v-list-item-subtitle>
</v-list-item>
<v-list-item @click="(event) => handleVisualize(character_name, event, 'CHARACTER_PORTRAIT')" prepend-icon="mdi-brush">
<v-list-item @click="(event) => handleVisualize(character_name, event, VIS_TYPE.CHARACTER_PORTRAIT)" prepend-icon="mdi-brush">
<v-list-item-title>Portrait</v-list-item-title>
<v-list-item-subtitle>Generate an image of {{ character_name }}'s face</v-list-item-subtitle>
</v-list-item>
</v-list>
</v-menu>
<!-- scene illustration -->
<v-list-item @click="(event) => handleVisualize(null, event, 'SCENE_ILLUSTRATION')" prepend-icon="mdi-image-filter-hdr">
<v-list-item @click="(event) => handleVisualize(null, event, VIS_TYPE.SCENE_ILLUSTRATION)" prepend-icon="mdi-image-filter-hdr">
<v-list-item-title>Visualize Moment (Illustration)</v-list-item-title>
<v-list-item-subtitle>Generate an image of the current moment</v-list-item-subtitle>
</v-list-item>
@@ -90,7 +90,7 @@
</v-dialog>
</template>
<script>
import { VIS_TYPE_OPTIONS } from '../constants/visual';
import { VIS_TYPE, VIS_TYPE_OPTIONS } from '@/constants/visual';
import { isPrimaryModifier } from '@/utils/keyboardModifiers';
export default {
name: 'SceneToolsVisual',
@@ -140,9 +140,10 @@ export default {
dialogTitle: '',
dialogPromptOnly: false,
dialogCharacterName: null,
dialogVisType: 'CHARACTER_CARD',
dialogVisType: VIS_TYPE.CHARACTER_CARD,
instructions: '',
autoAttachAssets: true,
VIS_TYPE,
}
},
@@ -177,13 +178,13 @@ export default {
return null;
}
if (vis_type === 'CHARACTER_CARD' || vis_type === 'CHARACTER_PORTRAIT') {
if (vis_type === VIS_TYPE.CHARACTER_CARD || vis_type === VIS_TYPE.CHARACTER_PORTRAIT) {
return `A visual of ${character_name} in the current moment`;
}
return null;
},
handleVisualize(character_name, event, vis_type = 'CHARACTER_CARD') {
handleVisualize(character_name, event, vis_type = VIS_TYPE.CHARACTER_CARD) {
const ctrlPressed = isPrimaryModifier(event);
const altPressed = event.altKey;
@@ -202,7 +203,7 @@ export default {
this.generateImage(this.dialogCharacterName, this.dialogPromptOnly, this.instructions, this.dialogVisType);
this.dialogOpen = false;
},
generateImage(character_name, prompt_only = false, instructions = null, vis_type = 'CHARACTER_CARD') {
generateImage(character_name, prompt_only = false, instructions = null, vis_type = VIS_TYPE.CHARACTER_CARD) {
const payload = {
type: 'visual',
action: 'visualize',

View File

@@ -392,7 +392,7 @@ import StatusNotification from './StatusNotification.vue';
import RateLimitAlert from './RateLimitAlert.vue';
import GenerationErrorDialog from './GenerationErrorDialog.vue';
import VersionMismatchAlert from './VersionMismatchAlert.vue';
import { versionsMatch } from '../constants/version.js';
import { versionsMatch } from '@/constants/version';
import VisualLibrary from './VisualLibrary.vue';
import VoiceLibrary from './VoiceLibrary.vue';
import WorldStateManager from './WorldStateManager.vue';
@@ -410,7 +410,7 @@ import PromptsView from './prompts/PromptsView.vue';
import PromptsMenu from './prompts/PromptsMenu.vue';
// import debounce
import { debounce } from 'lodash';
import { isVisualAgentReady, isImageEditAvailable, isImageCreateAvailable } from '../constants/visual.js';
import { isVisualAgentReady, isImageEditAvailable, isImageCreateAvailable } from '@/constants/visual';
import { createSceneAssetsRequester } from './VisualAssetsMixin.js';
export default {

View File

@@ -107,6 +107,8 @@
</template>
<script>
import { VIS_TYPE } from '@/constants/visual';
export default {
name: 'TemplateVisualStyle',
props: {
@@ -132,11 +134,11 @@ export default {
dirty: false,
visualTypeOptions: [
{ title: 'Style', value: 'STYLE' },
{ title: 'Character Card', value: 'CHARACTER_CARD' },
{ title: 'Character Portrait', value: 'CHARACTER_PORTRAIT' },
{ title: 'Scene Card', value: 'SCENE_CARD' },
{ title: 'Scene Background', value: 'SCENE_BACKGROUND' },
{ title: 'Scene Illustration', value: 'SCENE_ILLUSTRATION' },
{ title: 'Character Card', value: VIS_TYPE.CHARACTER_CARD },
{ title: 'Character Portrait', value: VIS_TYPE.CHARACTER_PORTRAIT },
{ title: 'Scene Card', value: VIS_TYPE.SCENE_CARD },
{ title: 'Scene Background', value: VIS_TYPE.SCENE_BACKGROUND },
{ title: 'Scene Illustration', value: VIS_TYPE.SCENE_ILLUSTRATION },
],
}
},

View File

@@ -40,7 +40,7 @@
</template>
<script>
import { FRONTEND_VERSION } from '../constants/version.js';
import { FRONTEND_VERSION } from '@/constants/version';
export default {
name: 'VersionMismatchAlert',

View File

@@ -19,6 +19,8 @@
</template>
<script>
import { VIS_TYPE } from '@/constants/visual';
export default {
name: 'VisualAssetsTree',
props: {
@@ -35,8 +37,8 @@ export default {
const groups = {};
for (const [id, asset] of Object.entries(assets)) {
const meta = (asset && asset.meta) || {};
const vis = meta.vis_type || 'UNSPECIFIED';
const groupKey = (vis && typeof vis === 'string') ? vis : 'UNSPECIFIED';
const vis = meta.vis_type || VIS_TYPE.UNSPECIFIED;
const groupKey = (vis && typeof vis === 'string') ? vis : VIS_TYPE.UNSPECIFIED;
if (!groups[groupKey]) groups[groupKey] = {};
if (groupKey.startsWith('CHARACTER_')) {
const charName = meta.character_name || 'Unknown';

View File

@@ -234,7 +234,7 @@
</template>
<script>
import { VIS_TYPE_OPTIONS } from '../constants/visual.js';
import { VIS_TYPE_OPTIONS, FORMAT_TYPE } from '@/constants/visual';
import { isPrimaryModifier } from '@/utils/keyboardModifiers';
import VisualReferenceImages from './VisualReferenceImages.vue';
import CoverBBoxEditor from './CoverBBoxEditor.vue';
@@ -339,8 +339,8 @@ export default {
return `data:${this.mediaType};base64,${base64}`;
},
imagePreviewClass(format) {
const fmt = format || 'LANDSCAPE';
return fmt === 'PORTRAIT' ? 'img-preview-portrait' : (fmt === 'SQUARE' ? 'img-preview-square' : 'img-preview-wide');
const fmt = format || FORMAT_TYPE.LANDSCAPE;
return fmt === FORMAT_TYPE.PORTRAIT ? 'img-preview-portrait' : (fmt === FORMAT_TYPE.SQUARE ? 'img-preview-square' : 'img-preview-wide');
},
saveMeta() {
if (!this.canSave) return;

View File

@@ -181,6 +181,7 @@ import VisualLibraryQueue from './VisualLibraryQueue.vue';
import VisualLibraryPendingQueue from './VisualLibraryPendingQueue.vue';
import VisualLibraryScene from './VisualLibraryScene.vue';
import ConfirmActionPrompt from './ConfirmActionPrompt.vue';
import { BACKEND_STATUS } from '@/constants/backendStatus';
export default {
name: 'VisualLibrary',
@@ -257,23 +258,23 @@ export default {
},
t2iColor() {
const status = this.visualMeta?.image_create?.status;
if (status === 'BackendStatusType.OK') return 'success';
if (status === 'BackendStatusType.WARNING') return 'warning';
if (status === 'BackendStatusType.ERROR') return 'error';
if (status === BACKEND_STATUS.OK) return 'success';
if (status === BACKEND_STATUS.WARNING) return 'warning';
if (status === BACKEND_STATUS.ERROR) return 'error';
return 'grey';
},
editColor() {
const status = this.visualMeta?.image_edit?.status;
if (status === 'BackendStatusType.OK') return 'success';
if (status === 'BackendStatusType.WARNING') return 'warning';
if (status === 'BackendStatusType.ERROR') return 'error';
if (status === BACKEND_STATUS.OK) return 'success';
if (status === BACKEND_STATUS.WARNING) return 'warning';
if (status === BACKEND_STATUS.ERROR) return 'error';
return 'grey';
},
analysisColor() {
const status = this.visualMeta?.image_analyzation?.status;
if (status === 'BackendStatusType.OK') return 'success';
if (status === 'BackendStatusType.WARNING') return 'warning';
if (status === 'BackendStatusType.ERROR') return 'error';
if (status === BACKEND_STATUS.OK) return 'success';
if (status === BACKEND_STATUS.WARNING) return 'warning';
if (status === BACKEND_STATUS.ERROR) return 'error';
return 'grey';
},
t2iTooltip() {
@@ -378,16 +379,16 @@ export default {
},
methods: {
getStatusIcon(status) {
if (status === 'BackendStatusType.OK') return 'mdi-check-circle';
if (status === 'BackendStatusType.WARNING') return 'mdi-alert';
if (status === 'BackendStatusType.ERROR') return 'mdi-alert';
if (status === 'BackendStatusType.DISCONNECTED') return 'mdi-clock-outline';
if (status === BACKEND_STATUS.OK) return 'mdi-check-circle';
if (status === BACKEND_STATUS.WARNING) return 'mdi-alert';
if (status === BACKEND_STATUS.ERROR) return 'mdi-alert';
if (status === BACKEND_STATUS.DISCONNECTED) return 'mdi-clock-outline';
return 'mdi-minus-circle';
},
getStatusIconColor(status) {
if (status === 'BackendStatusType.OK') return 'success';
if (status === 'BackendStatusType.WARNING') return 'warning';
if (status === 'BackendStatusType.ERROR') return 'error';
if (status === BACKEND_STATUS.OK) return 'success';
if (status === BACKEND_STATUS.WARNING) return 'warning';
if (status === BACKEND_STATUS.ERROR) return 'error';
return 'muted';
},
open() {
@@ -429,7 +430,7 @@ export default {
}
},
isOkStatus(status) {
return status === 'BackendStatusType.OK';
return status === BACKEND_STATUS.OK;
},
onDiscard(index) {
if (index == null || index < 0 || index >= this.items.length) return;

View File

@@ -173,7 +173,7 @@
</template>
<script>
import { VIS_TYPE_OPTIONS, FORMAT_OPTIONS } from '../constants/visual.js';
import { VIS_TYPE, VIS_TYPE_OPTIONS, FORMAT_OPTIONS, FORMAT_TYPE, GEN_TYPE } from '@/constants/visual';
import VisualReferenceImages from './VisualReferenceImages.vue';
export default {
name: 'VisualLibraryGenerate',
@@ -237,8 +237,8 @@ export default {
prompt: '',
negativePrompt: '',
instructions: '',
visType: 'UNSPECIFIED',
format: 'LANDSCAPE',
visType: VIS_TYPE.UNSPECIFIED,
format: FORMAT_TYPE.LANDSCAPE,
characterName: '',
referenceAssets: [],
};
@@ -344,7 +344,7 @@ export default {
} else {
// Prompt mode: use generate endpoint
// if references are set gen_type should be IMAGE_EDIT
const genType = (this.inlineReference != null || this.referenceAssets.length > 0) ? 'IMAGE_EDIT' : 'TEXT_TO_IMAGE';
const genType = (this.inlineReference != null || this.referenceAssets.length > 0) ? GEN_TYPE.IMAGE_EDIT : GEN_TYPE.TEXT_TO_IMAGE;
const payload = {
type: 'visual',
@@ -388,8 +388,8 @@ export default {
}
this.prompt = r.prompt || '';
this.negativePrompt = r.negative_prompt || '';
this.visType = r.vis_type || 'UNSPECIFIED';
this.format = r.format || 'LANDSCAPE';
this.visType = r.vis_type || VIS_TYPE.UNSPECIFIED;
this.format = r.format || FORMAT_TYPE.LANDSCAPE;
this.characterName = r.character_name || '';
this.referenceAssets = (r.reference_assets && Array.isArray(r.reference_assets)) ? r.reference_assets.slice() : [];
} else {
@@ -397,8 +397,8 @@ export default {
this.prompt = '';
this.negativePrompt = '';
this.instructions = '';
this.visType = 'UNSPECIFIED';
this.format = 'LANDSCAPE';
this.visType = VIS_TYPE.UNSPECIFIED;
this.format = FORMAT_TYPE.LANDSCAPE;
this.characterName = '';
this.referenceAssets = [];
}

View File

@@ -122,6 +122,7 @@ import VisualLibraryGenerate from './VisualLibraryGenerate.vue';
import ConfirmActionPrompt from './ConfirmActionPrompt.vue';
import VisualImageView from './VisualImageView.vue';
import RequestInput from './RequestInput.vue';
import { VIS_TYPE, FORMAT_TYPE, GEN_TYPE } from '@/constants/visual';
export default {
@@ -218,7 +219,7 @@ export default {
regenerateAvailable() {
const genType = this.selectedItem && this.selectedItem.request && this.selectedItem.request.gen_type;
if (!this.canGenerate) return false;
if (genType === 'IMAGE_EDIT') return this.editAvailable;
if (genType === GEN_TYPE.IMAGE_EDIT) return this.editAvailable;
return this.generationAvailable;
},
hasInstructions() {
@@ -246,8 +247,8 @@ export default {
return 'data:image/png;base64,' + base64;
},
imagePreviewClass(item) {
const fmt = item?.request?.format || 'LANDSCAPE';
return fmt === 'PORTRAIT' ? 'img-preview-portrait' : (fmt === 'SQUARE' ? 'img-preview-square' : 'img-preview-wide');
const fmt = item?.request?.format || FORMAT_TYPE.LANDSCAPE;
return fmt === FORMAT_TYPE.PORTRAIT ? 'img-preview-portrait' : (fmt === FORMAT_TYPE.SQUARE ? 'img-preview-square' : 'img-preview-wide');
},
discardSelected() {
if (this.selectedItem == null) return;
@@ -298,7 +299,7 @@ export default {
const payload = {
type: 'visual',
action: 'visualize',
vis_type: request.vis_type || 'UNSPECIFIED',
vis_type: request.vis_type || VIS_TYPE.UNSPECIFIED,
prompt_only: !this.generationAvailable,
};
if (request.character_name) {

View File

@@ -159,6 +159,7 @@ import VisualLibraryUpload from './VisualLibraryUpload.vue';
import VisualAssetsTree from './VisualAssetsTree.vue';
import { debounce } from 'lodash';
import { isPrimaryModifier, primaryModifierLabel } from '@/utils/keyboardModifiers';
import { VIS_TYPE, FORMAT_TYPE } from '@/constants/visual';
export default {
name: 'VisualLibraryScene',
@@ -254,8 +255,8 @@ export default {
if (!asset) continue;
const meta = asset.meta || {};
const vis = meta.vis_type || 'UNSPECIFIED';
const groupKey = (vis && typeof vis === 'string') ? vis : 'UNSPECIFIED';
const vis = meta.vis_type || VIS_TYPE.UNSPECIFIED;
const groupKey = (vis && typeof vis === 'string') ? vis : VIS_TYPE.UNSPECIFIED;
// Always open the top-level group folder
foldersToOpen.add(groupKey);
@@ -314,8 +315,8 @@ export default {
const initialRequest = {
prompt: '',
negative_prompt: '',
vis_type: meta.vis_type || 'UNSPECIFIED',
format: meta.format || 'LANDSCAPE',
vis_type: meta.vis_type || VIS_TYPE.UNSPECIFIED,
format: meta.format || FORMAT_TYPE.LANDSCAPE,
character_name: meta.character_name || '',
reference_assets: refIds,
};
@@ -327,8 +328,8 @@ export default {
const initialRequest = {
prompt: '',
negative_prompt: meta.negative_prompt || '',
vis_type: meta.vis_type || 'UNSPECIFIED',
format: meta.format || 'LANDSCAPE',
vis_type: meta.vis_type || VIS_TYPE.UNSPECIFIED,
format: meta.format || FORMAT_TYPE.LANDSCAPE,
character_name: meta.character_name || '',
reference_assets: [],
};

View File

@@ -24,7 +24,7 @@
</template>
<script>
import { VIS_TYPE_OPTIONS, isCharacterVisType as isCharacterVisTypeHelper } from '../constants/visual.js';
import { VIS_TYPE_OPTIONS, isCharacterVisType as isCharacterVisTypeHelper } from '@/constants/visual';
export default {
name: 'VisualLibraryUpload',
inject: ['getWebsocket'],

View File

@@ -92,6 +92,8 @@
<script>
import VisualAssetsTree from './VisualAssetsTree.vue';
import { VIS_TYPE } from '@/constants/visual';
export default {
name: 'VisualReferenceImages',
components: { VisualAssetsTree },
@@ -129,8 +131,8 @@ export default {
const groups = {};
for (const [id, asset] of Object.entries(assets)) {
const meta = (asset && asset.meta) || {};
const vis = meta.vis_type || 'UNSPECIFIED';
const groupKey = (vis && typeof vis === 'string') ? vis : 'UNSPECIFIED';
const vis = meta.vis_type || VIS_TYPE.UNSPECIFIED;
const groupKey = (vis && typeof vis === 'string') ? vis : VIS_TYPE.UNSPECIFIED;
if (!groups[groupKey]) groups[groupKey] = {};
if (groupKey.startsWith('CHARACTER_')) {
const charName = meta.character_name || 'Unknown';
@@ -167,7 +169,7 @@ export default {
const [group, sub] = nodeId.split('::');
for (const [id, asset] of Object.entries(assets)) {
const meta = (asset && asset.meta) || {};
const vis = meta.vis_type || 'UNSPECIFIED';
const vis = meta.vis_type || VIS_TYPE.UNSPECIFIED;
if (vis !== group) continue;
if (!sub || sub === '_root') {
ids.push(id);

View File

@@ -142,7 +142,7 @@ import WorldStateManagerHistory from './WorldStateManagerHistory.vue';
import WorldStateManagerSuggestions from './WorldStateManagerSuggestions.vue';
import GenerationOptions from './GenerationOptions.vue';
import RequestInput from './RequestInput.vue';
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
export default {

View File

@@ -349,7 +349,9 @@ import WorldStateManagerCharacterReinforcements from './WorldStateManagerCharact
import WorldStateManagerCharacterActor from './WorldStateManagerCharacterActor.vue';
import WorldStateManagerCharacterCreator from './WorldStateManagerCharacterCreator.vue';
import WorldStateManagerCharacterVisuals from './WorldStateManagerCharacterVisuals.vue';
import { MAX_CONTENT_WIDTH, FOLDER_NAME_MAX_LENGTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
import { FOLDER_NAME_MAX_LENGTH } from '@/constants/character';
import { VIS_TYPE } from '@/constants/visual';
import { isPrimaryModifier, primaryModifierLabel } from '@/utils/keyboardModifiers';
export default {
@@ -566,7 +568,7 @@ export default {
this.getWebsocket().send(JSON.stringify({
type: 'visual',
action: 'visualize',
vis_type: 'CHARACTER_CARD',
vis_type: VIS_TYPE.CHARACTER_CARD,
character_name: this.character.name,
set_cover_image: true,
override_character_cover: true,

View File

@@ -437,6 +437,7 @@ import VisualReferenceCarousel from './VisualReferenceCarousel.vue';
import AssetView from './AssetView.vue';
import EditableList from './EditableList.vue';
import { computeCharacterReferenceOptions } from '../utils/characterReferenceOptions.js';
import { VIS_TYPE, FORMAT_TYPE, GEN_TYPE } from '@/constants/visual';
export default {
name: 'WorldStateManagerCharacterVisualsAvatar',
@@ -491,11 +492,11 @@ export default {
assets() {
// Filter assets by CHARACTER_PORTRAIT vis_type and character name
if (!this.character?.name) return [];
return this.getCharacterAssets(this.character.name, 'CHARACTER_PORTRAIT');
return this.getCharacterAssets(this.character.name, VIS_TYPE.CHARACTER_PORTRAIT);
},
uploadConfig() {
return {
vis_type: 'CHARACTER_PORTRAIT',
vis_type: VIS_TYPE.CHARACTER_PORTRAIT,
namePrefix: 'avatar',
character: this.character,
};
@@ -542,7 +543,7 @@ export default {
this.defaultAvatarId = newVal?.avatar || null;
this.currentAvatarId = newVal?.current_avatar || null;
this.previousAssetsLength = 0;
this.loadAssetsForComponent('CHARACTER_PORTRAIT');
this.loadAssetsForComponent(VIS_TYPE.CHARACTER_PORTRAIT);
this.checkReferenceAssets();
this.hasAttemptedAutoSetDefaultAvatar = false;
},
@@ -669,7 +670,7 @@ export default {
checkReferenceAssets() {
if (!this.character?.name) return;
const targetVisType = 'CHARACTER_PORTRAIT';
const targetVisType = VIS_TYPE.CHARACTER_PORTRAIT;
const coverImageId = this.character?.cover_image;
// Use the shared helper to compute ordered options
@@ -774,7 +775,7 @@ export default {
// Store the request for saving later
this.pendingGenerateNewRequest = {
prompt: this.generateNewPromptInput.trim(),
vis_type: 'CHARACTER_PORTRAIT',
vis_type: VIS_TYPE.CHARACTER_PORTRAIT,
character_name: this.character.name,
};
@@ -782,7 +783,7 @@ export default {
const payload = {
type: 'visual',
action: 'visualize',
vis_type: 'CHARACTER_PORTRAIT',
vis_type: VIS_TYPE.CHARACTER_PORTRAIT,
character_name: this.character.name,
instructions: this.generateNewPromptInput.trim(),
// wsh-visualize uses these fields to construct an AssetAttachmentContext via MakeAssetAttachmentContext
@@ -823,9 +824,9 @@ export default {
this.pendingGenerationRequest = {
prompt: this.promptInput.trim(),
negative_prompt: null,
vis_type: 'CHARACTER_PORTRAIT',
gen_type: 'IMAGE_EDIT',
format: 'SQUARE',
vis_type: VIS_TYPE.CHARACTER_PORTRAIT,
gen_type: GEN_TYPE.IMAGE_EDIT,
format: FORMAT_TYPE.SQUARE,
character_name: this.character.name,
reference_assets: [this.selectedReferenceAssetId],
inline_reference: null,
@@ -860,9 +861,9 @@ export default {
const requests = prompts.map((prompt, idx) => ({
prompt: prompt,
negative_prompt: null,
vis_type: 'CHARACTER_PORTRAIT',
gen_type: 'IMAGE_EDIT',
format: 'SQUARE',
vis_type: VIS_TYPE.CHARACTER_PORTRAIT,
gen_type: GEN_TYPE.IMAGE_EDIT,
format: FORMAT_TYPE.SQUARE,
character_name: this.character.name,
reference_assets: [this.selectedReferenceAssetId],
inline_reference: null,
@@ -895,7 +896,7 @@ export default {
// Handle asset search results
if (data.type === 'asset_search_results') {
if (data.character_name === this.character?.name &&
data.vis_type === 'CHARACTER_PORTRAIT') {
data.vis_type === VIS_TYPE.CHARACTER_PORTRAIT) {
const assetIds = data.asset_ids || [];
// Apply results directly. Calling checkReferenceAssets() here would
@@ -937,7 +938,7 @@ export default {
const matchesCharacter = !request ||
(!request.character_name || request.character_name === this.character?.name);
const matchesVisType = !request ||
(!request.vis_type || request.vis_type === 'CHARACTER_PORTRAIT');
(!request.vis_type || request.vis_type === VIS_TYPE.CHARACTER_PORTRAIT);
if (matchesCharacter && matchesVisType) {
// Backend auto-saves when the visualize flow provides an AssetAttachmentContext
@@ -953,7 +954,7 @@ export default {
// Check if this is from Generate Variation (IMAGE_EDIT)
if (request && base64 &&
request.character_name === this.character?.name &&
request.vis_type === 'CHARACTER_PORTRAIT' &&
request.vis_type === VIS_TYPE.CHARACTER_PORTRAIT &&
this.isGenerating && this.pendingGenerationRequest) {
// Backend auto-saves via GenerationRequest.asset_attachment_context.
this.isGenerating = false;
@@ -994,7 +995,7 @@ export default {
},
mounted() {
this.registerMessageHandler(this.handleMessage);
this.loadAssetsForComponent('CHARACTER_PORTRAIT');
this.loadAssetsForComponent(VIS_TYPE.CHARACTER_PORTRAIT);
this.checkReferenceAssets();
},
unmounted() {

View File

@@ -405,6 +405,7 @@ import VisualReferenceCarousel from './VisualReferenceCarousel.vue';
import AssetView from './AssetView.vue';
import EditableList from './EditableList.vue';
import { computeCharacterReferenceOptions } from '../utils/characterReferenceOptions.js';
import { VIS_TYPE, FORMAT_TYPE, GEN_TYPE } from '@/constants/visual';
export default {
name: 'WorldStateManagerCharacterVisualsCover',
@@ -457,7 +458,7 @@ export default {
assets() {
// Filter assets by CHARACTER_CARD vis_type and character name
if (!this.character?.name) return [];
return this.getCharacterAssets(this.character.name, 'CHARACTER_CARD');
return this.getCharacterAssets(this.character.name, VIS_TYPE.CHARACTER_CARD);
},
anyCharacterAssets() {
// Get ALL assets for this character (any vis_type)
@@ -466,7 +467,7 @@ export default {
},
uploadConfig() {
return {
vis_type: 'CHARACTER_CARD',
vis_type: VIS_TYPE.CHARACTER_CARD,
namePrefix: 'cover',
character: this.character,
};
@@ -508,7 +509,7 @@ export default {
const coverImageId = newVal?.cover_image || null;
this.selectedAssetId = coverImageId;
this.currentCoverImageId = coverImageId;
this.loadAssetsForComponent('CHARACTER_CARD');
this.loadAssetsForComponent(VIS_TYPE.CHARACTER_CARD);
this.checkReferenceAssets();
},
immediate: true,
@@ -598,7 +599,7 @@ export default {
checkReferenceAssets() {
if (!this.character?.name) return;
const targetVisType = 'CHARACTER_CARD';
const targetVisType = VIS_TYPE.CHARACTER_CARD;
const avatarId = this.character?.avatar;
// Use the shared helper to compute ordered options
@@ -703,7 +704,7 @@ export default {
// Store the request for saving later
this.pendingGenerateNewRequest = {
prompt: this.generateNewPromptInput.trim(),
vis_type: 'CHARACTER_CARD',
vis_type: VIS_TYPE.CHARACTER_CARD,
character_name: this.character.name,
};
@@ -711,7 +712,7 @@ export default {
const payload = {
type: 'visual',
action: 'visualize',
vis_type: 'CHARACTER_CARD',
vis_type: VIS_TYPE.CHARACTER_CARD,
character_name: this.character.name,
instructions: this.generateNewPromptInput.trim(),
};
@@ -747,9 +748,9 @@ export default {
this.pendingGenerationRequest = {
prompt: this.promptInput.trim(),
negative_prompt: null,
vis_type: 'CHARACTER_CARD',
gen_type: 'IMAGE_EDIT',
format: 'PORTRAIT',
vis_type: VIS_TYPE.CHARACTER_CARD,
gen_type: GEN_TYPE.IMAGE_EDIT,
format: FORMAT_TYPE.PORTRAIT,
character_name: this.character.name,
reference_assets: [this.selectedReferenceAssetId],
inline_reference: null,
@@ -775,9 +776,9 @@ export default {
const requests = prompts.map((prompt, idx) => ({
prompt: prompt,
negative_prompt: null,
vis_type: 'CHARACTER_CARD',
gen_type: 'IMAGE_EDIT',
format: 'PORTRAIT',
vis_type: VIS_TYPE.CHARACTER_CARD,
gen_type: GEN_TYPE.IMAGE_EDIT,
format: FORMAT_TYPE.PORTRAIT,
character_name: this.character.name,
reference_assets: [this.selectedReferenceAssetId],
inline_reference: null,
@@ -808,7 +809,7 @@ export default {
// Handle asset search results
if (data.type === 'asset_search_results') {
if (data.character_name === this.character?.name &&
data.vis_type === 'CHARACTER_CARD') {
data.vis_type === VIS_TYPE.CHARACTER_CARD) {
const assetIds = data.asset_ids || [];
// Apply results directly. Calling checkReferenceAssets() here would
@@ -850,7 +851,7 @@ export default {
const matchesCharacter = !request ||
(!request.character_name || request.character_name === this.character?.name);
const matchesVisType = !request ||
(!request.vis_type || request.vis_type === 'CHARACTER_CARD');
(!request.vis_type || request.vis_type === VIS_TYPE.CHARACTER_CARD);
if (matchesCharacter && matchesVisType) {
// Use the request directly - it contains all the generation details including the generated prompt
@@ -858,13 +859,13 @@ export default {
const saveRequest = {
...request,
character_name: this.character.name,
vis_type: request?.vis_type || 'CHARACTER_CARD',
vis_type: request?.vis_type || VIS_TYPE.CHARACTER_CARD,
};
// Save the generated image as a scene asset
// If this is the first cover, set reference field to include both CHARACTER_PORTRAIT and CHARACTER_CARD
const isFirstCover = this.assets.length === 0;
const reference = isFirstCover ? ['CHARACTER_PORTRAIT', 'CHARACTER_CARD'] : null;
const reference = isFirstCover ? [VIS_TYPE.CHARACTER_PORTRAIT, VIS_TYPE.CHARACTER_CARD] : null;
this.saveGeneratedImage(base64, saveRequest, 'cover', reference);
this.isGeneratingNew = false;
@@ -878,12 +879,12 @@ export default {
// Check if this is from Generate Variation (IMAGE_EDIT)
if (request && base64 &&
request.character_name === this.character?.name &&
request.vis_type === 'CHARACTER_CARD' &&
request.vis_type === VIS_TYPE.CHARACTER_CARD &&
this.isGenerating && this.pendingGenerationRequest) {
// Automatically save the generated image as a scene asset
// If this is the first cover, set reference field to include both CHARACTER_PORTRAIT and CHARACTER_CARD
const isFirstCover = this.assets.length === 0;
const reference = isFirstCover ? ['CHARACTER_PORTRAIT', 'CHARACTER_CARD'] : null;
const reference = isFirstCover ? [VIS_TYPE.CHARACTER_PORTRAIT, VIS_TYPE.CHARACTER_CARD] : null;
this.saveGeneratedImage(base64, request, 'cover', reference);
this.isGenerating = false;
@@ -915,7 +916,7 @@ export default {
},
mounted() {
this.registerMessageHandler(this.handleMessage);
this.loadAssetsForComponent('CHARACTER_CARD');
this.loadAssetsForComponent(VIS_TYPE.CHARACTER_CARD);
this.checkReferenceAssets();
},
unmounted() {

View File

@@ -206,7 +206,7 @@
</template>
<script>
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
import { debounce } from 'lodash';
export default {

View File

@@ -137,7 +137,7 @@
import WorldStateManagerHistoryEntry from './WorldStateManagerHistoryEntry.vue';
import WorldStateManagerHistoryAdd from './WorldStateManagerHistoryAdd.vue';
import WorldStateManagerTimePassageEntry from './WorldStateManagerTimePassageEntry.vue';
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
export default {

View File

@@ -1,5 +1,5 @@
<template>
<v-card elevation="7" style="max-width: 1600px;" class="mb-2" density="compact">
<v-card elevation="7" :style="{ maxWidth: MAX_CONTENT_WIDTH }" class="mb-2" density="compact">
<v-card-title class="text-body-2 text-grey-lighten-3"><v-icon class="mr-2" size="small" icon="mdi-clock"></v-icon>{{ entry.time }}
<span v-if="entry.id && hovered" class="text-caption text-grey-darken-1 ml-2">
{{ entry.id }}
@@ -107,6 +107,7 @@ class HistoryEntry(pydantic.BaseModel):
import { SceneTextParser } from '@/utils/sceneMessageRenderer';
import { isPrimaryModifier } from '@/utils/keyboardModifiers';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
import ContextualGenerate from './ContextualGenerate.vue';
import ConfirmActionInline from './ConfirmActionInline.vue';
@@ -132,6 +133,7 @@ export default {
insertAmount: 1,
insertUnit: 'hours',
insertUnits: ['minutes', 'hours', 'days', 'weeks', 'months', 'years'],
MAX_CONTENT_WIDTH,
}
},
inject:[

View File

@@ -114,7 +114,7 @@
import CharacterImporter from './CharacterImporter.vue';
import WorldStateManagerCharacterListItem from './WorldStateManagerCharacterListItem.vue';
import VisualAssetsMixin from './VisualAssetsMixin.js';
import { FOLDER_NAME_MAX_LENGTH } from '@/constants';
import { FOLDER_NAME_MAX_LENGTH } from '@/constants/character';
export default {
name: "WorldStateManagerMenuCharacterTools",

View File

@@ -167,7 +167,7 @@
</template>
<script>
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
import ConfirmActionInline from './ConfirmActionInline.vue';
import WorldStateManagerPinConditionGroups from './WorldStateManagerPinConditionGroups.vue';

View File

@@ -111,7 +111,7 @@ import WorldStateManagerSceneExport from './WorldStateManagerSceneExport.vue';
import WorldStateManagerSceneDirection from './WorldStateManagerSceneDirection.vue';
import GameState from './GameState.vue';
import WorldStateManagerSceneSharedWorld from './WorldStateManagerSceneSharedWorld.vue';
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
export default {
name: "WorldStateManagerScene",

View File

@@ -390,7 +390,7 @@
<script>
import ConfirmActionInline from './ConfirmActionInline.vue';
import ContextualGenerate from './ContextualGenerate.vue';
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
const BLANK_SCENE_TYPE = {
name: '',

View File

@@ -77,7 +77,7 @@
</template>
<script>
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
export default {
name: 'WorldStateManagerSceneExport',

View File

@@ -111,7 +111,7 @@
<script>
import ContextualGenerate from './ContextualGenerate.vue';
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
export default {
name: "WorldStateManagerSceneOutline",

View File

@@ -98,7 +98,7 @@
<script>
import ConfirmActionPrompt from './ConfirmActionPrompt.vue';
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
export default {
name: "WorldStateManagerSceneSettings",

View File

@@ -178,7 +178,7 @@
<script>
import WorldStateManagerSceneEpisodes from './WorldStateManagerSceneEpisodes.vue';
import WorldStateManagerSceneSharedContext from './WorldStateManagerSceneSharedContext.vue';
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
export default {
name: 'WorldStateManagerSceneSharedWorld',

View File

@@ -12,7 +12,7 @@
</div>
</template>
<script>
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
import WorldStateManagerSuggestionsCharacter from './WorldStateManagerSuggestionsCharacter.vue';
export default {

View File

@@ -42,7 +42,7 @@
import WorldStateManagerWorldStates from './WorldStateManagerWorldStates.vue';
import WorldStateManagerWorldEntries from './WorldStateManagerWorldEntries.vue';
import { MAX_CONTENT_WIDTH } from '@/constants';
import { MAX_CONTENT_WIDTH } from '@/constants/layout';
export default {
name: 'WorldStateManagerWorld',

View File

@@ -0,0 +1,7 @@
// Must match str() repr of BackendStatusType in src/talemate/agents/visual/schema.py
export const BACKEND_STATUS = Object.freeze({
OK: 'BackendStatusType.OK',
WARNING: 'BackendStatusType.WARNING',
ERROR: 'BackendStatusType.ERROR',
DISCONNECTED: 'BackendStatusType.DISCONNECTED',
});

View File

@@ -1,4 +1,2 @@
export const MAX_CONTENT_WIDTH = '1600px';
// Must match FOLDER_NAME_MAX_LENGTH in src/talemate/server/world_state_manager/character.py
export const FOLDER_NAME_MAX_LENGTH = 29;

View File

@@ -0,0 +1 @@
export const MAX_CONTENT_WIDTH = '1600px';

View File

@@ -1,62 +1,73 @@
import { BACKEND_STATUS } from './backendStatus';
// Must match VIS_TYPE in src/talemate/agents/visual/schema.py
export const VIS_TYPE = Object.freeze({
CHARACTER_CARD: 'CHARACTER_CARD',
CHARACTER_PORTRAIT: 'CHARACTER_PORTRAIT',
// CHARACTER_SPRITE: 'CHARACTER_SPRITE',
SCENE_CARD: 'SCENE_CARD',
SCENE_BACKGROUND: 'SCENE_BACKGROUND',
SCENE_ILLUSTRATION: 'SCENE_ILLUSTRATION',
// ITEM_CARD: 'ITEM_CARD',
UNSPECIFIED: 'UNSPECIFIED',
});
export const VIS_TYPE_OPTIONS = [
'CHARACTER_CARD',
'CHARACTER_PORTRAIT',
//'CHARACTER_SPRITE',
'SCENE_CARD',
'SCENE_BACKGROUND',
'SCENE_ILLUSTRATION',
//'ITEM_CARD',
'UNSPECIFIED',
VIS_TYPE.CHARACTER_CARD,
VIS_TYPE.CHARACTER_PORTRAIT,
VIS_TYPE.SCENE_CARD,
VIS_TYPE.SCENE_BACKGROUND,
VIS_TYPE.SCENE_ILLUSTRATION,
VIS_TYPE.UNSPECIFIED,
];
export const FORMAT_OPTIONS = ['LANDSCAPE', 'PORTRAIT', 'SQUARE'];
// Must match FORMAT_TYPE in src/talemate/agents/visual/schema.py
export const FORMAT_TYPE = Object.freeze({
LANDSCAPE: 'LANDSCAPE',
PORTRAIT: 'PORTRAIT',
SQUARE: 'SQUARE',
});
export const FORMAT_OPTIONS = [
FORMAT_TYPE.LANDSCAPE,
FORMAT_TYPE.PORTRAIT,
FORMAT_TYPE.SQUARE,
];
// Must match GEN_TYPE in src/talemate/agents/visual/schema.py
export const GEN_TYPE = Object.freeze({
TEXT_TO_IMAGE: 'TEXT_TO_IMAGE',
IMAGE_EDIT: 'IMAGE_EDIT',
UPLOAD: 'UPLOAD',
});
export function isCharacterVisType(visType) {
return (visType || '').startsWith('CHARACTER_');
}
/**
* Check if the visual agent is ready (either image_create or image_edit is available)
* @param {Object} agentStatus - The agent status object
* @returns {boolean} True if visual agent is ready for image generation
*/
export function isVisualAgentReady(agentStatus) {
const visualAgent = agentStatus?.visual;
if (!visualAgent || !visualAgent.meta) {
return false;
}
return (
visualAgent.meta?.image_create?.status === 'BackendStatusType.OK' ||
visualAgent.meta?.image_edit?.status === 'BackendStatusType.OK'
visualAgent.meta?.image_create?.status === BACKEND_STATUS.OK ||
visualAgent.meta?.image_edit?.status === BACKEND_STATUS.OK
);
}
/**
* Check if image editing is available
* @param {Object} agentStatus - The agent status object
* @returns {boolean} True if image editing is available
*/
export function isImageEditAvailable(agentStatus) {
const visualAgent = agentStatus?.visual;
if (!visualAgent || !visualAgent.meta) {
return false;
}
const status = visualAgent.meta?.image_edit?.status;
return status === 'BackendStatusType.OK';
return visualAgent.meta?.image_edit?.status === BACKEND_STATUS.OK;
}
/**
* Check if image creation is available
* @param {Object} agentStatus - The agent status object
* @returns {boolean} True if image creation is available
*/
export function isImageCreateAvailable(agentStatus) {
const visualAgent = agentStatus?.visual;
if (!visualAgent || !visualAgent.meta) {
return false;
}
const status = visualAgent.meta?.image_create?.status;
return status === 'BackendStatusType.OK';
return visualAgent.meta?.image_create?.status === BACKEND_STATUS.OK;
}

View File

@@ -1,11 +1,13 @@
import { VIS_TYPE } from '@/constants/visual';
/**
* Shared utility for computing ordered reference asset options for character visuals.
*
*
* Orders assets with explicit references first, then other character assets.
* Prioritizes same-vis-type assets before falling back to any character assets.
* Returns the initial selection, ordered list, and reason for selection.
*
* @param {string} targetVisType - The target vis_type (e.g., 'CHARACTER_PORTRAIT', 'CHARACTER_CARD')
*
* @param {string} targetVisType - The target vis_type (a VIS_TYPE value)
* @param {Array} characterAssets - Array of asset objects with {id, meta, ...} structure (all character assets)
* @param {string|null} preferredId - Preferred asset ID (e.g., current avatar/cover id)
* @param {Array|null} sameVisTypeAssets - Optional array of assets with the same vis_type (prioritized first)
@@ -98,7 +100,7 @@ export function computeCharacterReferenceOptions(targetVisType, characterAssets,
const meta = fallbackAsset?.meta || {};
if (meta.character_name) {
const orderedIds = buildOrderedList([], [], characterAssets, fallbackId, targetVisType);
const fallbackType = targetVisType === 'CHARACTER_PORTRAIT' ? 'cover image' : 'avatar';
const fallbackType = targetVisType === VIS_TYPE.CHARACTER_PORTRAIT ? 'cover image' : 'avatar';
return {
selectedId: fallbackId,
orderedIds,