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