mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-23 17:08:10 -07:00
next: ai part 2
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
createAskMessage,
|
||||
setMessageContent,
|
||||
} from "./message/index.js";
|
||||
|
||||
/** @typedef {import("../storage.js").StoredMessage} StoredMessage */
|
||||
|
||||
/**
|
||||
* @typedef {Object} AskConversationOptions
|
||||
* @property {HTMLElement} hero
|
||||
* @property {() => void} onScroll
|
||||
*/
|
||||
|
||||
/** @param {AskConversationOptions} options */
|
||||
export function createAskConversation(options) {
|
||||
const conversation = document.createElement("section");
|
||||
const transcript = document.createElement("ol");
|
||||
let scrollFrame = 0;
|
||||
|
||||
conversation.dataset.askConversation = "";
|
||||
conversation.setAttribute("aria-label", "Conversation");
|
||||
transcript.ariaLive = "polite";
|
||||
transcript.hidden = true;
|
||||
conversation.append(options.hero, transcript);
|
||||
conversation.addEventListener("scroll", options.onScroll);
|
||||
|
||||
function sync() {
|
||||
const hasMessages = transcript.childElementCount > 0;
|
||||
|
||||
options.hero.hidden = hasMessages;
|
||||
transcript.hidden = !hasMessages;
|
||||
}
|
||||
|
||||
return {
|
||||
element: conversation,
|
||||
sync,
|
||||
isNearBottom() {
|
||||
const { clientHeight, scrollHeight, scrollTop } = conversation;
|
||||
return scrollHeight - clientHeight - scrollTop < 96;
|
||||
},
|
||||
cancelScroll() {
|
||||
cancelAnimationFrame(scrollFrame);
|
||||
},
|
||||
scrollToBottom() {
|
||||
cancelAnimationFrame(scrollFrame);
|
||||
scrollFrame = requestAnimationFrame(() => {
|
||||
conversation.scrollTop = conversation.scrollHeight;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @param {"user" | "assistant"} role
|
||||
* @param {string} text
|
||||
*/
|
||||
append(role, text) {
|
||||
const message = createAskMessage(role, text);
|
||||
|
||||
transcript.append(message.item);
|
||||
sync();
|
||||
return message;
|
||||
},
|
||||
/**
|
||||
* @param {HTMLElement} content
|
||||
* @param {"user" | "assistant"} role
|
||||
* @param {string} text
|
||||
*/
|
||||
setContent(content, role, text) {
|
||||
setMessageContent(content, role, text);
|
||||
},
|
||||
/** @param {StoredMessage[]} messages */
|
||||
render(messages) {
|
||||
transcript.replaceChildren(
|
||||
...messages.map((message) =>
|
||||
createAskMessage(message.role, message.content).item
|
||||
),
|
||||
);
|
||||
sync();
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { renderMarkdown } from "../../markdown.js";
|
||||
|
||||
/** @typedef {"user" | "assistant"} MessageRole */
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} content
|
||||
* @param {MessageRole} role
|
||||
* @param {string} text
|
||||
*/
|
||||
export function setMessageContent(content, role, text) {
|
||||
if (role === "assistant") renderMarkdown(content, text);
|
||||
else content.textContent = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MessageRole} role
|
||||
* @param {string} text
|
||||
*/
|
||||
export function createAskMessage(role, text) {
|
||||
const item = document.createElement("li");
|
||||
const label = document.createElement("strong");
|
||||
const content = document.createElement("div");
|
||||
|
||||
item.dataset.role = role;
|
||||
label.append(role === "user" ? "You" : "Assistant");
|
||||
setMessageContent(content, role, text);
|
||||
item.append(label, content);
|
||||
|
||||
return { item, content };
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
main[data-page="ask"] [data-ask-conversation] > ol > li {
|
||||
display: grid;
|
||||
gap: 0.35rem;
|
||||
max-width: 100%;
|
||||
|
||||
&[data-role="user"] {
|
||||
justify-self: end;
|
||||
max-width: min(82%, 34rem);
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 0.65rem 0.65rem 0.15rem 0.65rem;
|
||||
color: var(--black);
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
> strong {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
> div {
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: var(--line-height-sm);
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
> * {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
> * + * {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: var(--white);
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
h3,
|
||||
h4 {
|
||||
font-size: var(--font-size-base);
|
||||
}
|
||||
|
||||
:is(ul, ol) {
|
||||
display: grid;
|
||||
gap: 0.4rem;
|
||||
padding-left: 1.35rem;
|
||||
}
|
||||
|
||||
li::marker {
|
||||
color: var(--orange);
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--white);
|
||||
text-decoration-color: var(--orange);
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 0.15em;
|
||||
}
|
||||
|
||||
:not(pre) > code {
|
||||
border: 1px solid var(--ask-border);
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.08em 0.28em;
|
||||
color: color-mix(in oklch, var(--orange) 72%, var(--white));
|
||||
background: var(--ask-panel);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
pre {
|
||||
max-width: 100%;
|
||||
border: 1px solid var(--ask-border);
|
||||
border-radius: var(--control-radius);
|
||||
padding: 0.85rem 1rem;
|
||||
background: var(--ask-panel);
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
|
||||
code {
|
||||
font-size: var(--font-size-xs);
|
||||
line-height: var(--line-height-xs);
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 1px solid var(--orange);
|
||||
padding-left: 0.85rem;
|
||||
color: color-mix(in oklch, var(--white) 72%, transparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
main[data-page="ask"]
|
||||
[data-ask-conversation]
|
||||
> ol
|
||||
> li[data-role="user"] {
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
main[data-page="ask"] [data-ask-conversation] {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scroll-padding-block: 1.5rem 7rem;
|
||||
scrollbar-gutter: stable;
|
||||
mask-image: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
black 0.85rem,
|
||||
black calc(100% - 6rem),
|
||||
transparent
|
||||
);
|
||||
|
||||
> ol {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 1.5rem;
|
||||
width: min(100% - 3rem, var(--ask-content-width));
|
||||
margin: 0 auto;
|
||||
padding: 1.5rem 0 7.5rem;
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
main[data-page="ask"] [data-ask-conversation] {
|
||||
scroll-padding-top: 5.75rem;
|
||||
|
||||
> ol {
|
||||
width: min(100% - 2rem, var(--ask-content-width));
|
||||
padding-top: 5.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user