Compare commits

...

4 Commits

Author SHA1 Message Date
Gregory Schier 95ac3e310a Improve response history menu (#492) 2026-07-02 10:04:57 -07:00
Gregory Schier 9b524e3dc7 Upgrade Tailwind to v4 (#491) 2026-07-02 09:53:22 -07:00
Gregory Schier bdf78254b5 Disable automatic Flathub workflow 2026-07-01 13:51:00 -07:00
Gregory Schier c5545c8781 Document release note contributor attribution 2026-07-01 13:39:05 -07:00
123 changed files with 1107 additions and 1108 deletions
@@ -19,10 +19,12 @@ Generate formatted markdown release notes for a Yaak tag.
- `gh pr view <PR_NUMBER> --json number,title,body,author,url`
5. Extract useful details:
- Feedback URLs (`feedback.yaak.app`)
- Contributor GitHub handles from `author.login`
- Plugin install links or other notable context
6. Format notes using Yaak style:
- Changelog badge at top
- Bulleted items with PR links where available
- Contributor handles for external PRs
- Feedback links where available
- Full changelog compare link at bottom
@@ -31,6 +33,7 @@ Generate formatted markdown release notes for a Yaak tag.
- Wrap final notes in a markdown code fence.
- Keep a blank line before and after the code fence.
- Output the markdown code block last.
- Append contributor attribution to PR-backed bullets for non-`@gschier` authors, using `by [@handle](https://github.com/handle)`.
- Do not append `by @gschier` for PRs authored by `@gschier`.
- These are app release notes. Exclude CLI-only changes (commits prefixed with `cli:` or only touching `crates-cli/`) since the CLI has its own release process.
+9 -6
View File
@@ -1,7 +1,12 @@
name: Update Flathub
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: Release tag to publish to Flathub
required: true
type: string
permissions:
contents: read
@@ -10,8 +15,6 @@ jobs:
update-flathub:
name: Update Flathub manifest
runs-on: ubuntu-latest
# Only run for stable releases (skip betas/pre-releases)
if: ${{ !github.event.release.prerelease }}
steps:
- name: Checkout app repo
uses: actions/checkout@v4
@@ -39,7 +42,7 @@ jobs:
git clone --depth 1 https://github.com/flatpak/flatpak-builder-tools flatpak/flatpak-builder-tools
- name: Run update-manifest.sh
run: bash flatpak/update-manifest.sh "${{ github.event.release.tag_name }}" flathub-repo
run: bash flatpak/update-manifest.sh "${{ inputs.tag }}" flathub-repo
- name: Commit and push to Flathub
working-directory: flathub-repo
@@ -48,5 +51,5 @@ jobs:
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git diff --cached --quiet && echo "No changes to commit" && exit 0
git commit -m "Update to ${{ github.event.release.tag_name }}"
git commit -m "Update to ${{ inputs.tag }}"
git push
@@ -10,7 +10,7 @@ export function openFolderSettings(folderId: string, tab?: FolderSettingsTab) {
id: "folder-settings",
title: null,
size: "lg",
className: "h-[50rem]",
className: "h-200",
noPadding: true,
render: () => <FolderSettingsDialog folderId={folderId} tab={tab} />,
});
@@ -38,7 +38,7 @@ export function BinaryFileEditor({
<VStack space={2}>
<SelectFile onChange={handleChange} filePath={filePath} />
{filePath != null && mimeType !== contentType && !ignoreContentType.value && (
<Banner className="mt-3 !py-5">
<Banner className="mt-3 py-5!">
<div className="mb-4 text-center">
<div>Set Content-Type header</div>
<InlineCode>{mimeType}</InlineCode> for current request?
@@ -108,7 +108,7 @@ export function CloneGitRepositoryDialog({ hide }: Props) {
rightSlot={
<IconButton
size="xs"
className="mr-0.5 !h-auto my-0.5"
className="mr-0.5 h-auto! my-0.5"
icon="folder"
title="Browse"
onClick={handleSelectDirectory}
@@ -11,7 +11,7 @@ export function ColorIndicator({ color, onClick, className }: Props) {
const style: CSSProperties = { backgroundColor: color ?? undefined };
const finalClassName = classNames(
className,
"inline-block w-[0.75em] h-[0.75em] rounded-full mr-1.5 border border-transparent flex-shrink-0",
"inline-block w-[0.75em] h-[0.75em] rounded-full mr-1.5 border border-transparent shrink-0",
);
if (onClick) {
@@ -439,7 +439,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
name="command"
label="Command"
placeholder="Search or type a command"
className="font-sans !text-base"
className="font-sans text-base!"
defaultValue={command}
onChange={handleSetCommand}
onKeyDownCapture={handleKeyDown}
@@ -448,7 +448,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
<div className="h-full px-1.5 overflow-y-auto pt-2 pb-1">
{filteredGroups.map((g) => (
<div key={g.key} className="mb-1.5 w-full">
<Heading level={2} className="!text-xs uppercase px-1.5 h-sm flex items-center">
<Heading level={2} className="text-xs! uppercase px-1.5 h-sm flex items-center">
{g.label}
</Heading>
{g.items.map((v) => (
@@ -491,7 +491,7 @@ function CommandPaletteItem({
color="custom"
justify="start"
className={classNames(
"w-full h-sm flex items-center rounded px-1.5",
"w-full h-sm flex items-center rounded-sm px-1.5",
"hover:text-text",
active && "bg-surface-highlight",
!active && "text-text-subtle",
+5 -5
View File
@@ -155,7 +155,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => {
rightSlot={
filter.length > 0 && (
<IconButton
className="!bg-transparent !h-auto min-h-full opacity-50 hover:opacity-100 -mr-1"
className="bg-transparent! h-auto! min-h-full opacity-50 hover:opacity-100 -mr-1"
icon="x"
title="Clear filter"
onClick={() => {
@@ -239,7 +239,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => {
<TableCell className={classNames("pl-2", isSelected && "rounded-l")}>
{c.name}
</TableCell>
<TruncatedWideTableCell className="min-w-[10rem]">
<TruncatedWideTableCell className="min-w-40">
{c.value}
</TruncatedWideTableCell>
<TableCell>{cookieDomain(c)}</TableCell>
@@ -547,7 +547,7 @@ function CookieEditor({
}
function CookieKeyValueRow({ labelClassName, ...props }: ComponentProps<typeof KeyValueRow>) {
return <KeyValueRow labelClassName={classNames("w-[7rem]", labelClassName)} {...props} />;
return <KeyValueRow labelClassName={classNames("w-28", labelClassName)} {...props} />;
}
function CookieTextInput({
@@ -589,7 +589,7 @@ function CookieTextarea({ onChange, value }: { onChange: (value: string) => void
<textarea
autoCapitalize="off"
autoCorrect="off"
className={classNames(cookieInputClassName, "min-h-[5rem] resize-y")}
className={classNames(cookieInputClassName, "min-h-20 resize-y")}
onChange={(event) => onChange(event.target.value)}
value={value}
/>
@@ -600,7 +600,7 @@ const NEW_COOKIE_KEY = "__new-cookie__";
const NON_EMPTY_INPUT_PATTERN = ".*\\S.*";
const cookieInputClassName = classNames(
"x-theme-input w-full min-w-0 min-h-sm rounded-md bg-transparent",
"border border-border-subtle outline-none",
"border border-border-subtle outline-hidden",
"px-2 text-xs font-mono cursor-text placeholder:text-placeholder",
"focus:border-border-focus invalid:border-danger",
"disabled:opacity-disabled disabled:border-dotted",
@@ -75,7 +75,7 @@ export function DnsOverridesEditor({ workspace }: Props) {
<VStack space={3} className="pb-3">
<div className="text-text-subtle text-sm">
Override DNS resolution for specific hostnames. This works like{" "}
<code className="text-text-subtlest bg-surface-highlight px-1 rounded">/etc/hosts</code> but
<code className="text-text-subtlest bg-surface-highlight px-1 rounded-sm">/etc/hosts</code> but
only for requests made from this workspace.
</div>
+2 -2
View File
@@ -23,8 +23,8 @@ export const DropMarker = memo(
<div
className={classNames(
"absolute bg-primary rounded-full",
orientation === "horizontal" && "left-2 right-2 -bottom-[0.1rem] h-[0.2rem]",
orientation === "vertical" && "-left-[0.1rem] top-0 bottom-0 w-[0.2rem]",
orientation === "horizontal" && "left-2 right-2 bottom-[-0.1rem] h-[0.2rem]",
orientation === "vertical" && "left-[-0.1rem] top-0 bottom-0 w-[0.2rem]",
)}
/>
</div>
+5 -5
View File
@@ -204,7 +204,7 @@ function FormInputs<T extends Record<string, JsonPrimitive>>({
<div key={i + stateKey}>
<DetailsBanner
summary={input.label}
className={classNames("!mb-auto", disabled && "opacity-disabled")}
className={classNames("mb-auto!", disabled && "opacity-disabled")}
>
<div className="mt-3">
<FormInputsStack
@@ -300,7 +300,7 @@ function TextArg({
onChange,
name: arg.name,
multiLine: arg.multiLine,
className: arg.multiLine ? "min-h-[4rem]" : undefined,
className: arg.multiLine ? "min-h-16" : undefined,
defaultValue: value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value,
required: !arg.optional,
disabled: arg.disabled,
@@ -359,7 +359,7 @@ function EditorArg({
className={classNames(
"border border-border rounded-md overflow-hidden px-2 py-1",
"focus-within:border-border-focus",
!arg.rows && "max-h-[10rem]", // So it doesn't take up too much space
!arg.rows && "max-h-40", // So it doesn't take up too much space
)}
style={arg.rows ? { height: `${arg.rows * 1.4 + 0.75}rem` } : undefined}
>
@@ -372,7 +372,7 @@ function EditorArg({
onChange={onChange}
hideGutter
heightMode="auto"
className="min-h-[3rem]"
className="min-h-12"
defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value}
placeholder={arg.placeholder ?? undefined}
autocompleteFunctions={autocompleteFunctions}
@@ -392,7 +392,7 @@ function EditorArg({
id: "id",
size: "full",
title: arg.readOnly ? "View Value" : "Edit Value",
className: "!max-w-[50rem] !max-h-[60rem]",
className: "max-w-200! max-h-240!",
description: arg.label && (
<Label
htmlFor={id}
@@ -62,7 +62,7 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo
size="sm"
className={classNames(
className,
"text !px-2 truncate",
"text px-2! truncate",
!activeEnvironment && !hasBaseVars && "text-text-subtlest italic",
)}
// If no environments, the button simply opens the dialog.
@@ -57,7 +57,7 @@ export function EnvironmentEditDialog({ initialEnvironmentId, setRef }: Props) {
defaultRatio={0.75}
layout="horizontal"
className="gap-0"
resizeHandleClassName="-translate-x-[1px]"
resizeHandleClassName="-translate-x-px"
firstSlot={() => (
<EnvironmentEditDialogSidebar
selectedEnvironmentId={selectedEnvironment?.id ?? null}
+2 -2
View File
@@ -163,7 +163,7 @@ function HttpRequestCard({ request }: { request: HttpRequest }) {
return (
<div className="grid grid-rows-2 grid-cols-[minmax(0,1fr)] gap-2 overflow-hidden">
<code className="font-mono text-editor text-info border border-info rounded px-2.5 py-0.5 truncate w-full min-w-0">
<code className="font-mono text-editor text-info border border-info rounded-sm px-2.5 py-0.5 truncate w-full min-w-0">
{request.method} {request.url}
</code>
{latestResponse ? (
@@ -190,7 +190,7 @@ function HttpRequestCard({ request }: { request: HttpRequest }) {
className={classNames(
"cursor-default select-none",
"whitespace-nowrap w-full pl-3 overflow-x-auto font-mono text-sm hide-scrollbars",
"font-mono text-editor border rounded px-1.5 py-0.5 truncate w-full",
"font-mono text-editor border rounded-sm px-1.5 py-0.5 truncate w-full",
)}
>
{latestResponse.state !== "closed" && <LoadingIcon size="sm" />}
@@ -84,12 +84,12 @@ export function FolderSettingsDialog({ folderId, tab }: Props) {
return (
<div className="h-full flex flex-col">
<div className="flex items-center gap-3 px-6 pr-10 mt-4 mb-2 min-w-0 text-xl">
<Icon icon="folder_cog" size="lg" color="secondary" className="flex-shrink-0" />
<Icon icon="folder_cog" size="lg" color="secondary" className="shrink-0" />
<div className="flex items-center gap-1.5 font-semibold text-text min-w-0 overflow-hidden flex-1">
{breadcrumbs.map((item, index) => (
<Fragment key={item.id}>
{index > 0 && (
<Icon icon="chevron_right" size="lg" className="opacity-50 flex-shrink-0" />
<Icon icon="chevron_right" size="lg" className="opacity-50 shrink-0" />
)}
<span className="text-text-subtle truncate min-w-0" title={item.name}>
{item.name}
@@ -97,7 +97,7 @@ export function FolderSettingsDialog({ folderId, tab }: Props) {
</Fragment>
))}
{breadcrumbs.length > 0 && (
<Icon icon="chevron_right" size="lg" className="opacity-50 flex-shrink-0" />
<Icon icon="chevron_right" size="lg" className="opacity-50 shrink-0" />
)}
<span className="whitespace-nowrap" title={folder.name}>
{folder.name}
@@ -149,7 +149,7 @@ export function FolderSettingsDialog({ folderId, tab }: Props) {
<InlineCode className="flex gap-1 items-center text-primary pl-2.5">
{folder.id}
<CopyIconButton
className="opacity-70 !text-primary"
className="opacity-70 text-primary!"
size="2xs"
iconSize="sm"
title="Copy folder ID"
+1 -1
View File
@@ -126,7 +126,7 @@ export function GrpcEditor({
const actions = useMemo(
() => [
<div key="reflection" className={classNames(services == null && "!opacity-100")}>
<div key="reflection" className={classNames(services == null && "opacity-100!")}>
<Button
size="xs"
color={
@@ -162,7 +162,7 @@ export function GrpcRequestPane({
className={classNames(
"grid grid-cols-[minmax(0,1fr)_auto] gap-1.5",
paneWidth === 0 && "opacity-0",
paneWidth > 0 && paneWidth < 400 && "!grid-cols-1",
paneWidth > 0 && paneWidth < 400 && "grid-cols-1!",
)}
>
<UrlBar
@@ -201,7 +201,7 @@ export function GrpcRequestPane({
rightSlot={<Icon size="sm" icon="chevron_down" />}
disabled={isStreaming || services == null}
className={classNames(
"font-mono text-editor min-w-[5rem] !ring-0",
"font-mono text-editor min-w-20 ring-0!",
paneWidth < 400 && "flex-1",
)}
>
@@ -259,7 +259,7 @@ export function GrpcRequestPane({
<Tabs
label="Request"
tabs={tabs}
tabListClassName="mt-1 !mb-1.5"
tabListClassName="mt-1 mb-1.5!"
storageKey="grpc_request_tabs"
activeTabKey={activeRequest.id}
>
@@ -296,7 +296,7 @@ export function GrpcRequestPane({
hideLabel
forceUpdateKey={forceUpdateKey}
defaultValue={activeRequest.name}
className="font-sans !text-xl !px-0"
className="font-sans text-xl! px-0!"
containerClassName="border-0"
placeholder={resolvedModelName(activeRequest)}
onChange={(name) => patchModel(activeRequest, { name })}
@@ -156,7 +156,7 @@ export function HttpAuthenticationEditor({ model }: Props) {
title="Authentication Actions"
icon="settings"
size="xs"
className="!text-secondary"
className="text-secondary!"
/>
</Dropdown>
)}
@@ -57,7 +57,7 @@ export function HttpRequestLayout({ activeRequest, style }: Props) {
<GraphQLDocsExplorer
requestId={activeRequest.id}
schema={graphQLSchema}
className={classNames(orientation === "horizontal" && "!ml-0")}
className={classNames(orientation === "horizontal" && "ml-0!")}
style={style}
/>
)}
@@ -346,7 +346,7 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
onUrlChange={handleUrlChange}
leftSlot={
<div className="py-0.5">
<RequestMethodDropdown request={activeRequest} className="ml-0.5 !h-full" />
<RequestMethodDropdown request={activeRequest} className="ml-0.5 h-full!" />
</div>
}
forceUpdateKey={updateKey}
@@ -456,7 +456,7 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
hideLabel
forceUpdateKey={updateKey}
defaultValue={activeRequest.name}
className="font-sans !text-xl !px-0"
className="font-sans text-xl! px-0!"
containerClassName="border-0"
placeholder={resolvedModelName(activeRequest)}
onChange={(name) => patchModel(activeRequest, { name })}
@@ -4,10 +4,12 @@ import classNames from "classnames";
import type { ComponentType, CSSProperties } from "react";
import { lazy, Suspense, useMemo } from "react";
import { useCancelHttpResponse } from "../hooks/useCancelHttpResponse";
import { useCopyHttpResponse } from "../hooks/useCopyHttpResponse";
import { useHttpResponseEvents } from "../hooks/useHttpResponseEvents";
import { usePinnedHttpResponse } from "../hooks/usePinnedHttpResponse";
import { useResponseBodyBytes, useResponseBodyText } from "../hooks/useResponseBodyText";
import { useResponseViewMode } from "../hooks/useResponseViewMode";
import { useSaveResponse } from "../hooks/useSaveResponse";
import { useTimelineViewMode } from "../hooks/useTimelineViewMode";
import { getMimeTypeFromContentType } from "../lib/contentType";
import { getContentTypeFromHeaders, getCookieCounts } from "../lib/model_util";
@@ -78,6 +80,8 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
activeResponse?.state === "closed" && redirectDropWarning != null;
const cookieCounts = useMemo(() => getCookieCounts(responseEvents.data), [responseEvents.data]);
const saveResponse = useSaveResponse(activeResponse ?? null);
const copyResponse = useCopyHttpResponse(activeResponse ?? null);
const tabs = useMemo<TabItem[]>(
() => [
@@ -93,6 +97,22 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
? []
: [{ label: "Response (Raw)", shortLabel: "Raw", value: "raw" }]),
],
itemsAfter: [
{
label: "Save to File",
onSelect: saveResponse.mutate,
leftSlot: <Icon icon="save" />,
hidden: activeResponse == null || !!activeResponse.error,
disabled: activeResponse?.state !== "closed" && (activeResponse?.status ?? 0) >= 100,
},
{
label: "Copy Body",
onSelect: copyResponse.mutate,
leftSlot: <Icon icon="copy" />,
hidden: activeResponse == null || !!activeResponse.error,
disabled: activeResponse?.state !== "closed" && (activeResponse?.status ?? 0) >= 100,
},
],
},
},
{
@@ -135,12 +155,18 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
],
[
activeResponse?.headers,
activeResponse,
activeResponse?.error,
activeResponse?.requestContentLength,
activeResponse?.requestHeaders.length,
activeResponse?.state,
activeResponse?.status,
cookieCounts.sent,
cookieCounts.received,
copyResponse.mutate,
mimeType,
responseEvents.data?.length,
saveResponse.mutate,
setViewMode,
viewMode,
timelineViewMode,
@@ -167,7 +193,7 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
<div className="h-full w-full grid grid-rows-[auto_minmax(0,1fr)] grid-cols-1">
<HStack
className={classNames(
"text-text-subtle w-full flex-shrink-0",
"text-text-subtle w-full shrink-0",
// Remove a bit of space because the tabs have lots too
"-mb-1.5",
)}
@@ -180,7 +206,7 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
"whitespace-nowrap w-full pl-3 overflow-x-auto font-mono text-sm hide-scrollbars",
)}
>
<HStack space={2} className="w-full flex-shrink-0">
<HStack space={2} className="w-full shrink-0">
{activeResponse.state !== "closed" && <LoadingIcon size="sm" />}
<HttpStatusTag showReason response={activeResponse} />
<span>&bull;</span>
@@ -194,7 +220,7 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
{shouldShowRedirectDropWarning ? (
<Tooltip
tabIndex={0}
className="my-auto pl-3 flex-shrink-0 max-w-full justify-self-end overflow-hidden"
className="my-auto pl-3 shrink-0 max-w-full justify-self-end overflow-hidden"
content={
<VStack alignItems="start" space={1} className="text-xs">
<span className="font-medium text-warning">
@@ -223,7 +249,7 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
<span className="inline-flex min-w-0">
<PillButton
color="warning"
className="font-sans text-sm !flex-shrink max-w-full"
className="font-sans text-sm shrink! max-w-full"
innerClassName="flex items-center"
leftSlot={<Icon icon="alert_triangle" size="xs" color="warning" />}
>
@@ -236,7 +262,7 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
) : (
<span />
)}
<div className="justify-self-end flex-shrink-0">
<div className="justify-self-end shrink-0">
<RecentHttpResponsesDropdown
responses={responses}
activeResponse={activeResponse}
@@ -249,7 +275,7 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
<div className="overflow-hidden flex flex-col min-h-0">
{activeResponse?.error && (
<Banner color="danger" className="mx-3 mt-1 flex-shrink-0">
<Banner color="danger" className="mx-3 mt-1 shrink-0">
{activeResponse.error}
</Banner>
)}
@@ -73,14 +73,14 @@ export function JsonBodyEditor({ forceUpdateKey, heightMode, request }: Props) {
const actions = useMemo<EditorProps["actions"]>(
() => [
showBanner && (
<Banner color="notice" className="!opacity-100 h-sm !py-0 !px-2 flex items-center text-xs">
<Banner color="notice" className="opacity-100! h-sm py-0! px-2! flex items-center text-xs">
<p className="inline-flex items-center gap-1 min-w-0">
<span className="truncate">Auto-fix enabled</span>
<Icon icon="arrow_right" size="sm" className="opacity-disabled" />
</p>
</Banner>
),
<div key="settings" className="!opacity-100 !shadow">
<div key="settings" className="opacity-100! shadow!">
<Dropdown
onOpen={handleDropdownOpen}
items={
+1 -1
View File
@@ -59,7 +59,7 @@ function getDetail(
label: `License expired ${formatDate(data.data.periodEnd, "MMM dd, yyyy")}`,
},
{
label: <div className="min-w-[12rem]">Renew License</div>,
label: <div className="min-w-48">Renew License</div>,
leftSlot: <Icon icon="refresh" />,
rightSlot: <Icon icon="external_link" size="sm" className="opacity-disabled" />,
hidden: data.data.changesUrl == null,
@@ -33,7 +33,7 @@ export function MarkdownEditor({
<Editor
hideGutter
wrapLines
className={classNames(editorClassName, "[&_.cm-line]:!max-w-lg max-h-full")}
className={classNames(editorClassName, "[&_.cm-line]:max-w-lg! max-h-full")}
language="markdown"
defaultValue={defaultValue}
onChange={onChange}
@@ -46,7 +46,7 @@ export function MarkdownEditor({
defaultValue.length === 0 ? (
<p className="text-text-subtlest">No description</p>
) : (
<div className="pr-1.5 overflow-y-auto max-h-full [&_*]:cursor-auto [&_*]:select-auto">
<div className="pr-1.5 overflow-y-auto max-h-full **:cursor-auto **:select-auto">
<Markdown className="max-w-lg select-auto cursor-auto">{defaultValue}</Markdown>
</div>
);
@@ -539,7 +539,7 @@ function NumberUnitInput({
placeholder={placeholder}
defaultValue={value}
className="[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
containerClassName="!w-48"
containerClassName="w-48!"
validate={validate}
rightSlot={
<span className="flex self-stretch items-center border-l border-border-subtle px-2 text-xs font-medium text-text-subtle">
@@ -66,7 +66,7 @@ export function MoveToWorkspaceDialog({ onDone, requests, activeWorkspaceId }: P
<Button
size="xs"
color="secondary"
className="mr-auto min-w-[5rem]"
className="mr-auto min-w-20"
onClick={async () => {
await router.navigate({
to: "/workspaces/$workspaceId",
+7 -5
View File
@@ -1,3 +1,5 @@
@reference "../main.css";
.prose {
@apply text-text;
@@ -98,7 +100,7 @@
@apply text-notice hover:underline;
* {
@apply text-notice !important;
@apply text-notice!;
}
}
@@ -113,12 +115,12 @@
ol code,
ul code {
@apply text-xs bg-surface-active text-info font-normal whitespace-nowrap;
@apply px-1.5 py-0.5 rounded not-italic;
@apply px-1.5 py-0.5 rounded-sm not-italic;
@apply select-text;
}
pre {
@apply bg-surface-highlight text-text !important;
@apply bg-surface-highlight! text-text!;
@apply px-4 py-3 rounded-md;
@apply overflow-auto whitespace-pre;
@apply text-editor font-mono;
@@ -130,7 +132,7 @@
.banner {
@apply border border-dashed;
@apply border-border bg-surface-highlight text-text px-4 py-3 rounded text-base;
@apply border-border bg-surface-highlight text-text px-4 py-3 rounded-sm text-base;
&::before {
@apply block font-bold mb-1;
@@ -161,7 +163,7 @@
}
blockquote {
@apply italic py-3 pl-5 pr-3 border-l-8 border-surface-active text-lg text-text bg-surface-highlight rounded shadow-lg;
@apply italic py-3 pl-5 pr-3 border-l-8 border-surface-active text-lg text-text bg-surface-highlight rounded-sm shadow-lg;
p {
@apply m-0;
@@ -1,10 +1,17 @@
import type { GrpcConnection } from "@yaakapp-internal/models";
import { deleteModel } from "@yaakapp-internal/models";
import { HStack, Icon } from "@yaakapp-internal/ui";
import { formatDistanceToNowStrict } from "date-fns";
import {
differenceInHours,
differenceInMinutes,
format,
isToday,
isYesterday,
} from "date-fns";
import { useDeleteGrpcConnections } from "../hooks/useDeleteGrpcConnections";
import { pluralizeCount } from "../lib/pluralize";
import { Dropdown } from "./core/Dropdown";
import { Dropdown, type DropdownItem } from "./core/Dropdown";
import { formatMillis } from "./core/HttpResponseDurationTag";
import { IconButton } from "./core/IconButton";
interface Props {
@@ -20,6 +27,63 @@ export function RecentGrpcConnectionsDropdown({
}: Props) {
const deleteAllConnections = useDeleteGrpcConnections(activeConnection?.requestId);
const latestConnectionId = connections[0]?.id ?? "n/a";
const connectionHistoryItems: DropdownItem[] = [];
let lastHistoryGroup: string | null = null;
let hasRecentConnections = false;
let hasShownRecentEmptyState = false;
const now = new Date();
for (const c of connections) {
const createdAt = `${c.createdAt}Z`;
const createdAtDate = new Date(createdAt);
const minutesAgo = differenceInMinutes(now, createdAtDate);
const hoursAgo = differenceInHours(now, createdAtDate);
let historyGroup = format(createdAtDate, "MMM d, yyyy");
if (minutesAgo < 5) historyGroup = "Just now";
else if (minutesAgo < 15) historyGroup = "5 minutes ago";
else if (minutesAgo < 60) historyGroup = "15 minutes ago";
else if (hoursAgo < 3) historyGroup = "1 hour ago";
else if (hoursAgo < 6) historyGroup = "3 hours ago";
else if (isToday(createdAtDate)) historyGroup = "Today";
else if (isYesterday(createdAtDate)) historyGroup = "Yesterday";
else if (createdAtDate.getFullYear() === now.getFullYear()) historyGroup = format(createdAtDate, "MMM d");
const absoluteTime = format(createdAt, "MMM d, yyyy, h:mm:ss a O");
if (historyGroup === "Just now") {
hasRecentConnections = true;
} else if (!hasRecentConnections && !hasShownRecentEmptyState) {
connectionHistoryItems.push({
type: "content",
label: <span className="block px-4 py-1 text-sm text-text-subtle">No recent connections</span>,
});
hasShownRecentEmptyState = true;
}
if (historyGroup !== "Just now" && historyGroup !== lastHistoryGroup) {
connectionHistoryItems.push({
type: "separator",
label: <span title={absoluteTime}>{historyGroup}</span>,
});
lastHistoryGroup = historyGroup;
}
connectionHistoryItems.push({
label: (
<HStack space={2} className="text-sm" title={absoluteTime}>
<span className="font-mono">{formatMillis(c.elapsed)}</span>
</HStack>
),
leftSlot: activeConnection?.id === c.id ? <Icon icon="check" /> : <Icon icon="empty" />,
onSelect: () => onPinnedConnectionId(c.id),
});
}
if (!hasRecentConnections && !hasShownRecentEmptyState) {
connectionHistoryItems.push({
type: "content",
label: <span className="block px-4 py-1 text-sm text-text-subtle">No recent connections</span>,
});
}
return (
<Dropdown
@@ -36,16 +100,7 @@ export function RecentGrpcConnectionsDropdown({
disabled: connections.length === 0,
},
{ type: "separator", label: "History" },
...connections.map((c) => ({
label: (
<HStack space={2}>
{formatDistanceToNowStrict(`${c.createdAt}Z`)} ago &bull;{" "}
<span className="font-mono text-sm">{c.elapsed}ms</span>
</HStack>
),
leftSlot: activeConnection?.id === c.id ? <Icon icon="check" /> : <Icon icon="empty" />,
onSelect: () => onPinnedConnectionId(c.id),
})),
...connectionHistoryItems,
]}
>
<IconButton
@@ -1,13 +1,21 @@
import type { HttpResponse } from "@yaakapp-internal/models";
import { deleteModel } from "@yaakapp-internal/models";
import { HStack, Icon } from "@yaakapp-internal/ui";
import { useCopyHttpResponse } from "../hooks/useCopyHttpResponse";
import {
differenceInHours,
differenceInMinutes,
format,
isToday,
isYesterday,
} from "date-fns";
import { useDeleteHttpResponses } from "../hooks/useDeleteHttpResponses";
import { useSaveResponse } from "../hooks/useSaveResponse";
import { pluralize } from "../lib/pluralize";
import { Dropdown } from "./core/Dropdown";
import { useKeyValue } from "../hooks/useKeyValue";
import { DismissibleBanner } from "./core/DismissibleBanner";
import { Dropdown, type DropdownItem } from "./core/Dropdown";
import { formatMillis } from "./core/HttpResponseDurationTag";
import { HttpStatusTag } from "./core/HttpStatusTag";
import { IconButton } from "./core/IconButton";
import { SizeTag } from "./core/SizeTag";
interface Props {
responses: HttpResponse[];
@@ -22,32 +30,93 @@ export const RecentHttpResponsesDropdown = function ResponsePane({
onPinnedResponseId,
}: Props) {
const deleteAllResponses = useDeleteHttpResponses(activeResponse?.requestId);
const movedActionsBannerId = "response-actions-moved-to-response-menu-2026-07-02-v2";
const { value: dismissedMovedActions } = useKeyValue<boolean>({
namespace: "global",
key: ["dismiss-banner", movedActionsBannerId],
fallback: false,
});
const latestResponseId = responses[0]?.id ?? "n/a";
const saveResponse = useSaveResponse(activeResponse);
const copyResponse = useCopyHttpResponse(activeResponse);
const responseHistoryItems: DropdownItem[] = [];
let lastHistoryGroup: string | null = null;
let hasRecentResponses = false;
let hasShownRecentEmptyState = false;
const now = new Date();
for (const r of responses) {
const createdAt = `${r.createdAt}Z`;
const createdAtDate = new Date(createdAt);
const minutesAgo = differenceInMinutes(now, createdAtDate);
const hoursAgo = differenceInHours(now, createdAtDate);
let historyGroup = format(createdAtDate, "MMM d, yyyy");
if (minutesAgo < 5) historyGroup = "Just now";
else if (minutesAgo < 15) historyGroup = "5 minutes ago";
else if (minutesAgo < 60) historyGroup = "15 minutes ago";
else if (hoursAgo < 3) historyGroup = "1 hour ago";
else if (hoursAgo < 6) historyGroup = "3 hours ago";
else if (isToday(createdAtDate)) historyGroup = "Today";
else if (isYesterday(createdAtDate)) historyGroup = "Yesterday";
else if (createdAtDate.getFullYear() === now.getFullYear()) historyGroup = format(createdAtDate, "MMM d");
const absoluteTime = format(createdAt, "MMM d, yyyy, h:mm:ss a O");
if (historyGroup === "Just now") {
hasRecentResponses = true;
} else if (!hasRecentResponses && !hasShownRecentEmptyState) {
responseHistoryItems.push({
type: "content",
label: <span className="block px-4 py-1 text-sm text-text-subtle">No recent requests</span>,
});
hasShownRecentEmptyState = true;
}
if (historyGroup !== "Just now" && historyGroup !== lastHistoryGroup) {
responseHistoryItems.push({
type: "separator",
label: <span title={absoluteTime}>{historyGroup}</span>,
});
lastHistoryGroup = historyGroup;
}
responseHistoryItems.push({
label: (
<HStack space={2} className="text-sm" title={absoluteTime}>
<HttpStatusTag short className="text-xs" response={r} />
<span className="text-text-subtlest">&bull;</span>
<span className="font-mono">{r.elapsed >= 0 ? formatMillis(r.elapsed) : "n/a"}</span>
<span className="text-text-subtlest">&bull;</span>
<SizeTag
className="text-xs"
contentLength={r.contentLength ?? 0}
contentLengthCompressed={r.contentLengthCompressed}
/>
</HStack>
),
leftSlot: activeResponse?.id === r.id ? <Icon icon="check" /> : <Icon icon="empty" />,
onSelect: () => onPinnedResponseId(r.id),
});
}
if (!hasRecentResponses && !hasShownRecentEmptyState) {
responseHistoryItems.push({
type: "content",
label: <span className="block px-4 py-1 text-sm text-text-subtle">No recent requests</span>,
});
}
return (
<Dropdown
items={[
{
label: "Save to File",
onSelect: saveResponse.mutate,
leftSlot: <Icon icon="save" />,
hidden: responses.length === 0 || !!activeResponse.error,
disabled: activeResponse.state !== "closed" && activeResponse.status >= 100,
},
{
label: "Copy Body",
onSelect: copyResponse.mutate,
leftSlot: <Icon icon="copy" />,
hidden: responses.length === 0 || !!activeResponse.error,
disabled: activeResponse.state !== "closed" && activeResponse.status >= 100,
},
{
label: "Delete",
leftSlot: <Icon icon="trash" />,
onSelect: () => deleteModel(activeResponse),
},
{
label: "Delete all",
leftSlot: <Icon icon="trash" />,
onSelect: deleteAllResponses.mutate,
disabled: responses.length === 0,
},
{
label: "Unpin Response",
onSelect: () => onPinnedResponseId(activeResponse.id),
@@ -55,25 +124,25 @@ export const RecentHttpResponsesDropdown = function ResponsePane({
hidden: latestResponseId === activeResponse.id,
disabled: responses.length === 0,
},
{ type: "separator", label: "History" },
{
label: `Delete ${responses.length} ${pluralize("Response", responses.length)}`,
onSelect: deleteAllResponses.mutate,
hidden: responses.length === 0,
disabled: responses.length === 0,
},
{ type: "separator" },
...responses.map((r: HttpResponse) => ({
type: "content",
hidden: dismissedMovedActions === true,
label: (
<HStack space={2}>
<HttpStatusTag short className="text-xs" response={r} />
<span className="text-text-subtle">&rarr;</span>{" "}
<span className="font-mono text-sm">{r.elapsed >= 0 ? `${r.elapsed}ms` : "n/a"}</span>
</HStack>
<DismissibleBanner
id={movedActionsBannerId}
color="info"
size="xs"
className="max-w-72"
>
<p>Copy and save actions moved to the Response tab menu.</p>
</DismissibleBanner>
),
leftSlot: activeResponse?.id === r.id ? <Icon icon="check" /> : <Icon icon="empty" />,
onSelect: () => onPinnedResponseId(r.id),
})),
},
{
type: "separator",
label: "Recent",
},
...responseHistoryItems,
]}
>
<IconButton
@@ -1,10 +1,17 @@
import type { WebsocketConnection } from "@yaakapp-internal/models";
import { deleteModel, getModel } from "@yaakapp-internal/models";
import { HStack, Icon } from "@yaakapp-internal/ui";
import { formatDistanceToNowStrict } from "date-fns";
import {
differenceInHours,
differenceInMinutes,
format,
isToday,
isYesterday,
} from "date-fns";
import { deleteWebsocketConnections } from "../commands/deleteWebsocketConnections";
import { pluralizeCount } from "../lib/pluralize";
import { Dropdown } from "./core/Dropdown";
import { Dropdown, type DropdownItem } from "./core/Dropdown";
import { formatMillis } from "./core/HttpResponseDurationTag";
import { IconButton } from "./core/IconButton";
interface Props {
@@ -19,6 +26,63 @@ export function RecentWebsocketConnectionsDropdown({
onPinnedConnectionId,
}: Props) {
const latestConnectionId = connections[0]?.id ?? "n/a";
const connectionHistoryItems: DropdownItem[] = [];
let lastHistoryGroup: string | null = null;
let hasRecentConnections = false;
let hasShownRecentEmptyState = false;
const now = new Date();
for (const c of connections) {
const createdAt = `${c.createdAt}Z`;
const createdAtDate = new Date(createdAt);
const minutesAgo = differenceInMinutes(now, createdAtDate);
const hoursAgo = differenceInHours(now, createdAtDate);
let historyGroup = format(createdAtDate, "MMM d, yyyy");
if (minutesAgo < 5) historyGroup = "Just now";
else if (minutesAgo < 15) historyGroup = "5 minutes ago";
else if (minutesAgo < 60) historyGroup = "15 minutes ago";
else if (hoursAgo < 3) historyGroup = "1 hour ago";
else if (hoursAgo < 6) historyGroup = "3 hours ago";
else if (isToday(createdAtDate)) historyGroup = "Today";
else if (isYesterday(createdAtDate)) historyGroup = "Yesterday";
else if (createdAtDate.getFullYear() === now.getFullYear()) historyGroup = format(createdAtDate, "MMM d");
const absoluteTime = format(createdAt, "MMM d, yyyy, h:mm:ss a O");
if (historyGroup === "Just now") {
hasRecentConnections = true;
} else if (!hasRecentConnections && !hasShownRecentEmptyState) {
connectionHistoryItems.push({
type: "content",
label: <span className="block px-4 py-1 text-sm text-text-subtle">No recent connections</span>,
});
hasShownRecentEmptyState = true;
}
if (historyGroup !== "Just now" && historyGroup !== lastHistoryGroup) {
connectionHistoryItems.push({
type: "separator",
label: <span title={absoluteTime}>{historyGroup}</span>,
});
lastHistoryGroup = historyGroup;
}
connectionHistoryItems.push({
label: (
<HStack space={2} className="text-sm" title={absoluteTime}>
<span className="font-mono">{formatMillis(c.elapsed)}</span>
</HStack>
),
leftSlot: activeConnection?.id === c.id ? <Icon icon="check" /> : <Icon icon="empty" />,
onSelect: () => onPinnedConnectionId(c.id),
});
}
if (!hasRecentConnections && !hasShownRecentEmptyState) {
connectionHistoryItems.push({
type: "content",
label: <span className="block px-4 py-1 text-sm text-text-subtle">No recent connections</span>,
});
}
return (
<Dropdown
@@ -40,16 +104,7 @@ export function RecentWebsocketConnectionsDropdown({
disabled: connections.length === 0,
},
{ type: "separator", label: "History" },
...connections.map((c) => ({
label: (
<HStack space={2}>
{formatDistanceToNowStrict(`${c.createdAt}Z`)} ago &bull;{" "}
<span className="font-mono text-sm">{c.elapsed}ms</span>
</HStack>
),
leftSlot: activeConnection?.id === c.id ? <Icon icon="check" /> : <Icon icon="empty" />,
onSelect: () => onPinnedConnectionId(c.id),
})),
...connectionHistoryItems,
]}
>
<IconButton
@@ -167,7 +167,7 @@ export function ResponseCookies({ response }: Props) {
{cookie.value}
</span>
{cookie.isDeleted && (
<span className="text-xs font-sans text-danger bg-danger/10 px-1.5 py-0.5 rounded">
<span className="text-xs font-sans text-danger bg-danger/10 px-1.5 py-0.5 rounded-sm">
Deleted
</span>
)}
@@ -1,5 +1,6 @@
import { openUrl } from "@tauri-apps/plugin-opener";
import type { HttpResponse } from "@yaakapp-internal/models";
import { format, formatDistanceToNowStrict } from "date-fns";
import { useMemo } from "react";
import { CountBadge } from "./core/CountBadge";
import { DetailsBanner } from "./core/DetailsBanner";
@@ -29,12 +30,20 @@ export function ResponseHeaders({ response }: Props) {
<div className="overflow-auto h-full pb-4 gap-y-3 flex flex-col pr-0.5">
<DetailsBanner storageKey={`${response.requestId}.general`} summary={<h2>Info</h2>}>
<KeyValueRows>
<KeyValueRow labelColor="secondary" label="Sent">
<time
dateTime={new Date(`${response.createdAt}Z`).toISOString()}
title={formatDistanceToNowStrict(`${response.createdAt}Z`, { addSuffix: true })}
>
{format(`${response.createdAt}Z`, "MMM d, yyyy, h:mm:ss a O")}
</time>
</KeyValueRow>
<KeyValueRow labelColor="secondary" label="Request URL">
<div className="flex items-center gap-1">
<span className="select-text cursor-text">{response.url}</span>
<IconButton
iconSize="sm"
className="inline-block w-auto !h-auto opacity-50 hover:opacity-100"
className="inline-block w-auto h-auto! opacity-50 hover:opacity-100"
icon="external_link"
onClick={() => openUrl(response.url)}
title="Open in browser"
+1 -1
View File
@@ -24,7 +24,7 @@ export function ResponseInfo({ response }: Props) {
URL
<IconButton
iconSize="sm"
className="inline-block w-auto ml-1 !h-auto opacity-50 hover:opacity-100"
className="inline-block w-auto ml-1 h-auto! opacity-50 hover:opacity-100"
icon="external_link"
onClick={() => openUrl(response.url)}
title="Open in browser"
+1 -1
View File
@@ -10,7 +10,7 @@ export default function RouteError({ error }: { error: unknown }) {
typeof error === "object" && error != null && "stack" in error ? String(error.stack) : null;
return (
<div className="flex items-center justify-center h-full">
<VStack space={5} className="w-[50rem] !h-auto">
<VStack space={5} className="w-200 h-auto!">
<Heading>Route Error 🔥</Heading>
<FormattedError>
{message}
+1 -1
View File
@@ -108,7 +108,7 @@ export function SelectFile({
"rtl mr-1.5",
inline && "w-full",
filePath && inline && "font-mono text-xs",
isHovering && "!border-notice",
isHovering && "border-notice!",
)}
color={isHovering ? "primary" : "secondary"}
onClick={handleClick}
@@ -93,7 +93,7 @@ export default function Settings({ hide }: Props) {
layout="horizontal"
defaultValue={mainTab || tabFromQuery}
addBorders
tabListClassName="min-w-[10rem] bg-surface x-theme-sidebar border-r border-border pl-3"
tabListClassName="min-w-40 bg-surface x-theme-sidebar border-r border-border pl-3"
label="Settings"
tabs={tabs.map(
(value): TabItem => ({
@@ -131,28 +131,28 @@ export default function Settings({ hide }: Props) {
}),
)}
>
<TabContent value={TAB_GENERAL} className="overflow-y-auto h-full px-6 !py-4">
<TabContent value={TAB_GENERAL} className="overflow-y-auto h-full px-6 py-4!">
<SettingsGeneral />
</TabContent>
<TabContent value={TAB_INTERFACE} className="overflow-y-auto h-full px-6 !py-4">
<TabContent value={TAB_INTERFACE} className="overflow-y-auto h-full px-6 py-4!">
<SettingsInterface />
</TabContent>
<TabContent value={TAB_THEME} className="overflow-y-auto h-full px-6 !py-4">
<TabContent value={TAB_THEME} className="overflow-y-auto h-full px-6 py-4!">
<SettingsTheme />
</TabContent>
<TabContent value={TAB_SHORTCUTS} className="overflow-y-auto h-full px-6 !py-4">
<TabContent value={TAB_SHORTCUTS} className="overflow-y-auto h-full px-6 py-4!">
<SettingsHotkeys />
</TabContent>
<TabContent value={TAB_PLUGINS} className="h-full grid grid-rows-1">
<SettingsPlugins defaultSubtab={mainTab === TAB_PLUGINS ? subtab : undefined} />
</TabContent>
<TabContent value={TAB_PROXY} className="overflow-y-auto h-full px-6 !py-4">
<TabContent value={TAB_PROXY} className="overflow-y-auto h-full px-6 py-4!">
<SettingsProxy />
</TabContent>
<TabContent value={TAB_CERTIFICATES} className="overflow-y-auto h-full px-6 !py-4">
<TabContent value={TAB_CERTIFICATES} className="overflow-y-auto h-full px-6 py-4!">
<SettingsCertificates />
</TabContent>
<TabContent value={TAB_LICENSE} className="overflow-y-auto h-full px-6 !py-4">
<TabContent value={TAB_LICENSE} className="overflow-y-auto h-full px-6 py-4!">
<SettingsLicense />
</TabContent>
</Tabs>
@@ -56,7 +56,7 @@ export function SettingsGeneral() {
model={settings}
modelKey="updateChannel"
label="Update Channel"
selectClassName="!w-full"
selectClassName="w-full!"
options={[
{ label: "Stable", value: "stable" },
{ label: "Beta", value: "beta" },
@@ -341,7 +341,7 @@ function RecordHotkeyDialog({ label, onSave, onCancel }: RecordHotkeyDialogProps
}}
className={classNames(
"flex items-center justify-center",
"px-4 py-2 rounded-lg bg-surface-highlight border outline-none cursor-default w-full",
"px-4 py-2 rounded-lg bg-surface-highlight border outline-hidden cursor-default w-full",
"border-border-subtle focus:border-border-focus",
)}
>
@@ -89,7 +89,7 @@ export function SettingsInterface() {
<SettingSelectControl
name="uiFont"
label="Interface font"
selectClassName="!w-72"
selectClassName="w-72!"
value={settings.interfaceFont ?? NULL_FONT_VALUE}
defaultValue={NULL_FONT_VALUE}
options={[
@@ -106,7 +106,7 @@ export function SettingsInterface() {
<SettingSelectControl
name="interfaceFontSize"
label="Interface Font Size"
selectClassName="!w-20"
selectClassName="w-20!"
value={`${settings.interfaceFontSize}`}
defaultValue="14"
options={fontSizeOptions}
@@ -123,7 +123,7 @@ export function SettingsInterface() {
<SettingSelectControl
name="editorFont"
label="Editor font"
selectClassName="!w-72"
selectClassName="w-72!"
value={settings.editorFont ?? NULL_FONT_VALUE}
defaultValue={NULL_FONT_VALUE}
options={[
@@ -139,7 +139,7 @@ export function SettingsInterface() {
<SettingSelectControl
name="editorFontSize"
label="Editor Font Size"
selectClassName="!w-20"
selectClassName="w-20!"
value={`${settings.editorFontSize}`}
defaultValue="12"
options={fontSizeOptions}
@@ -211,7 +211,7 @@ function PluginTableRow({
return (
<TableRow>
{showCheckbox && (
<TableCell className="!py-0">
<TableCell className="py-0!">
<Checkbox
hideLabel
title={plugin?.enabled ? "Disable plugin" : "Enable plugin"}
@@ -249,7 +249,7 @@ function PluginTableRow({
)}
</HStack>
</TableCell>
<TableCell className="!py-0">
<TableCell className="py-0!">
<HStack justifyContent="end" space={1.5}>
{plugin != null && latestVersion != null ? (
<Button
@@ -56,7 +56,7 @@ export function SettingsProxy() {
{ label: "Custom proxy configuration", value: "enabled" },
{ label: "No proxy", value: "disabled" },
]}
selectClassName="!w-64"
selectClassName="w-64!"
/>
</SettingsSection>
@@ -99,7 +99,7 @@ export function SettingsProxy() {
description="Comma-separated list of hosts that should bypass the proxy."
value={settings.proxy.bypass}
placeholder="127.0.0.1, *.example.com, localhost:3000"
inputWidthClassName="!w-96"
inputWidthClassName="w-96!"
onChange={(bypass) => patchProxy({ bypass })}
/>
</SettingsSection>
@@ -120,7 +120,7 @@ export function SettingsTheme() {
<SettingsSection title="Preview">
<VStack
space={3}
className="mt-4 w-full bg-surface p-3 border border-dashed border-border-subtle rounded overflow-x-auto"
className="mt-4 w-full bg-surface p-3 border border-dashed border-border-subtle rounded-sm overflow-x-auto"
>
<HStack className="text" space={1.5}>
<Icon icon={appearance === "dark" ? "moon" : "sun"} />
+6 -6
View File
@@ -588,7 +588,7 @@ function Sidebar({ className }: { className?: string }) {
rightSlot={
filterText.text && (
<IconButton
className="!bg-transparent !h-auto min-h-full opacity-50 hover:opacity-100 -mr-1"
className="bg-transparent! h-auto! min-h-full opacity-50 hover:opacity-100 -mr-1"
icon="x"
title="Clear filter"
onClick={clearFilterText}
@@ -667,8 +667,8 @@ function Sidebar({ className }: { className?: string }) {
<div className="p-3 text-sm text-center">
{(emptyFilterSuggestions?.length ?? 0) > 0 ? (
<EmptyStateText
wrapperClassName="!h-auto mb-auto"
className="!h-auto py-3 px-3 !text-text-subtle text-sm leading-relaxed text-center"
wrapperClassName="h-auto! mb-auto"
className="h-auto! py-3 px-3 text-text-subtle! text-sm leading-relaxed text-center"
>
<div>
No results, but found matches for{" "}
@@ -677,7 +677,7 @@ function Sidebar({ className }: { className?: string }) {
{i > 0 && " or "}
<button
type="button"
className="max-w-full rounded align-middle focus-visible:outline focus-visible:outline-2 focus-visible:outline-info"
className="max-w-full rounded-sm align-middle focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-info"
onClick={() => applyFilterExample(suggestion.filterText)}
>
<InlineCode className="inline-block max-w-36 truncate align-middle whitespace-nowrap transition-colors hover:border-border hover:bg-surface-active hover:text-text">
@@ -690,8 +690,8 @@ function Sidebar({ className }: { className?: string }) {
</EmptyStateText>
) : (
<EmptyStateText
wrapperClassName="!h-auto mb-auto"
className="!h-auto py-3 px-3 !text-text-subtle text-sm leading-relaxed text-center"
wrapperClassName="h-auto! mb-auto"
className="h-auto! py-3 px-3 text-text-subtle! text-sm leading-relaxed text-center"
>
<div>
No results for{" "}
@@ -208,10 +208,10 @@ function InitializedTemplateFunctionDialog({
)}
/>
</HStack>
<div className="relative w-full max-h-[10rem]">
<div className="relative w-full max-h-40">
<InlineCode
className={classNames(
"block whitespace-pre-wrap !select-text cursor-text max-h-[10rem] overflow-auto hide-scrollbars !border-text-subtlest",
"block whitespace-pre-wrap select-text! cursor-text max-h-40 overflow-auto hide-scrollbars border-text-subtlest!",
tooLarge && "italic text-danger",
)}
>
@@ -246,7 +246,7 @@ function InitializedTemplateFunctionDialog({
) : (
<span />
)}
<div className="flex justify-stretch w-full flex-grow gap-2 [&>*]:flex-1">
<div className="flex justify-stretch w-full grow gap-2 *:flex-1">
{templateFunction.data.name === "secure" && (
<Button variant="border" color="secondary" onClick={setupOrConfigureEncryption}>
Reveal Encryption Key
@@ -271,7 +271,7 @@ TemplateFunctionDialog.show = (
showDialog({
id: `template-function-${Math.random()}`, // Allow multiple at once
size: "md",
className: "h-[60rem]",
className: "h-240",
noPadding: true,
title: <InlineCode>{fn.name}()</InlineCode>,
description: fn.description,
+1 -1
View File
@@ -94,7 +94,7 @@ export const UrlBar = memo(function UrlBar({
iconSize="md"
title="Send Request"
type="submit"
className="w-8 mr-0.5 !h-full"
className="w-8 mr-0.5 h-full!"
iconColor="secondary"
icon={isLoading ? "x" : submitIcon}
hotkeyAction="request.send"
@@ -217,7 +217,7 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque
title="Close connection"
icon="x"
iconColor="secondary"
className="w-8 mr-0.5 !h-full"
className="w-8 mr-0.5 h-full!"
onClick={handleCancel}
/>
)
@@ -236,7 +236,7 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque
ref={tabsRef}
label="Request"
tabs={tabs}
tabListClassName="mt-1 !mb-1.5"
tabListClassName="mt-1 mb-1.5!"
storageKey={TABS_STORAGE_KEY}
activeTabKey={activeRequestId}
>
@@ -283,7 +283,7 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque
hideLabel
forceUpdateKey={forceUpdateKey}
defaultValue={activeRequest.name}
className="font-sans !text-xl !px-0"
className="font-sans text-xl! px-0!"
containerClassName="border-0"
placeholder={resolvedModelName(activeRequest)}
onChange={(name) => patchModel(activeRequest, { name })}
+2 -2
View File
@@ -85,7 +85,7 @@ export function Workspace() {
<div style={environmentBgStyle} className="absolute inset-0 opacity-[0.07]" />
<div
style={environmentBgStyle}
className="absolute left-0 right-0 -bottom-[1px] h-[1px] opacity-20"
className="absolute left-0 right-0 -bottom-px h-px opacity-20"
/>
</div>
<WorkspaceHeader className="pointer-events-none" floatingSidebar={floating} />
@@ -162,7 +162,7 @@ function WorkspaceBody() {
// Delay the entering because the workspaces might load after a slight delay
transition={{ delay: 0.5 }}
>
<Banner color="warning" className="max-w-[30rem]">
<Banner color="warning" className="max-w-120">
The active workspace was not found. Select a workspace from the header menu or report this
bug to <FeedbackLink />
</Banner>
@@ -176,7 +176,7 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
size="sm"
className={classNames(
className,
"text !px-2 truncate",
"text px-2! truncate",
workspace === null && "italic opacity-disabled",
)}
{...buttonProps}
@@ -324,7 +324,7 @@ function KeyRevealer({
function HighlightedKey({ keyText, show }: { keyText: string; show: boolean }) {
return (
<span className="text-xs font-mono [&_*]:cursor-auto [&_*]:select-text">
<span className="text-xs font-mono **:cursor-auto **:select-text">
{show ? (
keyText.split("").map((c, i) => {
return (
@@ -127,7 +127,7 @@ export function WorkspaceSettingsDialog({ workspaceId, hide, tab }: Props) {
placeholder="Workspace Name"
label="Name"
defaultValue={workspace.name}
className="!text-base font-sans"
className="text-base! font-sans"
onChange={(name) => patchModel(workspace, { name })}
/>
@@ -161,7 +161,7 @@ export function WorkspaceSettingsDialog({ workspaceId, hide, tab }: Props) {
<InlineCode className="flex gap-1 items-center text-primary pl-2.5">
{workspaceId}
<CopyIconButton
className="opacity-70 !text-primary"
className="opacity-70 text-primary!"
size="2xs"
iconSize="sm"
title="Copy workspace ID"
@@ -182,7 +182,7 @@ WorkspaceSettingsDialog.show = (workspaceId: string, tab?: WorkspaceSettingsTab)
showDialog({
id: "workspace-settings",
size: "lg",
className: "h-[calc(100vh-5rem)] !max-h-[50rem]",
className: "h-[calc(100vh-5rem)] max-h-200!",
noPadding: true,
render: ({ hide }) => (
<WorkspaceSettingsDialog workspaceId={workspaceId} hide={hide} tab={tab} />
@@ -72,7 +72,7 @@ export function AutoScroller<T>({
size="sm"
iconSize="md"
variant="border"
className="!bg-surface z-10"
className="bg-surface! z-10"
onClick={() => setAutoScroll((v) => !v)}
/>
</div>
@@ -80,7 +80,7 @@ export function AutoScroller<T>({
{header ?? <span aria-hidden />}
<div
ref={containerRef}
className="h-full w-full overflow-y-auto focus:outline-none"
className="h-full w-full overflow-y-auto focus:outline-hidden"
onScroll={handleScroll}
tabIndex={focusable ? 0 : undefined}
>
@@ -39,10 +39,10 @@ export function Checkbox({
<input
aria-hidden
className={classNames(
"appearance-none flex-shrink-0 border border-border",
"appearance-none shrink-0 border border-border",
size === "sm" && "w-4 h-4",
size === "md" && "w-5 h-5",
"rounded outline-none ring-0",
"rounded-sm outline-hidden ring-0",
!disabled && "hocus:border-border-focus hocus:bg-focus/[5%]",
disabled && "border-dotted",
)}
@@ -17,7 +17,7 @@ export function ColorPicker({ onChange, color, className }: Props) {
<div className={className}>
<HexColorPicker
color={color ?? undefined}
className="!w-full"
className="w-full!"
onChange={(color) => {
onChange(color);
regenerateKey(); // To force input to change
@@ -96,7 +96,7 @@ export function ColorPickerWithThemeColors({ onChange, color, className }: Props
<>
<HexColorPicker
color={color ?? undefined}
className="!w-full"
className="w-full!"
onChange={(color) => {
onChange(color);
regenerateKey(); // To force input to change
@@ -18,7 +18,7 @@ export function CountBadge({ count, count2, className, color, showZero }: Props)
className={classNames(
className,
"flex items-center",
"opacity-70 border text-4xs rounded mb-0.5 px-1 ml-1 h-4 font-mono",
"opacity-70 border text-4xs rounded-sm mb-0.5 px-1 ml-1 h-4 font-mono",
color == null && "border-border-subtle",
color === "primary" && "text-primary",
color === "secondary" && "text-secondary",
@@ -42,7 +42,7 @@ export function DetailsBanner({
return (
<Banner color={color} className={className}>
<details className="group list-none" open={isOpen} onToggle={handleToggle} {...extraProps}>
<summary className="!cursor-default !select-none list-none flex items-center gap-3 focus:outline-none opacity-70">
<summary className="cursor-default! select-none! list-none flex items-center gap-3 focus:outline-hidden opacity-70">
<div
className={classNames(
"transition-transform",
+6 -6
View File
@@ -74,13 +74,13 @@ export function Dialog({
"relative bg-surface pointer-events-auto",
"rounded-lg",
"border border-border-subtle shadow-lg shadow-[rgba(0,0,0,0.1)]",
"min-h-[10rem]",
"min-h-40",
"max-w-[calc(100vw-5rem)] max-h-[calc(100vh-5rem)]",
size === "sm" && "w-[30rem]",
size === "md" && "w-[50rem]",
size === "lg" && "w-[70rem]",
size === "full" && "w-[100vw] h-[100vh]",
size === "dynamic" && "min-w-[20rem] max-w-[100vw]",
size === "sm" && "w-120",
size === "md" && "w-200",
size === "lg" && "w-280",
size === "full" && "w-screen h-screen",
size === "dynamic" && "min-w-80 max-w-[100vw]",
)}
>
{title ? (
@@ -2,21 +2,26 @@ import type { Color } from "@yaakapp-internal/plugins";
import type { BannerProps } from "@yaakapp-internal/ui";
import { Banner } from "@yaakapp-internal/ui";
import classNames from "classnames";
import type { MouseEvent } from "react";
import { useEffect } from "react";
import { useKeyValue } from "../../hooks/useKeyValue";
import type { ButtonProps } from "./Button";
import { Button } from "./Button";
type DismissibleBannerSize = "sm" | "xs";
export function DismissibleBanner({
children,
className,
id,
size = "sm",
onDismiss,
onShow,
actions,
...props
}: BannerProps & {
id: string;
size?: DismissibleBannerSize;
onDismiss?: () => void | Promise<void>;
onShow?: () => void | Promise<void>;
actions?: {
@@ -46,17 +51,36 @@ export function DismissibleBanner({
if (!shouldShow) return null;
const actionSize: ButtonProps["size"] = size === "xs" ? "2xs" : "xs";
const stopParentClick = (event: MouseEvent) => {
event.preventDefault();
event.stopPropagation();
};
return (
<Banner className={classNames(className, "relative")} {...props}>
<Banner
className={classNames(
className,
"relative",
size === "xs" && "!px-2 !py-2 text-xs",
)}
{...props}
>
<div className="@container">
<div className="grid gap-2 @[34rem]:grid-cols-[minmax(0,1fr)_auto] @[34rem]:items-center @[34rem]:gap-3">
<div
className={classNames(
"grid @[34rem]:grid-cols-[minmax(0,1fr)_auto] @[34rem]:items-center",
size === "xs" ? "gap-1.5 @[34rem]:gap-2" : "gap-2 @[34rem]:gap-3",
)}
>
{children}
<div className="flex flex-wrap gap-1.5 @[34rem]:justify-end">
<Button
variant="border"
color={props.color}
size="xs"
onClick={() => {
size={actionSize}
onClick={(event) => {
stopParentClick(event);
setDismissed(true).catch(console.error);
Promise.resolve(onDismiss?.()).catch(console.error);
}}
@@ -69,8 +93,11 @@ export function DismissibleBanner({
key={a.label}
variant={a.variant ?? "border"}
color={a.color ?? props.color}
size="xs"
onClick={a.onClick}
size={actionSize}
onClick={(event) => {
stopParentClick(event);
a.onClick();
}}
title={a.label}
>
{a.label}
+13 -13
View File
@@ -712,7 +712,7 @@ const Menu = forwardRef<Omit<DropdownRef, "open" | "isOpen" | "toggle" | "items"
className={classNames(
className,
"x-theme-menu",
"outline-none my-1 pointer-events-auto z-40",
"outline-hidden my-1 pointer-events-auto z-40",
"fixed",
)}
>
@@ -734,7 +734,7 @@ const Menu = forwardRef<Omit<DropdownRef, "open" | "isOpen" | "toggle" | "items"
{filter && (
<HStack
space={2}
className="pb-0.5 px-1.5 mb-2 text-sm border border-border-subtle mx-2 rounded font-mono h-xs"
className="pb-0.5 px-1.5 mb-2 text-sm border border-border-subtle mx-2 rounded-sm font-mono h-xs"
>
<Icon icon="search" size="xs" />
<div className="text">{filter}</div>
@@ -916,24 +916,24 @@ function MenuItem({
)
}
rightSlot={rightSlot && <div className="ml-auto pl-3">{rightSlot}</div>}
innerClassName="!text-left"
innerClassName="text-left!"
color="custom"
className={classNames(
className,
"h-xs", // More compact
"min-w-[8rem] outline-none px-2 mx-1.5 flex whitespace-nowrap",
"focus:bg-surface-highlight focus:text rounded focus:outline-none focus-visible:outline-1",
isParentOfActiveSubmenu && "bg-surface-highlight text rounded",
item.color === "danger" && "!text-danger",
item.color === "primary" && "!text-primary",
item.color === "success" && "!text-success",
item.color === "warning" && "!text-warning",
item.color === "notice" && "!text-notice",
item.color === "info" && "!text-info",
"min-w-32 outline-hidden px-2 mx-1.5 flex whitespace-nowrap",
"focus:bg-surface-highlight focus:text rounded-sm focus:outline-hidden focus-visible:outline-1",
isParentOfActiveSubmenu && "bg-surface-highlight text rounded-sm",
item.color === "danger" && "text-danger!",
item.color === "primary" && "text-primary!",
item.color === "success" && "text-success!",
item.color === "warning" && "text-warning!",
item.color === "notice" && "text-notice!",
item.color === "info" && "text-info!",
)}
{...props}
>
<div className={classNames("truncate min-w-[5rem]")}>{item.label}</div>
<div className={classNames("truncate min-w-20")}>{item.label}</div>
</Button>
);
}
@@ -1,3 +1,5 @@
@reference "../../../main.css";
.cm-wrapper.cm-multiline .cm-mergeView {
@apply h-full w-full overflow-auto pr-0.5;
@@ -9,7 +11,7 @@
@apply w-full min-h-full relative;
.cm-collapsedLines {
@apply bg-none bg-surface border border-border py-1 mx-0.5 text-text opacity-80 hover:opacity-100 rounded cursor-default;
@apply bg-none bg-surface border border-border py-1 mx-0.5 text-text opacity-80 hover:opacity-100 rounded-sm cursor-default;
}
}
@@ -19,21 +21,21 @@
.cm-changedLine {
/* Round top corners only if previous line is not a changed line */
&:not(.cm-changedLine + &) {
@apply rounded-t;
@apply rounded-t-sm;
}
/* Round bottom corners only if next line is not a changed line */
&:not(:has(+ .cm-changedLine)) {
@apply rounded-b;
@apply rounded-b-sm;
}
}
/* Let content grow and disable individual scrolling for sync */
.cm-editor {
@apply h-auto relative !important;
@apply h-auto! relative!;
position: relative !important;
}
.cm-scroller {
@apply overflow-visible !important;
@apply overflow-visible!;
}
}
@@ -1,3 +1,5 @@
@reference "../../../main.css";
.cm-wrapper {
@apply h-full overflow-hidden;
@@ -7,7 +9,7 @@
/* Regular cursor */
.cm-cursor {
@apply border-text !important;
@apply border-text!;
/* Widen the cursor a bit */
@apply border-l-[2px];
}
@@ -15,8 +17,8 @@
/* Vim-mode cursor */
.cm-fat-cursor {
@apply outline-0 bg-text !important;
@apply text-surface !important;
@apply outline-0! bg-text!;
@apply text-surface!;
}
/* Matching bracket */
@@ -59,12 +61,12 @@
* {
@apply cursor-text;
@apply caret-transparent !important;
@apply caret-transparent!;
}
}
.cm-selectionBackground {
@apply bg-selection !important;
@apply bg-selection!;
}
/* Fix WebKit/WKWebView rendering bug where selection layer leaves a ghost
@@ -88,7 +90,7 @@
}
.cm-gutter-lint {
@apply w-auto !important;
@apply w-auto!;
.cm-gutterElement {
@apply px-0;
@@ -111,7 +113,7 @@
@apply bg-surface text-text-subtle border-border-subtle whitespace-nowrap cursor-default;
@apply hover:border-border hover:text-text hover:bg-surface-highlight;
@apply inline border px-1 mx-[0.5px] rounded dark:shadow;
@apply inline border px-1 mx-[0.5px] rounded-sm dark:shadow;
-webkit-text-security: none;
@@ -162,7 +164,7 @@
&::-webkit-scrollbar-corner,
&::-webkit-scrollbar {
@apply hidden !important;
@apply hidden!;
}
}
}
@@ -189,16 +191,16 @@
/* Style search matches */
.cm-searchMatch {
@apply bg-transparent !important;
@apply bg-transparent!;
@apply rounded-[2px] outline outline-1;
&.cm-searchMatch-selected {
@apply outline-text;
@apply bg-text !important;
@apply bg-text!;
&,
* {
@apply text-surface font-semibold !important;
@apply text-surface! font-semibold!;
}
}
}
@@ -223,8 +225,8 @@
}
.cm-editor .fold-gutter-icon {
@apply pt-[0.25em] pl-[0.4em] px-[0.4em] h-4 rounded;
@apply cursor-default !important;
@apply pt-[0.25em] pl-[0.4em] px-[0.4em] h-4 rounded-sm;
@apply cursor-default!;
}
.cm-editor .fold-gutter-icon::after {
@@ -248,7 +250,7 @@
.cm-editor .cm-foldPlaceholder {
@apply px-2 border border-border-subtle bg-surface-highlight;
@apply hover:text-text hover:border-border-subtle text-text;
@apply cursor-default !important;
@apply cursor-default!;
}
.cm-editor .cm-activeLineGutter {
@@ -277,7 +279,7 @@
}
.cm-tooltip-lint {
@apply font-mono text-editor rounded overflow-hidden bg-surface-highlight border border-border shadow !important;
@apply font-mono! text-editor! rounded-sm! overflow-hidden! bg-surface-highlight! border! border-border! shadow!;
.cm-diagnostic-error {
@apply border-l-danger px-4 py-2;
@@ -293,18 +295,18 @@
}
.cm-tooltip.cm-tooltip-hover {
@apply shadow-lg bg-surface rounded text-text-subtle border border-border-subtle z-50 pointer-events-auto text-sm;
@apply shadow-lg bg-surface rounded-sm text-text-subtle border border-border-subtle z-50 pointer-events-auto text-sm;
@apply p-1.5;
/* Style the tooltip for popping up "open in browser" and other stuff */
a,
button {
@apply text-text hover:bg-surface-highlight w-full h-sm flex items-center px-2 rounded;
@apply text-text hover:bg-surface-highlight w-full h-sm flex items-center px-2 rounded-sm;
}
a {
@apply cursor-default !important;
@apply cursor-default!;
&::after {
@apply text-text bg-text h-3 w-3 ml-1;
@@ -319,10 +321,10 @@
/* NOTE: Extra selector required to override default styles */
.cm-tooltip.cm-tooltip-autocomplete,
.cm-tooltip.cm-completionInfo {
@apply shadow-lg bg-surface rounded text-text-subtle border border-border-subtle z-50 pointer-events-auto;
@apply shadow-lg bg-surface rounded-sm text-text-subtle border border-border-subtle z-50 pointer-events-auto;
& * {
@apply font-mono text-editor !important;
@apply font-mono! text-editor!;
}
.cm-completionIcon {
@@ -409,7 +411,7 @@
}
.cm-completionIcon {
@apply text-sm flex items-center pb-0.5 flex-shrink-0;
@apply text-sm flex items-center pb-0.5 shrink-0;
}
.cm-completionLabel {
@@ -427,7 +429,7 @@
input,
button {
@apply rounded-sm outline-none;
@apply rounded-sm outline-hidden;
}
button {
@@ -436,12 +438,12 @@
}
button[name="close"] {
@apply text-text-subtle hocus:text-text px-2 -mr-1.5 !important;
@apply text-text-subtle! hocus:text-text! px-2! -mr-1.5!;
}
input {
@apply bg-surface border-border-subtle focus:border-border-focus;
@apply border outline-none;
@apply border outline-hidden;
}
input.cm-textfield {
@@ -486,7 +486,7 @@ function EditorInner({
const decoratedActions = useMemo(() => {
const results = [];
const actionClassName = classNames(
"bg-surface transition-opacity transform-gpu opacity-0 group-hover:opacity-100 hover:!opacity-100 shadow",
"bg-surface transition-opacity transform-gpu opacity-0 group-hover:opacity-100 hover:opacity-100! shadow",
);
if (format) {
@@ -24,8 +24,8 @@ export function EventViewerRow({
onClick={onClick}
className={classNames(
"w-full grid grid-cols-[auto_minmax(0,1fr)_auto] gap-2 items-center text-left",
"px-1.5 h-xs font-mono text-editor cursor-default group focus:outline-none focus:text-text rounded",
isActive && "bg-surface-active !text-text",
"px-1.5 h-xs font-mono text-editor cursor-default group focus:outline-hidden focus:text-text rounded-sm",
isActive && "bg-surface-active text-text!",
"text-text-subtle hover:text",
)}
>
+1 -1
View File
@@ -30,7 +30,7 @@ export function HotkeyRaw({ labelParts, className, variant }: HotkeyRawProps) {
className={classNames(
className,
variant === "with-bg" &&
"rounded bg-surface-highlight px-1 border border-border text-text-subtle",
"rounded-sm bg-surface-highlight px-1 border border-border text-text-subtle",
variant === "text" && "text-text-subtlest",
)}
>
@@ -81,7 +81,7 @@ export function HttpMethodTagRaw({
colored && m === "PATCH" && "text-notice",
colored && m === "POST" && "text-success",
colored && m === "DELETE" && "text-danger",
"font-mono flex-shrink-0 whitespace-pre",
"font-mono shrink-0 whitespace-pre",
"pt-[0.15em]", // Fix for monospace font not vertically centering
)}
>
@@ -31,7 +31,7 @@ export function HttpResponseDurationTag({ response }: Props) {
);
}
function formatMillis(ms: number) {
export function formatMillis(ms: number) {
if (ms < 1000) {
return `${ms} ms`;
}
+5 -5
View File
@@ -201,7 +201,7 @@ function BaseInput({
const id = useRef(`input-${generateId()}`);
const editorClassName = classNames(
className,
"!bg-transparent min-w-0 h-auto w-full focus:outline-none placeholder:text-placeholder",
"bg-transparent! min-w-0 h-auto w-full focus:outline-hidden placeholder:text-placeholder",
);
const isValid = useMemo(() => {
@@ -264,7 +264,7 @@ function BaseInput({
"border",
focused && !disabled ? "border-border-focus" : "border-border",
disabled && "border-dotted",
!isValid && hasChanged && "!border-danger",
!isValid && hasChanged && "border-danger!",
size === "md" && "min-h-md",
size === "sm" && "min-h-sm",
size === "xs" && "min-h-xs",
@@ -333,7 +333,7 @@ function BaseInput({
: `Obscure ${typeof label === "string" ? label : "field"}`
}
size="xs"
className={classNames("mr-0.5 !h-auto my-0.5", disabled && "opacity-disabled")}
className={classNames("mr-0.5 h-auto! my-0.5", disabled && "opacity-disabled")}
color={tint}
// iconClassName={classNames(
// tint === 'primary' && 'text-primary',
@@ -548,9 +548,9 @@ function EncryptionInput({
color={tint}
aria-label="Configure encryption"
className={classNames(
"flex items-center justify-center !h-full !px-1",
"flex items-center justify-center h-full! px-1!",
"opacity-70", // Makes it a bit subtler
props.disabled && "!opacity-disabled",
props.disabled && "opacity-disabled!",
)}
>
<HStack space={0.5}>
@@ -73,7 +73,7 @@ export function KeyValueRow({
<>
<td
className={classNames(
"select-none py-0.5 pr-2 h-full max-w-[10rem]",
"select-none py-0.5 pr-2 h-full max-w-40",
align === "top" && "align-top",
align === "middle" && "align-middle",
labelClassName,
@@ -86,12 +86,12 @@ export function KeyValueRow({
</td>
<td
className={classNames(
"select-none py-0.5 break-all max-w-[15rem]",
"select-none py-0.5 break-all max-w-60",
align === "top" && "align-top",
align === "middle" && "align-middle",
)}
>
<div className="select-text cursor-text max-h-[12rem] overflow-y-auto grid grid-cols-[auto_minmax(0,1fr)_auto]">
<div className="select-text cursor-text max-h-48 overflow-y-auto grid grid-cols-[auto_minmax(0,1fr)_auto]">
{leftSlot ?? <span aria-hidden />}
{children}
{resolvedRightSlot ? (
+1 -1
View File
@@ -27,7 +27,7 @@ export function Label({
className={classNames(
className,
visuallyHidden && "sr-only",
"flex-shrink-0 text-sm",
"shrink-0 text-sm",
"text-text-subtle whitespace-nowrap flex items-center gap-1 mb-0.5",
)}
{...props}
@@ -566,7 +566,7 @@ export function PairEditorRow({
title={pair.enabled ? "Disable item" : "Enable item"}
disabled={isLast || disabled}
checked={isLast ? false : !!pair.enabled}
className={classNames(isLast && "!opacity-disabled")}
className={classNames(isLast && "opacity-disabled!")}
onChange={handleChangeEnabled}
/>
{!isLast && !disableDrag ? (
@@ -586,7 +586,7 @@ export function PairEditorRow({
<div
className={classNames(
"grid items-center",
"@xs:gap-2 @xs:!grid-rows-1 @xs:!grid-cols-[minmax(0,1fr)_minmax(0,1fr)]",
"@xs:gap-2 @xs:grid-rows-1! @xs:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]!",
"gap-0.5 grid-cols-1 grid-rows-2",
)}
>
@@ -830,7 +830,7 @@ function MultilineEditDialog({
const [value, setValue] = useState<string>(defaultValue);
const language = languageFromContentType(contentType, value);
return (
<div className="w-[100vw] max-w-[40rem] h-[50vh] max-h-full grid grid-rows-[minmax(0,1fr)_auto]">
<div className="w-screen max-w-160 h-[50vh] max-h-full grid grid-rows-[minmax(0,1fr)_auto]">
<Editor
heightMode="auto"
defaultValue={defaultValue}
@@ -26,7 +26,7 @@ export function PairOrBulkEditor({ preferenceName, ...props }: Props) {
variant="border"
title={useBulk ? "Enable form edit" : "Enable bulk edit"}
className={classNames(
"transition-opacity opacity-0 group-hover:opacity-80 hover:!opacity-100 shadow",
"transition-opacity opacity-0 group-hover:opacity-80 hover:opacity-100! shadow",
"bg-surface hover:text group-hover/wrapper:opacity-100",
)}
onClick={() => setUseBulk((b) => !b)}
@@ -7,7 +7,7 @@ export function PillButton({ className, ...props }: ButtonProps) {
<Button
size="2xs"
variant="border"
className={classNames(className, "!rounded-full mx-1 !px-3")}
className={classNames(className, "rounded-full! mx-1 px-3!")}
{...props}
/>
);
@@ -116,7 +116,7 @@ export const PlainInput = forwardRef<{ focus: () => void }, PlainInputProps>(fun
const id = useRef(`input-${generateId()}`);
const commonClassName = classNames(
className,
"!bg-transparent min-w-0 w-full focus:outline-none placeholder:text-placeholder",
"bg-transparent! min-w-0 w-full focus:outline-hidden placeholder:text-placeholder",
"px-2 text-xs font-mono cursor-text",
);
@@ -167,7 +167,7 @@ export const PlainInput = forwardRef<{ focus: () => void }, PlainInputProps>(fun
"overflow-hidden",
focused && !disabled ? "border-border-focus" : "border-border-subtle",
disabled && "border-dotted",
hasChanged && "has-[:invalid]:border-danger", // For built-in HTML validation
hasChanged && "has-invalid:border-danger", // For built-in HTML validation
size === "md" && "min-h-md",
size === "sm" && "min-h-sm",
size === "xs" && "min-h-xs",
@@ -225,7 +225,7 @@ export const PlainInput = forwardRef<{ focus: () => void }, PlainInputProps>(fun
: `Obscure ${typeof label === "string" ? label : "field"}`
}
size="xs"
className="mr-0.5 group/obscure !h-auto my-0.5"
className="mr-0.5 group/obscure h-auto! my-0.5"
iconClassName="group-hover/obscure:text"
iconSize="sm"
icon={obscured ? "eye" : "eye_closed"}
@@ -43,7 +43,7 @@ export function RadioCards<T extends string>({
/>
<div
className={classNames(
"mt-1 w-4 h-4 flex-shrink-0 rounded-full border",
"mt-1 w-4 h-4 shrink-0 rounded-full border",
"flex items-center justify-center",
selected ? "border-focus" : "border-border",
)}
@@ -92,7 +92,7 @@ export function SegmentedControl<T extends string>({
role="radio"
tabIndex={isSelected ? 0 : -1}
className={classNames(
isActive && "!text-text",
isActive && "text-text!",
"focus:ring-1 focus:ring-border-focus",
)}
onClick={() => onChange(o.value)}
@@ -111,8 +111,8 @@ export function SegmentedControl<T extends string>({
role="radio"
tabIndex={isSelected ? 0 : -1}
className={classNames(
isActive && "!text-text",
"!px-1.5 !w-auto",
isActive && "text-text!",
"px-1.5! w-auto!",
"focus:ring-border-focus",
)}
title={o.label}
+2 -2
View File
@@ -90,8 +90,8 @@ export function Select<T extends string>({
onBlur={() => setFocused(false)}
disabled={disabled}
className={classNames(
"pr-7 w-full outline-none bg-transparent disabled:opacity-disabled",
"leading-[1] rounded-none", // Center the text better vertically
"pr-7 w-full outline-hidden bg-transparent disabled:opacity-disabled",
"leading-none rounded-none", // Center the text better vertically
)}
>
{isInvalidSelection && <option value={"__NONE__"}>-- Select an Option --</option>}
@@ -189,7 +189,7 @@ export function ModelSettingRowBoolean<M extends AnyModel, K extends ModelKeyOfV
export function SettingRowNumber({
inputClassName,
inputWidthClassName = "!w-48",
inputWidthClassName = "w-48!",
name,
onChange,
placeholder,
@@ -251,7 +251,7 @@ export function ModelSettingRowNumber<M extends AnyModel, K extends ModelKeyOfVa
export function SettingRowText({
inputClassName,
inputWidthClassName = "!w-80",
inputWidthClassName = "w-80!",
name,
onChange,
placeholder,
@@ -358,7 +358,7 @@ export function SettingRowSelect<T extends string>({
name,
onChange,
options,
selectClassName = "!w-48",
selectClassName = "w-48!",
title,
value,
...props
@@ -393,7 +393,7 @@ export function SettingSelectControl<T extends string>({
name,
onChange,
options,
selectClassName = "!w-48",
selectClassName = "w-48!",
value,
}: {
defaultValue?: T;
+4 -2
View File
@@ -1,14 +1,16 @@
import { formatSize } from "@yaakapp-internal/lib/formatSize";
import classNames from "classnames";
interface Props {
className?: string;
contentLength: number;
contentLengthCompressed?: number | null;
}
export function SizeTag({ contentLength, contentLengthCompressed }: Props) {
export function SizeTag({ className, contentLength, contentLengthCompressed }: Props) {
return (
<span
className="font-mono"
className={classNames("font-mono", className)}
title={
`${contentLength} bytes` +
(contentLengthCompressed ? `\n${contentLengthCompressed} bytes compressed` : "")
@@ -342,7 +342,7 @@ export const Tabs = forwardRef<TabsRef, Props>(function Tabs(
<div
className={classNames(
layout === "horizontal" && "flex flex-col w-full pb-3 mb-auto",
layout === "vertical" && "flex flex-row flex-shrink-0 w-full",
layout === "vertical" && "flex flex-row shrink-0 w-full",
)}
>
{tabButtons}
@@ -456,9 +456,9 @@ function TabButton({
onChangeValue?.(tab.value);
},
className: classNames(
"flex items-center rounded whitespace-nowrap",
"!px-2 ml-[1px]",
"outline-none",
"flex items-center rounded-sm whitespace-nowrap",
"px-2! ml-px",
"outline-hidden",
"ring-none",
"focus-visible-or-class:outline-2",
addBorders && "border focus-visible:bg-surface-highlight",
@@ -468,7 +468,7 @@ function TabButton({
: layout === "vertical"
? "border-border-subtle"
: "border-transparent",
layout === "horizontal" && "min-w-[10rem]",
layout === "horizontal" && "min-w-40",
isDragging && "opacity-50",
overlay && "opacity-80",
),
+4 -4
View File
@@ -54,11 +54,11 @@ export function Toast({ children, open, onClose, timeout, action, icon, color }:
`x-theme-toast x-theme-toast--${color}`,
"pointer-events-auto overflow-hidden",
"relative pointer-events-auto bg-surface text-text rounded-lg",
"border border-border shadow-lg w-[25rem]",
"border border-border shadow-lg w-100",
)}
>
<div className="pl-3 py-3 pr-10 flex items-start gap-2 w-full max-h-[11rem] overflow-auto">
{toastIcon && <Icon icon={toastIcon} color={color} className="mt-1 flex-shrink-0" />}
<div className="pl-3 py-3 pr-10 flex items-start gap-2 w-full max-h-44 overflow-auto">
{toastIcon && <Icon icon={toastIcon} color={color} className="mt-1 shrink-0" />}
<VStack space={2} className="w-full min-w-0">
<div className="select-auto">{children}</div>
{action?.({ hide: onClose })}
@@ -68,7 +68,7 @@ export function Toast({ children, open, onClose, timeout, action, icon, color }:
<IconButton
color={color}
variant="border"
className="opacity-60 border-0 !absolute top-2 right-2"
className="opacity-60 border-0 absolute! top-2 right-2"
title="Dismiss"
icon="x"
onClick={onClose}
+4 -4
View File
@@ -116,7 +116,7 @@ export function Tooltip({ children, className, content, tabIndex, size = "md" }:
role="button"
aria-describedby={openState ? id.current : undefined}
tabIndex={tabIndex ?? -1}
className={classNames(className, "flex-grow-0 flex items-center")}
className={classNames(className, "grow-0 flex items-center")}
onClick={handleToggleImmediate}
onMouseEnter={handleOpen}
onMouseLeave={handleClose}
@@ -141,10 +141,10 @@ function Triangle({ className, position }: { className?: string; position: "top"
shapeRendering="crispEdges"
className={classNames(
className,
"absolute z-50 left-[calc(50%-0.4rem)] h-[0.5rem] w-[0.8rem]",
"absolute z-50 left-[calc(50%-0.4rem)] h-2 w-[0.8rem]",
isBottom
? "border-t-[2px] border-surface-highlight -bottom-[calc(0.5rem-3px)] mb-2"
: "border-b-[2px] border-surface-highlight -top-[calc(0.5rem-3px)] mt-2",
? "border-t-2 border-surface-highlight -bottom-[calc(0.5rem-3px)] mb-2"
: "border-b-2 border-surface-highlight -top-[calc(0.5rem-3px)] mt-2",
)}
>
<title>Triangle</title>
@@ -117,7 +117,7 @@ function CommitListItem({
<button
type="button"
className={classNames(
"w-full min-w-0 text-left rounded px-2 py-1.5",
"w-full min-w-0 text-left rounded-sm px-2 py-1.5",
selected && "bg-surface-active",
)}
onClick={onSelect}
@@ -241,7 +241,7 @@ export function GitCommitDialog({ syncDir, onDone, workspace }: Props) {
secondSlot={({ style: innerStyle }) => (
<div style={innerStyle} className="grid grid-rows-[minmax(0,1fr)_auto] gap-3 pb-2">
<Input
className="!text-base font-sans rounded-md"
className="text-base! font-sans rounded-md"
placeholder="Commit message..."
onChange={setMessage}
stateKey={null}
@@ -325,7 +325,7 @@ function TreeNodeChildren({
)}
>
{isSelected && (
<div className="absolute -left-[100vw] right-0 top-0 bottom-0 bg-surface-active opacity-30 -z-10" />
<div className="absolute left-[-100vw] right-0 top-0 bottom-0 bg-surface-active opacity-30 -z-10" />
)}
<Checkbox
checked={checked}
@@ -358,7 +358,7 @@ function TreeNodeChildren({
{node.status.status !== "current" && (
<InlineCode
className={classNames(
"py-0 bg-transparent w-[6rem] text-center shrink-0",
"py-0 bg-transparent w-24 text-center shrink-0",
node.status.status === "modified" && "text-info",
node.status.status === "untracked" && "text-success",
node.status.status === "removed" && "text-danger",
@@ -400,7 +400,7 @@ function ExternalTreeNode({
return (
<Checkbox
fullWidth
className="h-xs w-full hover:bg-surface-highlight rounded px-1 group"
className="h-xs w-full hover:bg-surface-highlight rounded-sm px-1 group"
checked={entry.staged}
onChange={() => onCheck(entry)}
title={
@@ -409,7 +409,7 @@ function ExternalTreeNode({
<div className="truncate">{entry.relaPath}</div>
<InlineCode
className={classNames(
"py-0 ml-auto bg-transparent w-[6rem] text-center",
"py-0 ml-auto bg-transparent w-24 text-center",
entry.status === "modified" && "text-info",
entry.status === "untracked" && "text-success",
entry.status === "removed" && "text-danger",
@@ -559,7 +559,7 @@ const GitMenuButton = forwardRef<HTMLButtonElement, HTMLAttributes<HTMLButtonEle
ref={ref}
className={classNames(
className,
"px-3 h-md border-t border-border flex items-center justify-between text-text-subtle outline-none focus-visible:bg-surface-highlight",
"px-3 h-md border-t border-border flex items-center justify-between text-text-subtle outline-hidden focus-visible:bg-surface-highlight",
)}
{...props}
/>
@@ -113,19 +113,19 @@ export const GraphQLDocsExplorer = memo(function GraphQLDocsExplorer({
name={{ value: "query", color: "primary" }}
item={qryItem}
setItem={setActiveItem}
className="!my-0"
className="my-0!"
/>
<GqlTypeRow
name={{ value: "mutation", color: "primary" }}
item={mutItem}
setItem={setActiveItem}
className="!my-0"
className="my-0!"
/>
<GqlTypeRow
name={{ value: "subscription", color: "primary" }}
item={subItem}
setItem={setActiveItem}
className="!my-0"
className="my-0!"
/>
<Subheading count={Object.keys(allTypes).length}>All Schema Types</Subheading>
<DocMarkdown>{schema.description ?? null}</DocMarkdown>
@@ -192,7 +192,7 @@ function GraphQLExplorerHeader({
noTruncate
item={crumb}
setItem={setItem}
className="!font-sans !text-sm flex-shrink-0"
className="font-sans! text-sm! shrink-0"
/>
)}
</Fragment>
@@ -208,7 +208,7 @@ function GraphQLExplorerHeader({
className="hidden @[10rem]:block"
/>
</div>
<div className="ml-auto flex gap-1 [&>*]:text-text-subtle">
<div className="ml-auto flex gap-1 *:text-text-subtle">
<IconButton icon="x" size="sm" title="Close documentation explorer" onClick={onClose} />
</div>
</nav>
@@ -528,7 +528,7 @@ function GqlTypeRow({
<span className="text-text-subtle">:</span>{" "}
<GqlTypeLink color="notice" item={returnItem} setItem={setItem} />
</div>
<DocMarkdown className="!text-text-subtle mt-0.5">
<DocMarkdown className="text-text-subtle! mt-0.5">
{item.type.description ?? null}
</DocMarkdown>
</div>
@@ -786,8 +786,8 @@ function GqlSchemaSearch({
className={classNames(
className,
"relative flex items-center bg-surface z-20 min-w-0",
!focused && "max-w-[6rem] ml-auto",
focused && "!absolute top-0 left-1.5 right-1.5 bottom-0 pt-1.5",
!focused && "max-w-24 ml-auto",
focused && "absolute! top-0 left-1.5 right-1.5 bottom-0 pt-1.5",
)}
>
<PlainInput
@@ -879,7 +879,7 @@ function SearchResult({
ref={initRef}
className={classNames(
className,
"px-3 truncate w-full text-left h-sm rounded text-editor font-mono",
"px-3 truncate w-full text-left h-sm rounded-sm text-editor font-mono",
isActive && "bg-surface-highlight",
)}
{...extraProps}
@@ -893,7 +893,7 @@ function Heading({ children }: { children: ReactNode }) {
function DocMarkdown({ children, className }: { children: string | null; className?: string }) {
return (
<Markdown className={classNames(className, "!text-text-subtle italic")}>{children}</Markdown>
<Markdown className={classNames(className, "text-text-subtle! italic")}>{children}</Markdown>
);
}
@@ -77,8 +77,8 @@ function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProp
const actions = useMemo<EditorProps["actions"]>(
() => [
<div key="actions" className="flex flex-row !opacity-100 !shadow">
<div key="introspection" className="!opacity-100">
<div key="actions" className="flex flex-row opacity-100! shadow!">
<div key="introspection" className="opacity-100!">
{schema === undefined ? null /* Initializing */ : (
<Dropdown
items={[
@@ -217,7 +217,7 @@ function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProp
stateKey={`graphql_body.${request.id}`}
{...extraEditorProps}
/>
<div className="grid grid-rows-[auto_minmax(0,1fr)] grid-cols-1 min-h-[5rem]">
<div className="grid grid-rows-[auto_minmax(0,1fr)] grid-cols-1 min-h-20">
<Separator dashed className="pb-1">
Variables
</Separator>
@@ -298,7 +298,7 @@ function SseSummaryFooter({
<Markdown className="select-auto cursor-auto">{summary}</Markdown>
</div>
) : (
<pre className="font-mono whitespace-pre-wrap break-words select-auto cursor-auto">
<pre className="font-mono whitespace-pre-wrap wrap-break-word select-auto cursor-auto">
{summary}
</pre>
)
@@ -1,9 +1,12 @@
import type { HttpResponse } from "@yaakapp-internal/models";
import { useMemo, useState } from "react";
import { useCopyHttpResponse } from "../../hooks/useCopyHttpResponse";
import { useResponseBodyText } from "../../hooks/useResponseBodyText";
import { useSaveResponse } from "../../hooks/useSaveResponse";
import { languageFromContentType } from "../../lib/contentType";
import { getContentTypeFromHeaders } from "../../lib/model_util";
import type { EditorProps } from "../core/Editor/Editor";
import { IconButton } from "../core/IconButton";
import { EmptyStateText } from "../EmptyStateText";
import { TextViewer } from "./TextViewer";
import { WebPageViewer } from "./WebPageViewer";
@@ -51,6 +54,9 @@ interface HttpTextViewerProps {
function HttpTextViewer({ response, text, language, pretty, className }: HttpTextViewerProps) {
const [currentFilter, setCurrentFilter] = useState<string | null>(null);
const filteredBody = useResponseBodyText({ response, filter: currentFilter });
const saveResponse = useSaveResponse(response);
const copyResponse = useCopyHttpResponse(response);
const actionsDisabled = response.state !== "closed" && response.status >= 100;
const filterCallback = useMemo(
() => (filter: string) => {
@@ -72,6 +78,26 @@ function HttpTextViewer({ response, text, language, pretty, className }: HttpTex
filterStateKey={`response.body.${response.requestId}`}
pretty={pretty}
className={className}
footerActions={[
<IconButton
key="save"
size="sm"
icon="save"
title="Save response to file"
disabled={actionsDisabled}
onClick={() => saveResponse.mutate()}
className="border !border-border-subtle"
/>,
<IconButton
key="copy"
size="sm"
icon="copy"
title="Copy response body"
disabled={actionsDisabled}
onClick={() => copyResponse.mutate()}
className="border !border-border-subtle"
/>,
]}
onFilter={filterCallback}
/>
);
@@ -63,7 +63,7 @@ export function MultipartViewer({ data, boundary, idPrefix = "multipart" }: Prop
<div className="h-5 w-5 overflow-auto flex items-center justify-end">
<ImageViewer
data={part.arrayBuffer}
className="ml-auto w-auto rounded overflow-hidden"
className="ml-auto w-auto rounded-sm overflow-hidden"
/>
</div>
) : part.filename ? (
@@ -76,7 +76,7 @@ export function MultipartViewer({ data, boundary, idPrefix = "multipart" }: Prop
// oxlint-disable-next-line react/no-array-index-key -- Nothing else to key on
key={idPrefix + part.name + i}
value={tabValue(part, i)}
className="pl-3 !pt-0"
className="pl-3 pt-0!"
>
<Part part={part} />
</TabContent>
@@ -1,6 +1,6 @@
import classNames from "classnames";
import type { ReactNode } from "react";
import { useCallback, useMemo } from "react";
import { Children, useCallback, useMemo } from "react";
import { createGlobalState } from "react-use";
import { useDebouncedValue } from "@yaakapp-internal/ui";
import { useFormatText } from "../../hooks/useFormatText";
@@ -19,6 +19,7 @@ interface Props {
filterStateKey?: string | null;
pretty?: boolean;
className?: string;
footerActions?: ReactNode;
onFilter?: (filter: string) => {
data: string | null | undefined;
isPending: boolean;
@@ -35,6 +36,7 @@ export function TextViewer({
filterStateKey,
pretty,
className,
footerActions,
onFilter,
}: Props) {
const filterKey = filterStateKey ?? stateKey;
@@ -66,13 +68,13 @@ export function TextViewer({
const canFilter = onFilter && (language === "json" || language === "xml" || language === "html");
const actions = useMemo<ReactNode[]>(() => {
const nodes: ReactNode[] = [];
const nodes: ReactNode[] = isSearching ? [] : Children.toArray(footerActions);
if (!canFilter) return nodes;
if (isSearching) {
nodes.push(
<div key="input" className="w-full !opacity-100">
<div key="input" className="w-full opacity-100!">
<Input
key={filterKey ?? "filter"}
validate={!filteredResponse.error}
@@ -100,13 +102,14 @@ export function TextViewer({
icon={isSearching ? "x" : "filter"}
title={isSearching ? "Close filter" : "Filter response"}
onClick={toggleSearch}
className={classNames("border !border-border-subtle", isSearching && "!opacity-100")}
className={classNames("border border-border-subtle!", isSearching && "opacity-100!")}
/>,
);
return nodes;
}, [
canFilter,
footerActions,
filterKey,
filterText,
filteredResponse.error,
@@ -3,10 +3,12 @@ import { copyToClipboard } from "../lib/copy";
import { getResponseBodyText } from "../lib/responseBody";
import { useFastMutation } from "./useFastMutation";
export function useCopyHttpResponse(response: HttpResponse) {
export function useCopyHttpResponse(response: HttpResponse | null) {
return useFastMutation({
mutationKey: ["copy_http_response", response.id],
mutationKey: ["copy_http_response", response?.id],
async mutationFn() {
if (response == null) return;
const body = await getResponseBodyText({ response, filter: null });
copyToClipboard(body);
},
+4 -2
View File
@@ -9,10 +9,12 @@ import { invokeCmd } from "../lib/tauri";
import { showToast } from "../lib/toast";
import { useFastMutation } from "./useFastMutation";
export function useSaveResponse(response: HttpResponse) {
export function useSaveResponse(response: HttpResponse | null) {
return useFastMutation({
mutationKey: ["save_response", response.id],
mutationKey: ["save_response", response?.id],
mutationFn: async () => {
if (response == null) return null;
const request = getModel("http_request", response.requestId);
if (request == null) return null;
@@ -8,7 +8,7 @@ export function useToggleCommandPalette() {
id: "command_palette",
size: "dynamic",
hideX: true,
className: "mb-auto mt-[4rem] !max-h-[min(30rem,calc(100vh-4rem))]",
className: "mb-auto mt-16 max-h-[min(30rem,calc(100vh-4rem))]!",
vAlign: "top",
noPadding: true,
noScroll: true,
+1 -1
View File
@@ -45,7 +45,7 @@ export async function editEnvironment(
id: "environment-editor",
noPadding: true,
size: "lg",
className: "h-[90vh] max-h-[60rem]",
className: "h-[90vh] max-h-240",
render: () => (
<EnvironmentEditDialog
initialEnvironmentId={environment?.id ?? null}
+4 -4
View File
@@ -166,7 +166,7 @@ function showUpdateInstalledToast(version: string) {
action: ({ hide }) => (
<ButtonInfiniteLoading
size="xs"
className="mr-auto min-w-[5rem]"
className="mr-auto min-w-20"
color="primary"
loadingChildren="Restarting..."
onClick={() => {
@@ -206,7 +206,7 @@ async function showUpdateAvailableToast(updateInfo: UpdateInfo) {
<ButtonInfiniteLoading
size="xs"
color="info"
className="min-w-[10rem]"
className="min-w-40"
loadingChildren={downloaded ? "Installing..." : "Downloading..."}
onClick={async () => {
await emit<UpdateResponse>(replyEventId, {
@@ -259,7 +259,7 @@ function showPluginUpdatesToast(updateInfo: PluginUpdateNotification) {
<ButtonInfiniteLoading
size="xs"
color="info"
className="min-w-[5rem]"
className="min-w-20"
loadingChildren="Updating..."
onClick={async () => {
const updated = await updateAllPlugins();
@@ -311,7 +311,7 @@ function showNotificationToast(n: YaakNotification) {
<Button
size="xs"
color={stringToColor(n.color) ?? undefined}
className="mr-auto min-w-[5rem]"
className="mr-auto min-w-20"
rightSlot={<Icon icon="external_link" />}
onClick={() => {
hide();
+1 -1
View File
@@ -61,7 +61,7 @@ export function showErrorToast<T>({
message: (
<div className="w-full">
<h2 className="text-lg font-bold mb-2">{title}</h2>
<div className="whitespace-pre-wrap break-words">{String(message)}</div>
<div className="whitespace-pre-wrap wrap-break-word">{String(message)}</div>
</div>
),
});
+6 -6
View File
@@ -1,6 +1,6 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
@import "../../packages/tailwind-config/index.css";
@source "../../packages/ui/src";
@layer base {
html,
@@ -50,7 +50,7 @@
a,
a[href] * {
@apply cursor-pointer !important;
@apply cursor-pointer!;
}
table th {
@@ -72,14 +72,14 @@
}
&::-webkit-scrollbar-thumb:hover {
@apply opacity-40 !important;
@apply opacity-40!;
}
}
.hide-scrollbars {
&::-webkit-scrollbar-corner,
&::-webkit-scrollbar {
@apply hidden !important;
@apply hidden!;
}
}
-6
View File
@@ -79,8 +79,6 @@
"devDependencies": {
"@lezer/generator": "^1.8.0",
"@rolldown/plugin-babel": "^0.2.3",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
"@tanstack/router-plugin": "^1.127.5",
"@types/babel__core": "^7.20.5",
"@types/node": "^24.0.13",
@@ -94,14 +92,10 @@
"@vitejs/plugin-react": "^6.0.1",
"@yaakapp-internal/theme": "^1.0.0",
"@yaakapp-internal/ui": "^1.0.0",
"autoprefixer": "^10.4.21",
"babel-plugin-react-compiler": "^1.0.0",
"decompress": "^4.2.1",
"internal-ip": "^8.0.0",
"postcss": "^8.5.14",
"postcss-nesting": "^13.0.2",
"rollup": "^4.60.3",
"tailwindcss": "^3.4.17",
"vite": "npm:@voidzero-dev/vite-plus-core@^0.2.1",
"vite-plugin-static-copy": "^3.3.0",
"vite-plugin-svgr": "^4.5.0",
+1 -5
View File
@@ -1,7 +1,3 @@
module.exports = {
plugins: [
require("@tailwindcss/nesting")(require("postcss-nesting")),
require("tailwindcss"),
require("autoprefixer"),
],
plugins: [require("@tailwindcss/postcss")],
};
-16
View File
@@ -1,16 +0,0 @@
const sharedConfig = require("@yaakapp-internal/tailwind-config");
/** @type {import('tailwindcss').Config} */
module.exports = {
...sharedConfig,
content: [
"./*.{html,ts,tsx}",
"./commands/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./hooks/**/*.{ts,tsx}",
"./init/**/*.{ts,tsx}",
"./lib/**/*.{ts,tsx}",
"./routes/**/*.{ts,tsx}",
"../../packages/ui/src/**/*.{ts,tsx}",
],
};
+6 -6
View File
@@ -1,6 +1,6 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
@import "../../packages/tailwind-config/index.css";
@source "../../packages/ui/src";
@layer base {
html,
@@ -46,7 +46,7 @@
a,
a[href] * {
@apply cursor-pointer !important;
@apply cursor-pointer!;
}
table th {
@@ -68,14 +68,14 @@
}
&::-webkit-scrollbar-thumb:hover {
@apply opacity-40 !important;
@apply opacity-40!;
}
}
.hide-scrollbars {
&::-webkit-scrollbar-corner,
&::-webkit-scrollbar {
@apply hidden !important;
@apply hidden!;
}
}

Some files were not shown because too many files have changed in this diff Show More