frontend: standardize base styles and color scheme, rework chat input section
This commit is contained in:
1 parent
98fbed03cc
commit
1232872360
8 files changed
+167
-88
No files matched your search
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
--vt-c-black: #181818;
|
--vt-c-black: #181818;
|
||||||
--vt-c-black-soft: #222222;
|
--vt-c-black-soft: #222222;
|
||||||
--vt-c-black-mute: #282828;
|
--vt-c-black-mute: #2c2c2c;
|
||||||
|
|
||||||
--vt-c-indigo: #2c3e50;
|
--vt-c-indigo: #2c3e50;
|
||||||
|
|
||||||
@@ -25,17 +25,25 @@
|
|||||||
--color-green: #00bd7e;
|
--color-green: #00bd7e;
|
||||||
--color-green-dark: #003825;
|
--color-green-dark: #003825;
|
||||||
--color-blue: #3d8de1;
|
--color-blue: #3d8de1;
|
||||||
|
--color-violet: #9250ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* semantic color and size variables for this project */
|
/* semantic color and size variables for this project */
|
||||||
:root {
|
:root {
|
||||||
|
|
||||||
--color-background: var(--vt-c-black);
|
--color-background: var(--vt-c-black);
|
||||||
|
|
||||||
|
/* only one background, the rest should be surface colors */
|
||||||
--color-background-soft: var(--vt-c-black-soft);
|
--color-background-soft: var(--vt-c-black-soft);
|
||||||
--color-background-mute: var(--vt-c-black-mute);
|
--color-background-mute: var(--vt-c-black-mute);
|
||||||
|
|
||||||
--color-background-hover: var(--vt-c-black-mute);
|
--color-background-hover: var(--vt-c-black-mute);
|
||||||
--color-background-active: var(--vt-c-black-mute);
|
--color-background-active: var(--vt-c-black-mute);
|
||||||
|
|
||||||
|
--color-surface: var(--color-background-soft);
|
||||||
|
--color-surface-strong: var(--color-background-mute);
|
||||||
|
--color-surface-input: var(--color-background);
|
||||||
|
|
||||||
--color-border: var(--vt-c-divider-dark-2);
|
--color-border: var(--vt-c-divider-dark-2);
|
||||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||||
|
|
||||||
@@ -46,6 +54,11 @@
|
|||||||
|
|
||||||
--color-active: var(--color-heading);
|
--color-active: var(--color-heading);
|
||||||
|
|
||||||
|
--color-highlight: var(--color-violet);
|
||||||
|
--color-primary: var(--color-green);
|
||||||
|
--color-secondary: var(--color-blue);
|
||||||
|
--color-error: #d54141;
|
||||||
|
|
||||||
--border-radius: 6px;
|
--border-radius: 6px;
|
||||||
--section-gap: 160px;
|
--section-gap: 160px;
|
||||||
--header-height: 4em;
|
--header-height: 4em;
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ main {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main section {
|
||||||
|
margin: 1em;
|
||||||
|
padding: 1em;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
background-color: var(--color-surface);
|
||||||
|
}
|
||||||
|
|
||||||
.main-sidebar {
|
.main-sidebar {
|
||||||
height: 100dvh;
|
height: 100dvh;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -37,17 +45,53 @@ a {
|
|||||||
padding: 3px;
|
padding: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button, .button {
|
||||||
color: inherit;
|
color: var(--color-text);
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-surface-strong);
|
||||||
|
padding: 0.6em;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button.hollow, .button.hollow {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.hollow:hover {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
color: var(--color-heading);
|
||||||
border-color: var(--color-border-hover);
|
border-color: var(--color-border-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.hollow.primary { color: var(--color-primary); }
|
||||||
|
button.hollow.error { color: var(--color-error); }
|
||||||
|
|
||||||
|
input, textarea, select {
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 0.6em;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
color: var(--color-text);
|
||||||
|
background-color: var(--color-surface-input);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
input:hover, textarea:hover, select:hover {
|
||||||
|
border: 1px solid var(--color-border-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus, textarea:focus, select:focus {
|
||||||
|
outline: none;
|
||||||
|
border: 1px solid var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
input:disabled, textarea:disabled, select:disabled {
|
||||||
|
background-color: var(--color-surface);
|
||||||
|
}
|
||||||
|
|
||||||
.with-sidebar-expanded .when-sidebar-collapsed {
|
.with-sidebar-expanded .when-sidebar-collapsed {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
+6
-12
@@ -150,18 +150,12 @@ export class ChatSession {
|
|||||||
// fast access map
|
// fast access map
|
||||||
this.threadMap = {};
|
this.threadMap = {};
|
||||||
|
|
||||||
this.editInfo = computed(() => {
|
this.statusLine = computed(() => {
|
||||||
const parts = [this.creatingThread.value ? 'New Thread' : `Thread ${this.currentThreadId.value}`];
|
return this.creatingThread.value ?
|
||||||
if (this.editingMessageId.value != null) {
|
(this.editingMessageId.value != null ? 'Forking Message' : 'New Thread') :
|
||||||
if (this.noteMode.value) {
|
(this.editingMessageId.value != null ? 'Editing Message' :
|
||||||
parts.push(`Editing Note ${this.editingMessageId.value}`);
|
this.currentThreadId == null ? 'Writing in Chat Root' : null
|
||||||
} else {
|
);
|
||||||
parts.push(`Editing ${this.editingMessageId.value}`);
|
|
||||||
}
|
|
||||||
} else if (this.noteMode.value) {
|
|
||||||
parts.push('Editing Note');
|
|
||||||
}
|
|
||||||
return parts.join(' - ');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Internals
|
// Internals
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ function chatEntryClass(msg) {
|
|||||||
return ['log'];
|
return ['log'];
|
||||||
} else if (msg.type == 'thread-header') {
|
} else if (msg.type == 'thread-header') {
|
||||||
let cls = ['thread'];
|
let cls = ['thread'];
|
||||||
if (!msg.active) cls.push('thread-open')
|
if (!msg.active) cls.push(msg.isFork ? 'thread-fork' : 'thread-closed')
|
||||||
if (msg.isFork) cls.push('thread-fork');
|
|
||||||
return cls;
|
return cls;
|
||||||
} else {
|
} else {
|
||||||
return ['info'];
|
return ['info'];
|
||||||
@@ -247,22 +246,18 @@ watch(() => [props.message.format, props.message.content], ([newFormat, newConte
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
border-top: 1px solid var(--color-border);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.thread.thread-open {
|
.thread:hover {
|
||||||
|
border-color: 1px solid var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-closed {
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thread-open:hover {
|
.thread-fork {
|
||||||
border-top: 1px solid var(--color-heading);
|
|
||||||
}
|
|
||||||
|
|
||||||
.thread.thread-fork {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thread.thread-fork.thread-open {
|
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
padding: 0 0.4em;
|
padding: 0 0.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main-header {
|
||||||
|
background-color: var(--color-surface);
|
||||||
|
}
|
||||||
|
|
||||||
.header-button {
|
.header-button {
|
||||||
height: var(--header-height);
|
height: var(--header-height);
|
||||||
width: var(--header-height);
|
width: var(--header-height);
|
||||||
|
|||||||
@@ -124,6 +124,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.main-sidebar {
|
||||||
|
background-color: var(--color-surface);
|
||||||
|
}
|
||||||
|
|
||||||
.tab-bar, .tab-group {
|
.tab-bar, .tab-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -147,16 +151,19 @@
|
|||||||
/*flex-grow: 1;*/
|
/*flex-grow: 1;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab:hover {
|
.tab.active, .tab:hover {
|
||||||
background-color: var(--color-background-mute);
|
background: linear-gradient(to bottom, var(--color-surface-strong) 20%, var(--color-surface) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab.active {
|
.tab:hover {
|
||||||
color: var(--color-text-heading);
|
color: var(--color-heading);
|
||||||
background-color: var(--color-background);
|
|
||||||
|
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
border-top: 3px solid var(--color-active);
|
border-top: 3px solid var(--color-heading);
|
||||||
|
}
|
||||||
|
.tab.active {
|
||||||
|
color: var(--color-highlight);
|
||||||
|
padding-top: 0;
|
||||||
|
border-top: 3px solid var(--color-highlight);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
@@ -177,7 +184,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:hover {
|
.nav-item:hover {
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
color: var(--color-heading);
|
||||||
|
background-color: var(--color-surface-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item i {
|
.nav-item i {
|
||||||
@@ -197,7 +205,7 @@
|
|||||||
|
|
||||||
/* Adapt sidebar to small screens */
|
/* Adapt sidebar to small screens */
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.sidebar {
|
.main-sidebar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -350,13 +350,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
section {
|
|
||||||
margin: 1em;
|
|
||||||
padding: 1em;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.basic-config > div {
|
.basic-config > div {
|
||||||
margin-right: 2em;
|
margin-right: 2em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
+70
-42
@@ -11,6 +11,8 @@
|
|||||||
import * as ui from '../ui';
|
import * as ui from '../ui';
|
||||||
import { ChatSession } from '../chat';
|
import { ChatSession } from '../chat';
|
||||||
|
|
||||||
|
import * as iconoir from '@iconoir/vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -19,10 +21,12 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Chat state and logic live in the session; the view binds to its refs
|
// Chat state and logic live in the session; the view binds to its refs
|
||||||
|
// only declared refs work reactively
|
||||||
const session = new ChatSession();
|
const session = new ChatSession();
|
||||||
const {
|
const {
|
||||||
chat, chatEntries, editInfo,
|
chat, chatEntries, statusLine,
|
||||||
noteMode, editingMessageId, messageRunMap,
|
noteMode, editingMessageId, messageRunMap,
|
||||||
|
creatingThread,
|
||||||
} = session;
|
} = session;
|
||||||
|
|
||||||
// UI state coupled to the document itself
|
// UI state coupled to the document itself
|
||||||
@@ -96,6 +100,14 @@
|
|||||||
ui.alert("TODO: Rename Chats");
|
ui.alert("TODO: Rename Chats");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function newThreadClick () {
|
||||||
|
if (editingMessageId.value != null) {
|
||||||
|
creatingThread.value = !creatingThread.value;
|
||||||
|
} else {
|
||||||
|
session.startNewThread()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// === Special Chat Functions === //
|
// === Special Chat Functions === //
|
||||||
|
|
||||||
@@ -116,7 +128,6 @@
|
|||||||
{value: 'pin', title: "Pin message"},
|
{value: 'pin', title: "Pin message"},
|
||||||
{value: 'edit', title: "Edit in-place"},
|
{value: 'edit', title: "Edit in-place"},
|
||||||
{value: 'edit-fork', title: "Edit in new thread"},
|
{value: 'edit-fork', title: "Edit in new thread"},
|
||||||
{value: 'fork', title: "Fork thread from here"},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Long logs can be expanded/collapsed
|
// Long logs can be expanded/collapsed
|
||||||
@@ -141,12 +152,6 @@
|
|||||||
editMessage(message, true);
|
editMessage(message, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'fork': {
|
|
||||||
let newThread = await session.createFork(message.threadId, message.id);
|
|
||||||
ui.alert(`Forked thread ${newThread.id} from ${message.threadId}`);
|
|
||||||
session.activateThread(newThread.id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'toggle-log': {
|
case 'toggle-log': {
|
||||||
expandedLogs.toggle(message.id);
|
expandedLogs.toggle(message.id);
|
||||||
break;
|
break;
|
||||||
@@ -213,35 +218,42 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<div v-if="editInfo" style="font-size: 75%; margin-bottom: 0.7em;">
|
<div v-if="statusLine" class="status-line">
|
||||||
{{editInfo}}
|
{{statusLine}}
|
||||||
|
<button class="hollow status-close" v-if="editingMessageId != null" @click="editMessage(null)">
|
||||||
|
<iconoir.Xmark />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-field" ref="inputFieldElem" @keydown.ctrl.enter.prevent="sendMessage" contenteditable></div>
|
<div class="input-field" ref="inputFieldElem" @keydown.ctrl.enter.prevent="sendMessage" contenteditable></div>
|
||||||
|
|
||||||
<div class="input-row">
|
<div class="input-row">
|
||||||
<button @click="session.startNewThread()">
|
<div>
|
||||||
New Thread
|
<button class="hollow" :class="{primary: creatingThread}" @click="newThreadClick()">
|
||||||
</button>
|
<iconoir.ChatPlusInSolid v-if='creatingThread' />
|
||||||
<button @click="addRemoteResource()">
|
<iconoir.ChatPlusIn v-else />
|
||||||
Add Remote
|
</button>
|
||||||
</button>
|
<button class="hollow" :class="{primary: noteMode}" @click="noteMode=!noteMode">
|
||||||
<button @click="noteMode=!noteMode">
|
<iconoir.PostSolid v-if='noteMode' />
|
||||||
{{noteMode ? 'Cancel Note' : 'Make Note'}}
|
<iconoir.Post v-else />
|
||||||
</button>
|
</button>
|
||||||
<button v-if="editingMessageId != null" @click="editMessage(null)">
|
<button class="hollow" @click="sendPicture()">
|
||||||
Cancel Edit
|
<iconoir.Camera />
|
||||||
</button>
|
</button>
|
||||||
<button @click="sendPicture()">
|
<button class="hollow" @click="uploadFile()">
|
||||||
Send Picture
|
<iconoir.Attachment />
|
||||||
</button>
|
</button>
|
||||||
<button @click="uploadFile()">
|
</div>
|
||||||
Upload File
|
<div>
|
||||||
</button>
|
<button class="hollow" @click="addRemoteResource()">
|
||||||
<button id="send-button" @click="sendMessage()">
|
Add Remote
|
||||||
Send
|
</button>
|
||||||
<span style="font-size: 66%;">(Ctrl+Enter)</span>
|
</div>
|
||||||
</button>
|
<div>
|
||||||
|
<button class="hollow primary" id="send-button" @click="sendMessage()">
|
||||||
|
<iconoir.SendDiagonalSolid width="3em" height="3em" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
@@ -253,6 +265,17 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-line {
|
||||||
|
font-size: 75%;
|
||||||
|
margin-bottom: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-close {
|
||||||
|
float: right;
|
||||||
|
padding: 0;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.messages-container {
|
.messages-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
@@ -272,13 +295,17 @@
|
|||||||
|
|
||||||
.input-container {
|
.input-container {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
padding-bottom: 0;
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
border-top: 1px solid var(--color-border);
|
border-top: 1px solid var(--color-border);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-field {
|
.input-field {
|
||||||
white-space: preserve;
|
white-space: preserve;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-field:empty::before {
|
.input-field:empty::before {
|
||||||
@@ -288,19 +315,20 @@
|
|||||||
|
|
||||||
.input-row {
|
.input-row {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-row button {
|
||||||
|
width: 4em;
|
||||||
|
height: 4em;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#send-button {
|
#send-button {
|
||||||
padding: 8px 16px;
|
width: 5em;
|
||||||
border: none;
|
height: 4em;
|
||||||
border-radius: 4px;
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: #fff;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
height: min-content;
|
|
||||||
|
|
||||||
/* align-self: end; /* for flex rows */
|
|
||||||
float: right; /* for flow rows */
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in new issue
Block a user