refactor(ui): extract dock prompt shell
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { For, Show, createMemo, onCleanup, onMount, type Component } from "solid-js"
|
import { For, Show, createMemo, onCleanup, onMount, type Component } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
|
import { DockPrompt } from "@opencode-ai/ui/dock-prompt"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
import type { QuestionAnswer, QuestionRequest } from "@opencode-ai/sdk/v2"
|
import type { QuestionAnswer, QuestionRequest } from "@opencode-ai/sdk/v2"
|
||||||
@@ -232,9 +233,11 @@ export const QuestionDock: Component<{ request: QuestionRequest }> = (props) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-component="question-prompt" ref={(el) => (root = el)}>
|
<DockPrompt
|
||||||
<div data-slot="question-body">
|
kind="question"
|
||||||
<div data-slot="question-header">
|
ref={(el) => (root = el)}
|
||||||
|
header={
|
||||||
|
<>
|
||||||
<div data-slot="question-header-title">{summary()}</div>
|
<div data-slot="question-header-title">{summary()}</div>
|
||||||
<div data-slot="question-progress">
|
<div data-slot="question-progress">
|
||||||
<For each={questions()}>
|
<For each={questions()}>
|
||||||
@@ -254,172 +257,169 @@ export const QuestionDock: Component<{ request: QuestionRequest }> = (props) =>
|
|||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
|
}
|
||||||
<div data-slot="question-content">
|
footer={
|
||||||
<div data-slot="question-text">{question()?.question}</div>
|
<>
|
||||||
<Show when={multi()} fallback={<div data-slot="question-hint">{language.t("ui.question.singleHint")}</div>}>
|
<Button variant="ghost" size="large" disabled={store.sending} onClick={reject}>
|
||||||
<div data-slot="question-hint">{language.t("ui.question.multiHint")}</div>
|
{language.t("ui.common.dismiss")}
|
||||||
</Show>
|
</Button>
|
||||||
<div data-slot="question-options">
|
<div data-slot="question-footer-actions">
|
||||||
<For each={options()}>
|
<Show when={store.tab > 0}>
|
||||||
{(opt, i) => {
|
<Button variant="secondary" size="large" disabled={store.sending} onClick={back}>
|
||||||
const picked = () => store.answers[store.tab]?.includes(opt.label) ?? false
|
{language.t("ui.common.back")}
|
||||||
return (
|
</Button>
|
||||||
<button
|
</Show>
|
||||||
data-slot="question-option"
|
<Button variant={last() ? "primary" : "secondary"} size="large" disabled={store.sending} onClick={next}>
|
||||||
data-picked={picked()}
|
{last() ? language.t("ui.common.submit") : language.t("ui.common.next")}
|
||||||
role={multi() ? "checkbox" : "radio"}
|
</Button>
|
||||||
aria-checked={picked()}
|
</div>
|
||||||
disabled={store.sending}
|
</>
|
||||||
onClick={() => selectOption(i())}
|
}
|
||||||
>
|
>
|
||||||
<span data-slot="question-option-check" aria-hidden="true">
|
<div data-slot="question-text">{question()?.question}</div>
|
||||||
<span
|
<Show when={multi()} fallback={<div data-slot="question-hint">{language.t("ui.question.singleHint")}</div>}>
|
||||||
data-slot="question-option-box"
|
<div data-slot="question-hint">{language.t("ui.question.multiHint")}</div>
|
||||||
data-type={multi() ? "checkbox" : "radio"}
|
</Show>
|
||||||
data-picked={picked()}
|
<div data-slot="question-options">
|
||||||
>
|
<For each={options()}>
|
||||||
<Show when={multi()} fallback={<span data-slot="question-option-radio-dot" />}>
|
{(opt, i) => {
|
||||||
<Icon name="check-small" size="small" />
|
const picked = () => store.answers[store.tab]?.includes(opt.label) ?? false
|
||||||
</Show>
|
return (
|
||||||
</span>
|
<button
|
||||||
</span>
|
|
||||||
<span data-slot="question-option-main">
|
|
||||||
<span data-slot="option-label">{opt.label}</span>
|
|
||||||
<Show when={opt.description}>
|
|
||||||
<span data-slot="option-description">{opt.description}</span>
|
|
||||||
</Show>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</For>
|
|
||||||
|
|
||||||
<Show
|
|
||||||
when={store.editing}
|
|
||||||
fallback={
|
|
||||||
<button
|
|
||||||
data-slot="question-option"
|
|
||||||
data-custom="true"
|
|
||||||
data-picked={on()}
|
|
||||||
role={multi() ? "checkbox" : "radio"}
|
|
||||||
aria-checked={on()}
|
|
||||||
disabled={store.sending}
|
|
||||||
onClick={customOpen}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
data-slot="question-option-check"
|
|
||||||
aria-hidden="true"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
customToggle()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span data-slot="question-option-box" data-type={multi() ? "checkbox" : "radio"} data-picked={on()}>
|
|
||||||
<Show when={multi()} fallback={<span data-slot="question-option-radio-dot" />}>
|
|
||||||
<Icon name="check-small" size="small" />
|
|
||||||
</Show>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span data-slot="question-option-main">
|
|
||||||
<span data-slot="option-label">{language.t("ui.messagePart.option.typeOwnAnswer")}</span>
|
|
||||||
<span data-slot="option-description">
|
|
||||||
{input() || language.t("ui.question.custom.placeholder")}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<form
|
|
||||||
data-slot="question-option"
|
data-slot="question-option"
|
||||||
data-custom="true"
|
data-picked={picked()}
|
||||||
data-picked={on()}
|
|
||||||
role={multi() ? "checkbox" : "radio"}
|
role={multi() ? "checkbox" : "radio"}
|
||||||
aria-checked={on()}
|
aria-checked={picked()}
|
||||||
onMouseDown={(e) => {
|
disabled={store.sending}
|
||||||
if (store.sending) {
|
onClick={() => selectOption(i())}
|
||||||
e.preventDefault()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (e.target instanceof HTMLTextAreaElement) return
|
|
||||||
const input = e.currentTarget.querySelector('[data-slot="question-custom-input"]')
|
|
||||||
if (input instanceof HTMLTextAreaElement) input.focus()
|
|
||||||
}}
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
commitCustom()
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<span
|
<span data-slot="question-option-check" aria-hidden="true">
|
||||||
data-slot="question-option-check"
|
<span
|
||||||
aria-hidden="true"
|
data-slot="question-option-box"
|
||||||
onClick={(e) => {
|
data-type={multi() ? "checkbox" : "radio"}
|
||||||
e.preventDefault()
|
data-picked={picked()}
|
||||||
e.stopPropagation()
|
>
|
||||||
customToggle()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span data-slot="question-option-box" data-type={multi() ? "checkbox" : "radio"} data-picked={on()}>
|
|
||||||
<Show when={multi()} fallback={<span data-slot="question-option-radio-dot" />}>
|
<Show when={multi()} fallback={<span data-slot="question-option-radio-dot" />}>
|
||||||
<Icon name="check-small" size="small" />
|
<Icon name="check-small" size="small" />
|
||||||
</Show>
|
</Show>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span data-slot="question-option-main">
|
<span data-slot="question-option-main">
|
||||||
<span data-slot="option-label">{language.t("ui.messagePart.option.typeOwnAnswer")}</span>
|
<span data-slot="option-label">{opt.label}</span>
|
||||||
<textarea
|
<Show when={opt.description}>
|
||||||
ref={(el) =>
|
<span data-slot="option-description">{opt.description}</span>
|
||||||
setTimeout(() => {
|
</Show>
|
||||||
el.focus()
|
|
||||||
el.style.height = "0px"
|
|
||||||
el.style.height = `${el.scrollHeight}px`
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
data-slot="question-custom-input"
|
|
||||||
placeholder={language.t("ui.question.custom.placeholder")}
|
|
||||||
value={input()}
|
|
||||||
rows={1}
|
|
||||||
disabled={store.sending}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
e.preventDefault()
|
|
||||||
setStore("editing", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (e.key !== "Enter" || e.shiftKey) return
|
|
||||||
e.preventDefault()
|
|
||||||
commitCustom()
|
|
||||||
}}
|
|
||||||
onInput={(e) => {
|
|
||||||
customUpdate(e.currentTarget.value)
|
|
||||||
e.currentTarget.style.height = "0px"
|
|
||||||
e.currentTarget.style.height = `${e.currentTarget.scrollHeight}px`
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
</form>
|
</button>
|
||||||
</Show>
|
)
|
||||||
</div>
|
}}
|
||||||
</div>
|
</For>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-slot="question-footer">
|
<Show
|
||||||
<Button variant="ghost" size="large" disabled={store.sending} onClick={reject}>
|
when={store.editing}
|
||||||
{language.t("ui.common.dismiss")}
|
fallback={
|
||||||
</Button>
|
<button
|
||||||
<div data-slot="question-footer-actions">
|
data-slot="question-option"
|
||||||
<Show when={store.tab > 0}>
|
data-custom="true"
|
||||||
<Button variant="secondary" size="large" disabled={store.sending} onClick={back}>
|
data-picked={on()}
|
||||||
{language.t("ui.common.back")}
|
role={multi() ? "checkbox" : "radio"}
|
||||||
</Button>
|
aria-checked={on()}
|
||||||
</Show>
|
disabled={store.sending}
|
||||||
<Button variant={last() ? "primary" : "secondary"} size="large" disabled={store.sending} onClick={next}>
|
onClick={customOpen}
|
||||||
{last() ? language.t("ui.common.submit") : language.t("ui.common.next")}
|
>
|
||||||
</Button>
|
<span
|
||||||
</div>
|
data-slot="question-option-check"
|
||||||
|
aria-hidden="true"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
customToggle()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span data-slot="question-option-box" data-type={multi() ? "checkbox" : "radio"} data-picked={on()}>
|
||||||
|
<Show when={multi()} fallback={<span data-slot="question-option-radio-dot" />}>
|
||||||
|
<Icon name="check-small" size="small" />
|
||||||
|
</Show>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span data-slot="question-option-main">
|
||||||
|
<span data-slot="option-label">{language.t("ui.messagePart.option.typeOwnAnswer")}</span>
|
||||||
|
<span data-slot="option-description">{input() || language.t("ui.question.custom.placeholder")}</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
data-slot="question-option"
|
||||||
|
data-custom="true"
|
||||||
|
data-picked={on()}
|
||||||
|
role={multi() ? "checkbox" : "radio"}
|
||||||
|
aria-checked={on()}
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
if (store.sending) {
|
||||||
|
e.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (e.target instanceof HTMLTextAreaElement) return
|
||||||
|
const input = e.currentTarget.querySelector('[data-slot="question-custom-input"]')
|
||||||
|
if (input instanceof HTMLTextAreaElement) input.focus()
|
||||||
|
}}
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
commitCustom()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
data-slot="question-option-check"
|
||||||
|
aria-hidden="true"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
customToggle()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span data-slot="question-option-box" data-type={multi() ? "checkbox" : "radio"} data-picked={on()}>
|
||||||
|
<Show when={multi()} fallback={<span data-slot="question-option-radio-dot" />}>
|
||||||
|
<Icon name="check-small" size="small" />
|
||||||
|
</Show>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span data-slot="question-option-main">
|
||||||
|
<span data-slot="option-label">{language.t("ui.messagePart.option.typeOwnAnswer")}</span>
|
||||||
|
<textarea
|
||||||
|
ref={(el) =>
|
||||||
|
setTimeout(() => {
|
||||||
|
el.focus()
|
||||||
|
el.style.height = "0px"
|
||||||
|
el.style.height = `${el.scrollHeight}px`
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
data-slot="question-custom-input"
|
||||||
|
placeholder={language.t("ui.question.custom.placeholder")}
|
||||||
|
value={input()}
|
||||||
|
rows={1}
|
||||||
|
disabled={store.sending}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
e.preventDefault()
|
||||||
|
setStore("editing", false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (e.key !== "Enter" || e.shiftKey) return
|
||||||
|
e.preventDefault()
|
||||||
|
commitCustom()
|
||||||
|
}}
|
||||||
|
onInput={(e) => {
|
||||||
|
customUpdate(e.currentTarget.value)
|
||||||
|
e.currentTarget.style.height = "0px"
|
||||||
|
e.currentTarget.style.height = `${e.currentTarget.scrollHeight}px`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</DockPrompt>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { For, Show, createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
|
import { For, Show, createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
|
||||||
import type { QuestionRequest, Todo } from "@opencode-ai/sdk/v2"
|
import type { QuestionRequest, Todo } from "@opencode-ai/sdk/v2"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
|
import { DockPrompt } from "@opencode-ai/ui/dock-prompt"
|
||||||
import { PromptInput } from "@/components/prompt-input"
|
import { PromptInput } from "@/components/prompt-input"
|
||||||
import { QuestionDock } from "@/components/question-dock"
|
import { QuestionDock } from "@/components/question-dock"
|
||||||
import { SessionTodoDock } from "@/components/session-todo-dock"
|
import { SessionTodoDock } from "@/components/session-todo-dock"
|
||||||
@@ -139,64 +140,57 @@ export function SessionPromptDock(props: {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div data-component="question-prompt" data-permission="true">
|
<DockPrompt
|
||||||
<div data-slot="question-body">
|
kind="permission"
|
||||||
<div data-slot="question-header">
|
header={
|
||||||
<div data-slot="question-header-title">
|
<>
|
||||||
{props.t("notification.permission.title")}{" "}
|
<div data-slot="permission-header-title">{props.t("notification.permission.title")}</div>
|
||||||
<span class="text-13-regular text-text-weak">{toolTitle()}</span>
|
</>
|
||||||
|
}
|
||||||
|
footer={
|
||||||
|
<>
|
||||||
|
<div />
|
||||||
|
<div data-slot="permission-footer-actions">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="normal"
|
||||||
|
onClick={() => props.onDecide("reject")}
|
||||||
|
disabled={props.responding}
|
||||||
|
>
|
||||||
|
{props.t("ui.permission.deny")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="normal"
|
||||||
|
onClick={() => props.onDecide("always")}
|
||||||
|
disabled={props.responding}
|
||||||
|
>
|
||||||
|
{props.t("ui.permission.allowAlways")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="normal"
|
||||||
|
onClick={() => props.onDecide("once")}
|
||||||
|
disabled={props.responding}
|
||||||
|
>
|
||||||
|
{props.t("ui.permission.allowOnce")}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Show when={toolDescription()}>
|
||||||
|
<div data-slot="permission-hint">{toolDescription()}</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
<div data-slot="question-content">
|
<Show when={perm.patterns.length > 0}>
|
||||||
<Show when={toolDescription()}>
|
<div data-slot="permission-patterns">
|
||||||
<div data-slot="question-hint">{toolDescription()}</div>
|
<For each={perm.patterns}>
|
||||||
</Show>
|
{(pattern) => <code class="text-12-regular text-text-base break-all">{pattern}</code>}
|
||||||
|
</For>
|
||||||
<Show when={perm.patterns.length > 0}>
|
|
||||||
<div data-slot="question-options">
|
|
||||||
<For each={perm.patterns}>
|
|
||||||
{(pattern) => (
|
|
||||||
<div class="px-[10px]">
|
|
||||||
<code class="text-12-regular text-text-base break-all">{pattern}</code>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Show>
|
||||||
|
</DockPrompt>
|
||||||
<div data-slot="question-footer">
|
|
||||||
<div />
|
|
||||||
<div data-slot="question-footer-actions">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="normal"
|
|
||||||
onClick={() => props.onDecide("reject")}
|
|
||||||
disabled={props.responding}
|
|
||||||
>
|
|
||||||
{props.t("ui.permission.deny")}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
size="normal"
|
|
||||||
onClick={() => props.onDecide("always")}
|
|
||||||
disabled={props.responding}
|
|
||||||
>
|
|
||||||
{props.t("ui.permission.allowAlways")}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
size="normal"
|
|
||||||
onClick={() => props.onDecide("once")}
|
|
||||||
disabled={props.responding}
|
|
||||||
>
|
|
||||||
{props.t("ui.permission.allowOnce")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|||||||
21
packages/ui/src/components/dock-prompt.tsx
Normal file
21
packages/ui/src/components/dock-prompt.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import type { JSX } from "solid-js"
|
||||||
|
|
||||||
|
export function DockPrompt(props: {
|
||||||
|
kind: "question" | "permission"
|
||||||
|
header: JSX.Element
|
||||||
|
children: JSX.Element
|
||||||
|
footer: JSX.Element
|
||||||
|
ref?: (el: HTMLDivElement) => void
|
||||||
|
}) {
|
||||||
|
const slot = (name: string) => `${props.kind}-${name}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-component="dock-prompt" data-kind={props.kind} ref={props.ref}>
|
||||||
|
<div data-slot={slot("body")}>
|
||||||
|
<div data-slot={slot("header")}>{props.header}</div>
|
||||||
|
<div data-slot={slot("content")}>{props.children}</div>
|
||||||
|
</div>
|
||||||
|
<div data-slot={slot("footer")}>{props.footer}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -753,7 +753,114 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-component="question-prompt"] {
|
[data-component="dock-prompt"][data-kind="permission"] {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
min-height: 0;
|
||||||
|
max-height: 100dvh;
|
||||||
|
|
||||||
|
[data-slot="permission-body"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 8px 8px 0;
|
||||||
|
background-color: var(--surface-raised-stronger-non-alpha);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: var(--shadow-xs-border);
|
||||||
|
overflow: clip;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="permission-header"] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="permission-header-title"] {
|
||||||
|
font-family: var(--font-family-sans);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
line-height: var(--line-height-large);
|
||||||
|
color: var(--text-strong);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="permission-content"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="permission-hint"] {
|
||||||
|
font-family: var(--font-family-sans);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: var(--font-weight-regular);
|
||||||
|
line-height: var(--line-height-large);
|
||||||
|
color: var(--text-weak);
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="permission-patterns"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 1px 10px 8px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: var(--line-height-large);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="permission-footer"] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 32px 8px 8px;
|
||||||
|
background-color: var(--background-base);
|
||||||
|
border: 1px solid var(--border-weak-base);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: clip;
|
||||||
|
margin-top: -24px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="permission-footer-actions"] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
[data-component="button"] {
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:is([data-component="question-prompt"], [data-component="dock-prompt"][data-kind="question"]) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -761,22 +868,6 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
max-height: var(--question-prompt-max-height, 100dvh);
|
max-height: var(--question-prompt-max-height, 100dvh);
|
||||||
|
|
||||||
&[data-permission="true"] {
|
|
||||||
[data-slot="question-options"] {
|
|
||||||
code {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: var(--line-height-large);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-slot="question-footer-actions"] {
|
|
||||||
[data-component="button"] {
|
|
||||||
padding-left: 12px;
|
|
||||||
padding-right: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-slot="question-body"] {
|
[data-slot="question-body"] {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
Reference in New Issue
Block a user