+
+ {/* Header */}
+
+
+
+ {props.title || t('newChat')}
+
+
+
+
+
+
+
+
+
+ {t('provider')}
+
+
+
+ {t('model')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Messages */}
+
+
+
+
+
+
+ {/* Input */}
+
+
+
+
+ {/* Tasks sidebar */}
+
+ setTasksOpen(false)}
+ />
+
+
+ {/* Delete session dialog */}
+
+ setSessionPendingDeleteId(undefined)}
+ onConfirm={() => void confirmDeleteSession()}
+ />
+
+
+ {/* Delete message dialog */}
+
+ setPendingDeleteMessage(undefined)}
+ onConfirm={confirmDeleteMessage}
+ />
+
+
+ {/* Regenerate message dialog */}
+
+ setPendingRegenerateMessage(undefined)}
+ onConfirm={confirmRegenerateMessage}
+ />
+
+
+ {/* Recall message dialog */}
+
+ setPendingRecallMessage(undefined)}
+ onConfirm={() => void confirmRecallMessage()}
+ onSecondaryConfirm={() => void confirmRecallAndRestoreMessage()}
+ />
+
+
+ )
+}
+
+export default App
diff --git a/packages/chatbox/src/assets/styles/global.css b/packages/chatbox/src/assets/styles/global.css
new file mode 100644
index 00000000..8d6d294d
--- /dev/null
+++ b/packages/chatbox/src/assets/styles/global.css
@@ -0,0 +1,109 @@
+@unocss;
+
+.markdown-rendered {
+ min-width: 0;
+ overflow-wrap: anywhere;
+ word-break: break-word;
+}
+
+.markdown-rendered > *:first-child {
+ margin-top: 0;
+}
+
+.markdown-rendered > *:last-child {
+ margin-bottom: 0;
+}
+
+.markdown-rendered table {
+ display: block;
+ max-width: 100%;
+ overflow-x: auto;
+ border-collapse: collapse;
+ white-space: nowrap;
+}
+
+.markdown-rendered thead,
+.markdown-rendered tbody,
+.markdown-rendered tr {
+ width: max-content;
+ min-width: 100%;
+}
+
+.markdown-rendered th,
+.markdown-rendered td {
+ white-space: nowrap;
+}
+
+.markdown-rendered pre,
+.markdown-rendered code,
+.markdown-rendered .internal-embed,
+.markdown-rendered img {
+ max-width: 100%;
+}
+
+.markdown-rendered pre {
+ overflow-x: auto;
+ white-space: pre;
+}
+
+.chatbox-tag-button {
+ border-radius: 999px;
+}
+
+.chatbox-input {
+ height: 7rem;
+ padding: 0.75rem;
+ line-height: 1.5;
+}
+
+.chatbox-input-pane {
+ display: flex;
+ flex-direction: column;
+}
+
+.chatbox-resizer {
+ flex-shrink: 0;
+ cursor: ns-resize;
+ padding-bottom: 4px;
+ padding-top: 4px;
+ touch-action: none;
+ user-select: none;
+}
+
+.chatbox-resizer-line {
+ height: 1px;
+ background: var(--background-modifier-border);
+ transition: background-color 120ms ease;
+}
+
+.chatbox-resizer:hover .chatbox-resizer-line,
+.chatbox-resizer.is-resizing .chatbox-resizer-line {
+ background: var(--interactive-accent);
+}
+
+.chatbox-resize-active,
+.chatbox-resize-active * {
+ cursor: ns-resize !important;
+ user-select: none !important;
+}
+
+@media (pointer: fine) and (min-width: 1024px) {
+ .chatbox-input-pane--resizable {
+ border-top-width: 0;
+ }
+
+ .chatbox-input-pane--resizable .chatbox-input {
+ flex: 1 1 auto;
+ height: auto;
+ min-height: 4rem;
+ min-width: 0;
+ }
+}
+
+@media (max-width: 768px) {
+ .chatbox-input {
+ height: 5.5rem;
+ padding: 0.625rem;
+ line-height: 1.4;
+ }
+}
diff --git a/packages/chatbox/src/components/ConfirmDialog.tsx b/packages/chatbox/src/components/ConfirmDialog.tsx
new file mode 100644
index 00000000..9e6f7092
--- /dev/null
+++ b/packages/chatbox/src/components/ConfirmDialog.tsx
@@ -0,0 +1,58 @@
+import { Show } from 'solid-js'
+import { t } from '../i18n'
+
+export function ConfirmDialog(props: {
+ title: string | undefined
+ message: string | undefined
+ confirmLabel: string | undefined
+ confirmClass?: string
+ secondaryConfirmLabel?: string | undefined
+ secondaryConfirmClass?: string
+ onCancel: () => void
+ onConfirm: () => void
+ onSecondaryConfirm?: () => void
+}) {
+ return (
+