Refactor message toolbar integration across components for improved action handling

This commit is contained in:
vegu-ai-tools
2026-05-14 16:14:20 +03:00
parent 2c65605173
commit a233d53643
4 changed files with 195 additions and 129 deletions

View File

@@ -56,55 +56,29 @@
</div>
</div>
<v-sheet v-if="hovered" rounded="sm" color="transparent">
<v-chip size="x-small" color="indigo-lighten-4" v-if="editing">
<v-icon class="mr-1">mdi-pencil</v-icon>
Editing - Press `enter` to submit. Click anywhere to cancel.</v-chip>
<v-chip size="x-small" color="grey-lighten-1" v-else-if="!editing && hovered" variant="text" class="mr-1">
<v-icon>mdi-pencil</v-icon>
Double-click to edit.</v-chip>
<!-- create pin -->
<v-chip size="x-small" label color="success" v-if="!editing && hovered" variant="outlined" @click="createPin(message_id)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-pin</v-icon>
Create Pin
</v-chip>
<!-- revision -->
<v-chip size="x-small" class="ml-2" label color="dirty" v-if="!editing && hovered && editorRevisionsEnabled && isLastMessage" variant="outlined" @click="reviseMessage(message_id)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-typewriter</v-icon>
Editor Revision
</v-chip>
<!-- fork scene -->
<v-chip size="x-small" class="ml-2" label :color="rev > 0 ? 'highlight1' : 'muted'" v-if="!editing && hovered && forkable" variant="outlined" @click="forkSceneInitiate(message_id)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-source-fork</v-icon>
Fork
</v-chip>
<!-- generate continuation -->
<v-chip size="x-small" class="ml-2" label color="primary" v-if="!editing && hovered && !continuing && isLastMessage" variant="outlined" @click="continueConversation" :disabled="uxLocked">
<v-icon class="mr-1">mdi-fast-forward</v-icon>
Continue
</v-chip>
<v-chip size="x-small" class="ml-2" label color="primary" v-if="!editing && hovered && continuing && isLastMessage" variant="outlined" disabled>
<v-progress-circular class="mr-1" size="14" indeterminate="disable-shrink" color="primary"></v-progress-circular>
Continuing...
</v-chip>
<!-- generate tts -->
<v-chip size="x-small" class="ml-2" label color="secondary" v-if="!editing && hovered && ttsAvailable" variant="outlined" @click="generateTTS(message_id)" :disabled="uxLocked || ttsBusy">
<v-icon class="mr-1">mdi-account-voice</v-icon>
TTS
<v-progress-circular v-if="ttsBusy" class="ml-2" size="14" indeterminate="disable-shrink"
color="secondary"></v-progress-circular>
</v-chip>
<!-- insert time passage -->
<v-chip size="x-small" class="ml-2" label color="time" v-if="!editing && hovered" variant="outlined" @click="insertTimePassage(message_id)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-clock-plus-outline</v-icon>
Time Passage
</v-chip>
<MessageToolbar
:message-id="message_id"
:editing="editing"
:ux-locked="uxLocked"
:is-last-message="isLastMessage"
:editor-revisions-enabled="editorRevisionsEnabled"
:tts-available="ttsAvailable"
:tts-busy="ttsBusy"
:rev="rev"
:scene-rev="sceneRev"
>
<template #extra-actions>
<!-- generate continuation -->
<v-chip size="x-small" class="ml-2" label color="primary" v-if="!continuing && isLastMessage" variant="outlined" @click="continueConversation" :disabled="uxLocked">
<v-icon class="mr-1">mdi-fast-forward</v-icon>
Continue
</v-chip>
<v-chip size="x-small" class="ml-2" label color="primary" v-if="continuing && isLastMessage" variant="outlined" disabled>
<v-progress-circular class="mr-1" size="14" indeterminate="disable-shrink" color="primary"></v-progress-circular>
Continuing...
</v-chip>
</template>
</MessageToolbar>
</v-sheet>
<div v-else style="height:24px">
@@ -119,13 +93,14 @@ import { isPrimaryModifier } from '@/utils/keyboardModifiers';
import MessageAssetImage from './MessageAssetImage.vue';
import MessageAssetMixin from './MessageAssetMixin.js';
import RevisionNav from './RevisionNav.vue';
import MessageToolbar from './MessageToolbar.vue';
export default {
components: {
MessageAssetImage,
RevisionNav,
MessageToolbar,
},
mixins: [MessageAssetMixin],
//props: ['character', 'text', 'color', 'message_id', 'uxLocked', 'isLastMessage'],
props: {
character: {
type: String,
@@ -211,15 +186,10 @@ export default {
emits: ['navigate-revision'],
inject: [
'requestDeleteMessage',
'getWebsocket',
'createPin',
'forkSceneInitiate',
'autocompleteRequest',
'autocompleteInfoMessage',
'getMessageStyle',
'reviseMessage',
'generateTTS',
'insertTimePassage',
'getWebsocket',
'autocompleteRequest',
'autocompleteInfoMessage',
'getMessageStyle',
],
computed: {
parser() {
@@ -244,9 +214,6 @@ export default {
renderedText() {
return this.parser.parse(this.text);
},
forkable() {
return this.rev <= this.sceneRev;
},
characterData() {
if (!this.scene || !this.scene.data || !this.scene.data.characters) {
return null;

View File

@@ -49,20 +49,17 @@
</div>
<v-sheet v-if="hovered" rounded="sm" color="transparent">
<v-chip size="x-small" color="indigo-lighten-4" v-if="editing">
<v-icon class="mr-1">mdi-pencil</v-icon>
Editing - Press `enter` to submit. Click anywhere to cancel.</v-chip>
<v-chip size="x-small" color="grey-lighten-1" v-else-if="!editing && hovered" variant="text" class="mr-1">
<v-icon>mdi-pencil</v-icon>
Double-click to edit.</v-chip>
<!-- generate tts -->
<v-chip size="x-small" class="ml-2" label color="secondary" v-if="!editing && hovered && ttsAvailable" variant="outlined" @click="generateTTS(message.id)" :disabled="uxLocked || ttsBusy">
<v-icon class="mr-1">mdi-account-voice</v-icon>
TTS
<v-progress-circular v-if="ttsBusy" class="ml-2" size="14" indeterminate="disable-shrink"
color="secondary"></v-progress-circular>
</v-chip>
<MessageToolbar
:message-id="message.id"
:editing="editing"
:ux-locked="uxLocked"
:tts-available="ttsAvailable"
:tts-busy="ttsBusy"
:show-pin="false"
:show-revision="false"
:show-fork="false"
:show-time-passage="false"
/>
</v-sheet>
<div v-else style="height:24px">
@@ -77,12 +74,14 @@ import { isPrimaryModifier } from '@/utils/keyboardModifiers';
import MessageAssetImage from './MessageAssetImage.vue';
import MessageAssetMixin from './MessageAssetMixin.js';
import RevisionNav from './RevisionNav.vue';
import MessageToolbar from './MessageToolbar.vue';
export default {
name: 'ContextInvestigationMessage',
components: {
MessageAssetImage,
RevisionNav,
MessageToolbar,
},
mixins: [MessageAssetMixin],
data() {
@@ -188,7 +187,7 @@ export default {
},
},
emits: ['navigate-revision'],
inject: ['requestDeleteMessage', 'getWebsocket', 'createPin', 'autocompleteRequest', 'autocompleteInfoMessage', 'getMessageStyle', 'getMessageColor', 'generateTTS'],
inject: ['requestDeleteMessage', 'getWebsocket', 'autocompleteRequest', 'autocompleteInfoMessage', 'getMessageStyle', 'getMessageColor'],
methods: {
toggle() {
if (!this.editing) {

View File

@@ -0,0 +1,134 @@
<template>
<!-- editing indicator -->
<v-chip size="x-small" color="indigo-lighten-4" v-if="editing">
<v-icon class="mr-1">mdi-pencil</v-icon>
Editing - Press `enter` to submit. Click anywhere to cancel.
</v-chip>
<template v-else>
<!-- edit hint -->
<v-chip size="x-small" color="grey-lighten-1" variant="text" class="mr-1">
<v-icon>mdi-pencil</v-icon>
Double-click to edit.
</v-chip>
<!-- create pin -->
<v-chip v-if="showPin" size="x-small" class="ml-2" label color="success" variant="outlined" @click="createPin(messageId)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-pin</v-icon>
Create Pin
</v-chip>
<!-- revision -->
<v-chip v-if="showRevision && editorRevisionsEnabled && isLastMessage" size="x-small" class="ml-2" label color="dirty" variant="outlined" @click="reviseMessage(messageId)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-typewriter</v-icon>
Editor Revision
</v-chip>
<!-- fork scene -->
<v-chip v-if="showFork && forkable" size="x-small" class="ml-2" label :color="rev > 0 ? 'highlight1' : 'muted'" variant="outlined" @click="forkSceneInitiate(messageId)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-source-fork</v-icon>
Fork
</v-chip>
<!-- type-specific actions (e.g. Continue) -->
<slot name="extra-actions" />
<!-- generate tts -->
<v-chip v-if="showTts && ttsAvailable" size="x-small" class="ml-2" label color="secondary" variant="outlined" @click="generateTTS(messageId)" :disabled="uxLocked || ttsBusy">
<v-icon class="mr-1">mdi-account-voice</v-icon>
TTS
<v-progress-circular v-if="ttsBusy" class="ml-2" size="14" indeterminate="disable-shrink" color="secondary"></v-progress-circular>
</v-chip>
<!-- insert time passage -->
<v-chip v-if="showTimePassage" size="x-small" class="ml-2" label color="time" variant="outlined" @click="insertTimePassage(messageId)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-clock-plus-outline</v-icon>
Time Passage
</v-chip>
</template>
</template>
<script>
// Shared hover toolbar for scene message components (CharacterMessage,
// NarratorMessage, ContextInvestigationMessage, ...). Action handlers are
// pulled from the provide/inject tree exposed by SceneMessages.vue, so the
// only wiring a consumer needs is state props + which actions to show.
export default {
name: 'MessageToolbar',
// Renders a fragment (multiple sibling chips) — consumers must wrap it
// (e.g. in a <v-sheet>); inheritAttrs is off so stray attrs don't warn.
inheritAttrs: false,
props: {
// id passed to the action handlers
messageId: {
type: [String, Number],
required: true,
},
editing: {
type: Boolean,
default: false,
},
uxLocked: {
type: Boolean,
default: false,
},
isLastMessage: {
type: Boolean,
default: false,
},
editorRevisionsEnabled: {
type: Boolean,
default: false,
},
ttsAvailable: {
type: Boolean,
default: false,
},
ttsBusy: {
type: Boolean,
default: false,
},
rev: {
type: Number,
default: 0,
},
sceneRev: {
type: Number,
default: 0,
},
// action toggles
showPin: {
type: Boolean,
default: true,
},
showRevision: {
type: Boolean,
default: true,
},
showFork: {
type: Boolean,
default: true,
},
showTts: {
type: Boolean,
default: true,
},
showTimePassage: {
type: Boolean,
default: true,
},
},
inject: [
'createPin',
'reviseMessage',
'forkSceneInitiate',
'generateTTS',
'insertTimePassage',
],
computed: {
forkable() {
return this.rev <= this.sceneRev;
},
},
}
</script>

View File

@@ -49,45 +49,17 @@
</div>
<v-sheet v-if="hovered" rounded="sm" color="transparent">
<div v-if="message_id">
<v-chip size="x-small" color="indigo-lighten-4" v-if="editing">
<v-icon class="mr-1">mdi-pencil</v-icon>
Editing - Press `enter` to submit. Click anywhere to cancel.</v-chip>
<v-chip size="x-small" color="grey-lighten-1" v-else-if="!editing && hovered" variant="text" class="mr-1">
<v-icon>mdi-pencil</v-icon>
Double-click to edit.</v-chip>
<v-chip size="x-small" label color="success" v-if="!editing && hovered" variant="outlined"
@click="createPin(message_id)">
<v-icon class="mr-1">mdi-pin</v-icon>
Create Pin
</v-chip>
<!-- revision -->
<v-chip size="x-small" class="ml-2" label color="dirty" v-if="!editing && hovered && editorRevisionsEnabled && isLastMessage" variant="outlined" @click="reviseMessage(message_id)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-typewriter</v-icon>
Editor Revision
</v-chip>
<!-- fork scene -->
<v-chip size="x-small" class="ml-2" label :color="rev > 0 ? 'highlight1' : 'muted'" v-if="!editing && hovered && forkable" variant="outlined"
@click="forkSceneInitiate(message_id)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-source-fork</v-icon>
Fork
</v-chip>
<!-- generate tts -->
<v-chip size="x-small" class="ml-2" label color="secondary" v-if="!editing && hovered && ttsAvailable" variant="outlined" @click="generateTTS(message_id)" :disabled="uxLocked || ttsBusy">
<v-icon class="mr-1">mdi-account-voice</v-icon>
TTS
<v-progress-circular v-if="ttsBusy" class="ml-2" size="14" indeterminate="disable-shrink"
color="secondary"></v-progress-circular>
</v-chip>
<!-- insert time passage -->
<v-chip size="x-small" class="ml-2" label color="time" v-if="!editing && hovered" variant="outlined" @click="insertTimePassage(message_id)" :disabled="uxLocked">
<v-icon class="mr-1">mdi-clock-plus-outline</v-icon>
Time Passage
</v-chip>
<MessageToolbar
:message-id="message_id"
:editing="editing"
:ux-locked="uxLocked"
:is-last-message="isLastMessage"
:editor-revisions-enabled="editorRevisionsEnabled"
:tts-available="ttsAvailable"
:tts-busy="ttsBusy"
:rev="rev"
:scene-rev="sceneRev"
/>
</div>
<div v-else>
<span class="text-muted text-caption">To edit the intro message open the <v-btn size="x-small" variant="text" color="primary" @click="openWorldStateManager('scene')"><v-icon>mdi-script</v-icon>Scene Editor</v-btn></span>
@@ -113,13 +85,14 @@ import { isPrimaryModifier } from '@/utils/keyboardModifiers';
import MessageAssetImage from './MessageAssetImage.vue';
import MessageAssetMixin from './MessageAssetMixin.js';
import RevisionNav from './RevisionNav.vue';
import MessageToolbar from './MessageToolbar.vue';
export default {
components: {
MessageAssetImage,
RevisionNav,
MessageToolbar,
},
mixins: [MessageAssetMixin],
// props: ['text', 'message_id', 'uxLocked', 'isLastMessage'],
props: {
text: {
@@ -188,17 +161,13 @@ export default {
},
emits: ['navigate-revision'],
inject: [
'requestDeleteMessage',
'getWebsocket',
'createPin',
'forkSceneInitiate',
'autocompleteRequest',
'autocompleteInfoMessage',
'getMessageStyle',
'requestDeleteMessage',
'getWebsocket',
'autocompleteRequest',
'autocompleteInfoMessage',
'getMessageStyle',
'openWorldStateManager',
'reviseMessage',
'generateTTS',
'insertTimePassage',
],
computed: {
parser() {
@@ -217,9 +186,6 @@ export default {
renderedText() {
return this.parser.parse(this.text);
},
forkable() {
return this.rev <= this.sceneRev;
},
// Asset mixin expects these
assetId() {
return this.asset_id;