mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-26 19:31:12 +01:00
Run oxfmt across repo, add format script and docs
Add .oxfmtignore to skip generated bindings and wasm-pack output. Add npm format script, update DEVELOPMENT.md for Vite+ toolchain, and format all non-generated files with oxfmt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,39 +1,39 @@
|
||||
import { useSearch } from '@tanstack/react-router';
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import { type } from '@tauri-apps/plugin-os';
|
||||
import { useLicense } from '@yaakapp-internal/license';
|
||||
import { pluginsAtom, settingsAtom } from '@yaakapp-internal/models';
|
||||
import classNames from 'classnames';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useKeyPressEvent } from 'react-use';
|
||||
import { appInfo } from '../../lib/appInfo';
|
||||
import { capitalize } from '../../lib/capitalize';
|
||||
import { CountBadge } from '../core/CountBadge';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { HStack } from '../core/Stacks';
|
||||
import { TabContent, type TabItem, Tabs } from '../core/Tabs/Tabs';
|
||||
import { HeaderSize } from '../HeaderSize';
|
||||
import { SettingsCertificates } from './SettingsCertificates';
|
||||
import { SettingsGeneral } from './SettingsGeneral';
|
||||
import { SettingsHotkeys } from './SettingsHotkeys';
|
||||
import { SettingsInterface } from './SettingsInterface';
|
||||
import { SettingsLicense } from './SettingsLicense';
|
||||
import { SettingsPlugins } from './SettingsPlugins';
|
||||
import { SettingsProxy } from './SettingsProxy';
|
||||
import { SettingsTheme } from './SettingsTheme';
|
||||
import { useSearch } from "@tanstack/react-router";
|
||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import { type } from "@tauri-apps/plugin-os";
|
||||
import { useLicense } from "@yaakapp-internal/license";
|
||||
import { pluginsAtom, settingsAtom } from "@yaakapp-internal/models";
|
||||
import classNames from "classnames";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useKeyPressEvent } from "react-use";
|
||||
import { appInfo } from "../../lib/appInfo";
|
||||
import { capitalize } from "../../lib/capitalize";
|
||||
import { CountBadge } from "../core/CountBadge";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { HStack } from "../core/Stacks";
|
||||
import { TabContent, type TabItem, Tabs } from "../core/Tabs/Tabs";
|
||||
import { HeaderSize } from "../HeaderSize";
|
||||
import { SettingsCertificates } from "./SettingsCertificates";
|
||||
import { SettingsGeneral } from "./SettingsGeneral";
|
||||
import { SettingsHotkeys } from "./SettingsHotkeys";
|
||||
import { SettingsInterface } from "./SettingsInterface";
|
||||
import { SettingsLicense } from "./SettingsLicense";
|
||||
import { SettingsPlugins } from "./SettingsPlugins";
|
||||
import { SettingsProxy } from "./SettingsProxy";
|
||||
import { SettingsTheme } from "./SettingsTheme";
|
||||
|
||||
interface Props {
|
||||
hide?: () => void;
|
||||
}
|
||||
|
||||
const TAB_GENERAL = 'general';
|
||||
const TAB_INTERFACE = 'interface';
|
||||
const TAB_THEME = 'theme';
|
||||
const TAB_SHORTCUTS = 'shortcuts';
|
||||
const TAB_PROXY = 'proxy';
|
||||
const TAB_CERTIFICATES = 'certificates';
|
||||
const TAB_PLUGINS = 'plugins';
|
||||
const TAB_LICENSE = 'license';
|
||||
const TAB_GENERAL = "general";
|
||||
const TAB_INTERFACE = "interface";
|
||||
const TAB_THEME = "theme";
|
||||
const TAB_SHORTCUTS = "shortcuts";
|
||||
const TAB_PROXY = "proxy";
|
||||
const TAB_CERTIFICATES = "certificates";
|
||||
const TAB_PLUGINS = "plugins";
|
||||
const TAB_LICENSE = "license";
|
||||
const tabs = [
|
||||
TAB_GENERAL,
|
||||
TAB_THEME,
|
||||
@@ -47,16 +47,16 @@ const tabs = [
|
||||
export type SettingsTab = (typeof tabs)[number];
|
||||
|
||||
export default function Settings({ hide }: Props) {
|
||||
const { tab: tabFromQuery } = useSearch({ from: '/workspaces/$workspaceId/settings' });
|
||||
const { tab: tabFromQuery } = useSearch({ from: "/workspaces/$workspaceId/settings" });
|
||||
// Parse tab and subtab (e.g., "plugins:installed")
|
||||
const [mainTab, subtab] = tabFromQuery?.split(':') ?? [];
|
||||
const [mainTab, subtab] = tabFromQuery?.split(":") ?? [];
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
const plugins = useAtomValue(pluginsAtom);
|
||||
const licenseCheck = useLicense();
|
||||
|
||||
// Close settings window on escape
|
||||
// TODO: Could this be put in a better place? Eg. in Rust key listener when creating the window
|
||||
useKeyPressEvent('Escape', async () => {
|
||||
useKeyPressEvent("Escape", async () => {
|
||||
if (hide != null) {
|
||||
// It's being shown in a dialog, so close the dialog
|
||||
hide();
|
||||
@@ -67,7 +67,7 @@ export default function Settings({ hide }: Props) {
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classNames('grid grid-rows-[auto_minmax(0,1fr)] h-full')}>
|
||||
<div className={classNames("grid grid-rows-[auto_minmax(0,1fr)] h-full")}>
|
||||
{hide ? (
|
||||
<span />
|
||||
) : (
|
||||
@@ -83,7 +83,7 @@ export default function Settings({ hide }: Props) {
|
||||
justifyContent="center"
|
||||
className="w-full h-full grid grid-cols-[1fr_auto] pointer-events-none"
|
||||
>
|
||||
<div className={classNames(type() === 'macos' ? 'text-center' : 'pl-2')}>Settings</div>
|
||||
<div className={classNames(type() === "macos" ? "text-center" : "pl-2")}>Settings</div>
|
||||
</HStack>
|
||||
</HeaderSize>
|
||||
)}
|
||||
@@ -120,10 +120,10 @@ export default function Settings({ hide }: Props) {
|
||||
value === TAB_CERTIFICATES ? (
|
||||
<CountBadge count={settings.clientCertificates.length} />
|
||||
) : value === TAB_PLUGINS ? (
|
||||
<CountBadge count={plugins.filter((p) => p.source !== 'bundled').length} />
|
||||
) : value === TAB_PROXY && settings.proxy?.type === 'enabled' ? (
|
||||
<CountBadge count={plugins.filter((p) => p.source !== "bundled").length} />
|
||||
) : value === TAB_PROXY && settings.proxy?.type === "enabled" ? (
|
||||
<CountBadge count />
|
||||
) : value === TAB_LICENSE && licenseCheck.check.data?.status === 'personal_use' ? (
|
||||
) : value === TAB_LICENSE && licenseCheck.check.data?.status === "personal_use" ? (
|
||||
<CountBadge count color="notice" />
|
||||
) : null,
|
||||
}),
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import type { ClientCertificate } from '@yaakapp-internal/models';
|
||||
import { patchModel, settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useRef } from 'react';
|
||||
import { showConfirmDelete } from '../../lib/confirm';
|
||||
import { Button } from '../core/Button';
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import { DetailsBanner } from '../core/DetailsBanner';
|
||||
import { Heading } from '../core/Heading';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { PlainInput } from '../core/PlainInput';
|
||||
import { Separator } from '../core/Separator';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
import { SelectFile } from '../SelectFile';
|
||||
import type { ClientCertificate } from "@yaakapp-internal/models";
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useRef } from "react";
|
||||
import { showConfirmDelete } from "../../lib/confirm";
|
||||
import { Button } from "../core/Button";
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { DetailsBanner } from "../core/DetailsBanner";
|
||||
import { Heading } from "../core/Heading";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { InlineCode } from "../core/InlineCode";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { Separator } from "../core/Separator";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
import { SelectFile } from "../SelectFile";
|
||||
|
||||
function createEmptyCertificate(): ClientCertificate {
|
||||
return {
|
||||
host: '',
|
||||
host: "",
|
||||
port: null,
|
||||
crtFile: null,
|
||||
keyFile: null,
|
||||
@@ -44,11 +44,11 @@ function CertificateEditor({ certificate, index, onUpdate, onRemove }: Certifica
|
||||
const hasPfx = Boolean(certificate.pfxFile && certificate.pfxFile.length > 0);
|
||||
const hasCrtKey = Boolean(
|
||||
(certificate.crtFile && certificate.crtFile.length > 0) ||
|
||||
(certificate.keyFile && certificate.keyFile.length > 0),
|
||||
(certificate.keyFile && certificate.keyFile.length > 0),
|
||||
);
|
||||
|
||||
// Determine certificate type for display
|
||||
const certType = hasPfx ? 'PFX' : hasCrtKey ? 'CERT' : null;
|
||||
const certType = hasPfx ? "PFX" : hasCrtKey ? "CERT" : null;
|
||||
const defaultOpen = useRef<boolean>(!certificate.host);
|
||||
|
||||
return (
|
||||
@@ -60,9 +60,9 @@ function CertificateEditor({ certificate, index, onUpdate, onRemove }: Certifica
|
||||
<Checkbox
|
||||
className="ml-1"
|
||||
checked={certificate.enabled ?? true}
|
||||
title={certificate.enabled ? 'Disable certificate' : 'Enable certificate'}
|
||||
title={certificate.enabled ? "Disable certificate" : "Enable certificate"}
|
||||
hideLabel
|
||||
onChange={(enabled) => updateField('enabled', enabled)}
|
||||
onChange={(enabled) => updateField("enabled", enabled)}
|
||||
/>
|
||||
|
||||
{certificate.host ? (
|
||||
@@ -103,7 +103,7 @@ function CertificateEditor({ certificate, index, onUpdate, onRemove }: Certifica
|
||||
size="sm"
|
||||
required
|
||||
defaultValue={certificate.host}
|
||||
onChange={(host) => updateField('host', host)}
|
||||
onChange={(host) => updateField("host", host)}
|
||||
/>
|
||||
<PlainInput
|
||||
label="Port"
|
||||
@@ -121,8 +121,8 @@ function CertificateEditor({ certificate, index, onUpdate, onRemove }: Certifica
|
||||
}
|
||||
size="sm"
|
||||
className="w-24"
|
||||
defaultValue={certificate.port?.toString() ?? ''}
|
||||
onChange={(port) => updateField('port', port ? parseInt(port, 10) : null)}
|
||||
defaultValue={certificate.port?.toString() ?? ""}
|
||||
onChange={(port) => updateField("port", port ? parseInt(port, 10) : null)}
|
||||
/>
|
||||
</HStack>
|
||||
|
||||
@@ -135,7 +135,7 @@ function CertificateEditor({ certificate, index, onUpdate, onRemove }: Certifica
|
||||
filePath={certificate.crtFile ?? null}
|
||||
size="sm"
|
||||
disabled={hasPfx}
|
||||
onChange={({ filePath }) => updateField('crtFile', filePath)}
|
||||
onChange={({ filePath }) => updateField("crtFile", filePath)}
|
||||
/>
|
||||
<SelectFile
|
||||
label="KEY File"
|
||||
@@ -143,7 +143,7 @@ function CertificateEditor({ certificate, index, onUpdate, onRemove }: Certifica
|
||||
filePath={certificate.keyFile ?? null}
|
||||
size="sm"
|
||||
disabled={hasPfx}
|
||||
onChange={({ filePath }) => updateField('keyFile', filePath)}
|
||||
onChange={({ filePath }) => updateField("keyFile", filePath)}
|
||||
/>
|
||||
</VStack>
|
||||
|
||||
@@ -155,15 +155,15 @@ function CertificateEditor({ certificate, index, onUpdate, onRemove }: Certifica
|
||||
filePath={certificate.pfxFile ?? null}
|
||||
size="sm"
|
||||
disabled={hasCrtKey}
|
||||
onChange={({ filePath }) => updateField('pfxFile', filePath)}
|
||||
onChange={({ filePath }) => updateField("pfxFile", filePath)}
|
||||
/>
|
||||
|
||||
<PlainInput
|
||||
label="Passphrase"
|
||||
size="sm"
|
||||
type="password"
|
||||
defaultValue={certificate.passphrase ?? ''}
|
||||
onChange={(passphrase) => updateField('passphrase', passphrase || null)}
|
||||
defaultValue={certificate.passphrase ?? ""}
|
||||
onChange={(passphrase) => updateField("passphrase", passphrase || null)}
|
||||
/>
|
||||
</VStack>
|
||||
</DetailsBanner>
|
||||
@@ -193,15 +193,15 @@ export function SettingsCertificates() {
|
||||
const cert = certificates[index];
|
||||
if (cert == null) return;
|
||||
|
||||
const host = cert.host || 'this certificate';
|
||||
const port = cert.port != null ? `:${cert.port}` : '';
|
||||
const host = cert.host || "this certificate";
|
||||
const port = cert.port != null ? `:${cert.port}` : "";
|
||||
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'confirm-remove-certificate',
|
||||
title: 'Delete Certificate',
|
||||
id: "confirm-remove-certificate",
|
||||
title: "Delete Certificate",
|
||||
description: (
|
||||
<>
|
||||
Permanently delete certificate for{' '}
|
||||
Permanently delete certificate for{" "}
|
||||
<InlineCode>
|
||||
{host}
|
||||
{port}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { revealItemInDir } from '@tauri-apps/plugin-opener';
|
||||
import { patchModel, settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { activeWorkspaceAtom } from '../../hooks/useActiveWorkspace';
|
||||
import { useCheckForUpdates } from '../../hooks/useCheckForUpdates';
|
||||
import { appInfo } from '../../lib/appInfo';
|
||||
import { revealInFinderText } from '../../lib/reveal';
|
||||
import { CargoFeature } from '../CargoFeature';
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import { Heading } from '../core/Heading';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import { KeyValueRow, KeyValueRows } from '../core/KeyValueRow';
|
||||
import { PlainInput } from '../core/PlainInput';
|
||||
import { Select } from '../core/Select';
|
||||
import { Separator } from '../core/Separator';
|
||||
import { VStack } from '../core/Stacks';
|
||||
import { revealItemInDir } from "@tauri-apps/plugin-opener";
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { activeWorkspaceAtom } from "../../hooks/useActiveWorkspace";
|
||||
import { useCheckForUpdates } from "../../hooks/useCheckForUpdates";
|
||||
import { appInfo } from "../../lib/appInfo";
|
||||
import { revealInFinderText } from "../../lib/reveal";
|
||||
import { CargoFeature } from "../CargoFeature";
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { Heading } from "../core/Heading";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { KeyValueRow, KeyValueRows } from "../core/KeyValueRow";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { Select } from "../core/Select";
|
||||
import { Separator } from "../core/Separator";
|
||||
import { VStack } from "../core/Stacks";
|
||||
|
||||
export function SettingsGeneral() {
|
||||
const workspace = useAtomValue(activeWorkspaceAtom);
|
||||
@@ -41,8 +41,8 @@ export function SettingsGeneral() {
|
||||
value={settings.updateChannel}
|
||||
onChange={(updateChannel) => patchModel(settings, { updateChannel })}
|
||||
options={[
|
||||
{ label: 'Stable', value: 'stable' },
|
||||
{ label: 'Beta (more frequent)', value: 'beta' },
|
||||
{ label: "Stable", value: "stable" },
|
||||
{ label: "Beta (more frequent)", value: "beta" },
|
||||
]}
|
||||
/>
|
||||
<IconButton
|
||||
@@ -57,15 +57,15 @@ export function SettingsGeneral() {
|
||||
|
||||
<Select
|
||||
name="autoupdate"
|
||||
value={settings.autoupdate ? 'auto' : 'manual'}
|
||||
value={settings.autoupdate ? "auto" : "manual"}
|
||||
label="Update Behavior"
|
||||
labelPosition="left"
|
||||
size="sm"
|
||||
labelClassName="w-[14rem]"
|
||||
onChange={(v) => patchModel(settings, { autoupdate: v === 'auto' })}
|
||||
onChange={(v) => patchModel(settings, { autoupdate: v === "auto" })}
|
||||
options={[
|
||||
{ label: 'Automatic', value: 'auto' },
|
||||
{ label: 'Manual', value: 'manual' },
|
||||
{ label: "Automatic", value: "auto" },
|
||||
{ label: "Manual", value: "manual" },
|
||||
]}
|
||||
/>
|
||||
<Checkbox
|
||||
@@ -97,7 +97,7 @@ export function SettingsGeneral() {
|
||||
<Separator className="my-4" />
|
||||
|
||||
<Heading level={2}>
|
||||
Workspace{' '}
|
||||
Workspace{" "}
|
||||
<div className="inline-block ml-1 bg-surface-highlight px-2 py-0.5 rounded text text-shrink">
|
||||
{workspace.name}
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { patchModel, settingsAtom } from '@yaakapp-internal/models';
|
||||
import classNames from 'classnames';
|
||||
import { fuzzyMatch } from 'fuzzbunny';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import classNames from "classnames";
|
||||
import { fuzzyMatch } from "fuzzbunny";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
defaultHotkeys,
|
||||
formatHotkeyString,
|
||||
@@ -11,27 +11,27 @@ import {
|
||||
hotkeyActions,
|
||||
hotkeysAtom,
|
||||
useHotkeyLabel,
|
||||
} from '../../hooks/useHotKey';
|
||||
import { capitalize } from '../../lib/capitalize';
|
||||
import { showDialog } from '../../lib/dialog';
|
||||
import { Button } from '../core/Button';
|
||||
import { Dropdown, type DropdownItem } from '../core/Dropdown';
|
||||
import { Heading } from '../core/Heading';
|
||||
import { HotkeyRaw } from '../core/Hotkey';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import { PlainInput } from '../core/PlainInput';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from '../core/Table';
|
||||
} from "../../hooks/useHotKey";
|
||||
import { capitalize } from "../../lib/capitalize";
|
||||
import { showDialog } from "../../lib/dialog";
|
||||
import { Button } from "../core/Button";
|
||||
import { Dropdown, type DropdownItem } from "../core/Dropdown";
|
||||
import { Heading } from "../core/Heading";
|
||||
import { HotkeyRaw } from "../core/Hotkey";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "../core/Table";
|
||||
|
||||
const HOLD_KEYS = ['Shift', 'Control', 'Alt', 'Meta'];
|
||||
const HOLD_KEYS = ["Shift", "Control", "Alt", "Meta"];
|
||||
const LAYOUT_INSENSITIVE_KEYS = [
|
||||
'Equal',
|
||||
'Minus',
|
||||
'BracketLeft',
|
||||
'BracketRight',
|
||||
'Backquote',
|
||||
'Space',
|
||||
"Equal",
|
||||
"Minus",
|
||||
"BracketLeft",
|
||||
"BracketRight",
|
||||
"Backquote",
|
||||
"Space",
|
||||
];
|
||||
|
||||
/** Convert a KeyboardEvent to a hotkey string like "Meta+Shift+k" or "Control+Shift+k" */
|
||||
@@ -45,37 +45,37 @@ function eventToHotkeyString(e: KeyboardEvent): string | null {
|
||||
|
||||
// Add modifiers in consistent order (Meta, Control, Alt, Shift)
|
||||
if (e.metaKey) {
|
||||
parts.push('Meta');
|
||||
parts.push("Meta");
|
||||
}
|
||||
if (e.ctrlKey) {
|
||||
parts.push('Control');
|
||||
parts.push("Control");
|
||||
}
|
||||
if (e.altKey) {
|
||||
parts.push('Alt');
|
||||
parts.push("Alt");
|
||||
}
|
||||
if (e.shiftKey) {
|
||||
parts.push('Shift');
|
||||
parts.push("Shift");
|
||||
}
|
||||
|
||||
// Get the main key - use the same logic as useHotKey.ts
|
||||
const key = LAYOUT_INSENSITIVE_KEYS.includes(e.code) ? e.code : e.key;
|
||||
parts.push(key);
|
||||
|
||||
return parts.join('+');
|
||||
return parts.join("+");
|
||||
}
|
||||
|
||||
export function SettingsHotkeys() {
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
const hotkeys = useAtomValue(hotkeysAtom);
|
||||
const [filter, setFilter] = useState('');
|
||||
const [filter, setFilter] = useState("");
|
||||
|
||||
const filteredActions = useMemo(() => {
|
||||
if (!filter.trim()) {
|
||||
return hotkeyActions;
|
||||
}
|
||||
return hotkeyActions.filter((action) => {
|
||||
const scope = getHotkeyScope(action).replace(/_/g, ' ');
|
||||
const label = action.replace(/[_.]/g, ' ');
|
||||
const scope = getHotkeyScope(action).replace(/_/g, " ");
|
||||
const label = action.replace(/[_.]/g, " ");
|
||||
const searchText = `${scope} ${label}`;
|
||||
return fuzzyMatch(searchText, filter) != null;
|
||||
});
|
||||
@@ -152,7 +152,7 @@ interface HotkeyRowProps {
|
||||
|
||||
function HotkeyRow({ action, currentKeys, defaultKeys, onSave, onReset }: HotkeyRowProps) {
|
||||
const label = useHotkeyLabel(action);
|
||||
const scope = capitalize(getHotkeyScope(action).replace(/_/g, ' '));
|
||||
const scope = capitalize(getHotkeyScope(action).replace(/_/g, " "));
|
||||
const isCustomized = !arraysEqual(currentKeys, defaultKeys);
|
||||
const isDisabled = currentKeys.length === 0;
|
||||
|
||||
@@ -160,7 +160,7 @@ function HotkeyRow({ action, currentKeys, defaultKeys, onSave, onReset }: Hotkey
|
||||
showDialog({
|
||||
id: `record-hotkey-${action}`,
|
||||
title: label,
|
||||
size: 'sm',
|
||||
size: "sm",
|
||||
render: ({ hide }) => (
|
||||
<RecordHotkeyDialog
|
||||
label={label}
|
||||
@@ -189,7 +189,7 @@ function HotkeyRow({ action, currentKeys, defaultKeys, onSave, onReset }: Hotkey
|
||||
// Build dropdown items dynamically
|
||||
const dropdownItems: DropdownItem[] = [
|
||||
{
|
||||
label: 'Add Keyboard Shortcut',
|
||||
label: "Add Keyboard Shortcut",
|
||||
leftSlot: <Icon icon="plus" />,
|
||||
onSelect: handleStartRecording,
|
||||
},
|
||||
@@ -213,10 +213,10 @@ function HotkeyRow({ action, currentKeys, defaultKeys, onSave, onReset }: Hotkey
|
||||
if (currentKeys.length > 1) {
|
||||
dropdownItems.push(
|
||||
{
|
||||
type: 'separator',
|
||||
type: "separator",
|
||||
},
|
||||
{
|
||||
label: 'Remove All Shortcuts',
|
||||
label: "Remove All Shortcuts",
|
||||
leftSlot: <Icon icon="trash" />,
|
||||
onSelect: handleClearAll,
|
||||
},
|
||||
@@ -226,10 +226,10 @@ function HotkeyRow({ action, currentKeys, defaultKeys, onSave, onReset }: Hotkey
|
||||
|
||||
if (isCustomized) {
|
||||
dropdownItems.push({
|
||||
type: 'separator',
|
||||
type: "separator",
|
||||
});
|
||||
dropdownItems.push({
|
||||
label: 'Reset to Default',
|
||||
label: "Reset to Default",
|
||||
leftSlot: <Icon icon="refresh" />,
|
||||
onSelect: onReset,
|
||||
});
|
||||
@@ -292,7 +292,7 @@ function RecordHotkeyDialog({ label, onSave, onCancel }: RecordHotkeyDialogProps
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
if (e.key === "Escape") {
|
||||
onCancel();
|
||||
return;
|
||||
}
|
||||
@@ -303,9 +303,9 @@ function RecordHotkeyDialog({ label, onSave, onCancel }: RecordHotkeyDialogProps
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown, { capture: true });
|
||||
window.addEventListener("keydown", handleKeyDown, { capture: true });
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown, { capture: true });
|
||||
window.removeEventListener("keydown", handleKeyDown, { capture: true });
|
||||
};
|
||||
}, [isFocused, onCancel]);
|
||||
|
||||
@@ -332,9 +332,9 @@ function RecordHotkeyDialog({ label, onSave, onCancel }: RecordHotkeyDialogProps
|
||||
e.currentTarget.focus();
|
||||
}}
|
||||
className={classNames(
|
||||
'flex items-center justify-center',
|
||||
'px-4 py-2 rounded-lg bg-surface-highlight border outline-none cursor-default w-full',
|
||||
'border-border-subtle focus:border-border-focus',
|
||||
"flex items-center justify-center",
|
||||
"px-4 py-2 rounded-lg bg-surface-highlight border outline-none cursor-default w-full",
|
||||
"border-border-subtle focus:border-border-focus",
|
||||
)}
|
||||
>
|
||||
{recordedKey ? (
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import { type } from '@tauri-apps/plugin-os';
|
||||
import { useFonts } from '@yaakapp-internal/fonts';
|
||||
import { useLicense } from '@yaakapp-internal/license';
|
||||
import type { EditorKeymap, Settings } from '@yaakapp-internal/models';
|
||||
import { patchModel, settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useState } from 'react';
|
||||
import { type } from "@tauri-apps/plugin-os";
|
||||
import { useFonts } from "@yaakapp-internal/fonts";
|
||||
import { useLicense } from "@yaakapp-internal/license";
|
||||
import type { EditorKeymap, Settings } from "@yaakapp-internal/models";
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useState } from "react";
|
||||
|
||||
import { activeWorkspaceAtom } from '../../hooks/useActiveWorkspace';
|
||||
import { clamp } from '../../lib/clamp';
|
||||
import { showConfirm } from '../../lib/confirm';
|
||||
import { invokeCmd } from '../../lib/tauri';
|
||||
import { CargoFeature } from '../CargoFeature';
|
||||
import { Button } from '../core/Button';
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import { Heading } from '../core/Heading';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { Link } from '../core/Link';
|
||||
import { Select } from '../core/Select';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
import { activeWorkspaceAtom } from "../../hooks/useActiveWorkspace";
|
||||
import { clamp } from "../../lib/clamp";
|
||||
import { showConfirm } from "../../lib/confirm";
|
||||
import { invokeCmd } from "../../lib/tauri";
|
||||
import { CargoFeature } from "../CargoFeature";
|
||||
import { Button } from "../core/Button";
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { Heading } from "../core/Heading";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { Link } from "../core/Link";
|
||||
import { Select } from "../core/Select";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
|
||||
const NULL_FONT_VALUE = '__NULL_FONT__';
|
||||
const NULL_FONT_VALUE = "__NULL_FONT__";
|
||||
|
||||
const fontSizeOptions = [
|
||||
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
|
||||
].map((n) => ({ label: `${n}`, value: `${n}` }));
|
||||
|
||||
const keymaps: { value: EditorKeymap; label: string }[] = [
|
||||
{ value: 'default', label: 'Default' },
|
||||
{ value: 'vim', label: 'Vim' },
|
||||
{ value: 'vscode', label: 'VSCode' },
|
||||
{ value: 'emacs', label: 'Emacs' },
|
||||
{ value: "default", label: "Default" },
|
||||
{ value: "vim", label: "Vim" },
|
||||
{ value: "vscode", label: "VSCode" },
|
||||
{ value: "emacs", label: "Emacs" },
|
||||
];
|
||||
|
||||
export function SettingsInterface() {
|
||||
@@ -54,20 +54,20 @@ export function SettingsInterface() {
|
||||
help="When opening a workspace, should it open in the current window or a new window?"
|
||||
value={
|
||||
settings.openWorkspaceNewWindow === true
|
||||
? 'new'
|
||||
? "new"
|
||||
: settings.openWorkspaceNewWindow === false
|
||||
? 'current'
|
||||
: 'ask'
|
||||
? "current"
|
||||
: "ask"
|
||||
}
|
||||
onChange={async (v) => {
|
||||
if (v === 'current') await patchModel(settings, { openWorkspaceNewWindow: false });
|
||||
else if (v === 'new') await patchModel(settings, { openWorkspaceNewWindow: true });
|
||||
if (v === "current") await patchModel(settings, { openWorkspaceNewWindow: false });
|
||||
else if (v === "new") await patchModel(settings, { openWorkspaceNewWindow: true });
|
||||
else await patchModel(settings, { openWorkspaceNewWindow: null });
|
||||
}}
|
||||
options={[
|
||||
{ label: 'Always ask', value: 'ask' },
|
||||
{ label: 'Open in current window', value: 'current' },
|
||||
{ label: 'Open in new window', value: 'new' },
|
||||
{ label: "Always ask", value: "ask" },
|
||||
{ label: "Open in current window", value: "current" },
|
||||
{ label: "Open in new window", value: "new" },
|
||||
]}
|
||||
/>
|
||||
<HStack space={2} alignItems="end">
|
||||
@@ -78,7 +78,7 @@ export function SettingsInterface() {
|
||||
label="Interface font"
|
||||
value={settings.interfaceFont ?? NULL_FONT_VALUE}
|
||||
options={[
|
||||
{ label: 'System default', value: NULL_FONT_VALUE },
|
||||
{ label: "System default", value: NULL_FONT_VALUE },
|
||||
...(fonts.data.uiFonts.map((f) => ({
|
||||
label: f,
|
||||
value: f,
|
||||
@@ -114,7 +114,7 @@ export function SettingsInterface() {
|
||||
label="Editor font"
|
||||
value={settings.editorFont ?? NULL_FONT_VALUE}
|
||||
options={[
|
||||
{ label: 'System default', value: NULL_FONT_VALUE },
|
||||
{ label: "System default", value: NULL_FONT_VALUE },
|
||||
...(fonts.data.editorFonts.map((f) => ({
|
||||
label: f,
|
||||
value: f,
|
||||
@@ -164,7 +164,7 @@ export function SettingsInterface() {
|
||||
|
||||
<NativeTitlebarSetting settings={settings} />
|
||||
|
||||
{type() !== 'macos' && (
|
||||
{type() !== "macos" && (
|
||||
<Checkbox
|
||||
checked={settings.hideWindowControls}
|
||||
title="Hide window controls"
|
||||
@@ -192,7 +192,7 @@ function NativeTitlebarSetting({ settings }: { settings: Settings }) {
|
||||
size="2xs"
|
||||
onClick={async () => {
|
||||
await patchModel(settings, { useNativeTitlebar: nativeTitlebar });
|
||||
await invokeCmd('cmd_restart');
|
||||
await invokeCmd("cmd_restart");
|
||||
}}
|
||||
>
|
||||
Apply and Restart
|
||||
@@ -204,7 +204,7 @@ function NativeTitlebarSetting({ settings }: { settings: Settings }) {
|
||||
|
||||
function LicenseSettings({ settings }: { settings: Settings }) {
|
||||
const license = useLicense();
|
||||
if (license.check.data?.status !== 'personal_use') {
|
||||
if (license.check.data?.status !== "personal_use") {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -215,24 +215,24 @@ function LicenseSettings({ settings }: { settings: Settings }) {
|
||||
onChange={async (hideLicenseBadge) => {
|
||||
if (hideLicenseBadge) {
|
||||
const confirmed = await showConfirm({
|
||||
id: 'hide-license-badge',
|
||||
title: 'Confirm Personal Use',
|
||||
confirmText: 'Confirm',
|
||||
id: "hide-license-badge",
|
||||
title: "Confirm Personal Use",
|
||||
confirmText: "Confirm",
|
||||
description: (
|
||||
<VStack space={3}>
|
||||
<p>Hey there 👋🏼</p>
|
||||
<p>
|
||||
Yaak is free for personal projects and learning.{' '}
|
||||
Yaak is free for personal projects and learning.{" "}
|
||||
<strong>If you’re using Yaak at work, a license is required.</strong>
|
||||
</p>
|
||||
<p>
|
||||
Licenses help keep Yaak independent and sustainable.{' '}
|
||||
Licenses help keep Yaak independent and sustainable.{" "}
|
||||
<Link href="https://yaak.app/pricing?s=badge">Purchase a License →</Link>
|
||||
</p>
|
||||
</VStack>
|
||||
),
|
||||
requireTyping: 'Personal Use',
|
||||
color: 'info',
|
||||
requireTyping: "Personal Use",
|
||||
color: "info",
|
||||
});
|
||||
if (!confirmed) {
|
||||
return; // Cancel
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||
import { useLicense } from '@yaakapp-internal/license';
|
||||
import { differenceInDays } from 'date-fns';
|
||||
import { formatDate } from 'date-fns/format';
|
||||
import { useState } from 'react';
|
||||
import { useToggle } from '../../hooks/useToggle';
|
||||
import { pluralizeCount } from '../../lib/pluralize';
|
||||
import { CargoFeature } from '../CargoFeature';
|
||||
import { Banner } from '../core/Banner';
|
||||
import { Button } from '../core/Button';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { Link } from '../core/Link';
|
||||
import { PlainInput } from '../core/PlainInput';
|
||||
import { Separator } from '../core/Separator';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { useLicense } from "@yaakapp-internal/license";
|
||||
import { differenceInDays } from "date-fns";
|
||||
import { formatDate } from "date-fns/format";
|
||||
import { useState } from "react";
|
||||
import { useToggle } from "../../hooks/useToggle";
|
||||
import { pluralizeCount } from "../../lib/pluralize";
|
||||
import { CargoFeature } from "../CargoFeature";
|
||||
import { Banner } from "../core/Banner";
|
||||
import { Button } from "../core/Button";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { Link } from "../core/Link";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { Separator } from "../core/Separator";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
|
||||
export function SettingsLicense() {
|
||||
return (
|
||||
@@ -24,7 +24,7 @@ export function SettingsLicense() {
|
||||
|
||||
function SettingsLicenseCmp() {
|
||||
const { check, activate, deactivate } = useLicense();
|
||||
const [key, setKey] = useState<string>('');
|
||||
const [key, setKey] = useState<string>("");
|
||||
const [activateFormVisible, toggleActivateFormVisible] = useToggle(false);
|
||||
|
||||
if (check.isPending) {
|
||||
@@ -35,16 +35,16 @@ function SettingsLicenseCmp() {
|
||||
if (!check.data) return null;
|
||||
|
||||
switch (check.data.status) {
|
||||
case 'active':
|
||||
case "active":
|
||||
return <Banner color="success">Your license is active 🥳</Banner>;
|
||||
|
||||
case 'trialing':
|
||||
case "trialing":
|
||||
return (
|
||||
<Banner color="info" className="max-w-lg">
|
||||
<p className="w-full">
|
||||
<strong>
|
||||
{pluralizeCount('day', differenceInDays(check.data.data.end, new Date()))}
|
||||
</strong>{' '}
|
||||
{pluralizeCount("day", differenceInDays(check.data.data.end, new Date()))}
|
||||
</strong>{" "}
|
||||
left to evaluate Yaak for commercial use.
|
||||
<br />
|
||||
<span className="opacity-50">Personal use is always free, forever.</span>
|
||||
@@ -58,7 +58,7 @@ function SettingsLicenseCmp() {
|
||||
</Banner>
|
||||
);
|
||||
|
||||
case 'personal_use':
|
||||
case "personal_use":
|
||||
return (
|
||||
<Banner color="notice" className="max-w-lg">
|
||||
<p className="w-full">
|
||||
@@ -78,19 +78,19 @@ function SettingsLicenseCmp() {
|
||||
</Banner>
|
||||
);
|
||||
|
||||
case 'inactive':
|
||||
case "inactive":
|
||||
return (
|
||||
<Banner color="danger">
|
||||
Your license is invalid. Please <Link href="https://yaak.app/dashboard">Sign In</Link>{' '}
|
||||
Your license is invalid. Please <Link href="https://yaak.app/dashboard">Sign In</Link>{" "}
|
||||
for more details
|
||||
</Banner>
|
||||
);
|
||||
|
||||
case 'expired':
|
||||
case "expired":
|
||||
return (
|
||||
<Banner color="notice">
|
||||
Your license expired{' '}
|
||||
<strong>{formatDate(check.data.data.periodEnd, 'MMMM dd, yyyy')}</strong>. Please{' '}
|
||||
Your license expired{" "}
|
||||
<strong>{formatDate(check.data.data.periodEnd, "MMMM dd, yyyy")}</strong>. Please{" "}
|
||||
<Link href="https://yaak.app/dashboard">Resubscribe</Link> to continue receiving
|
||||
updates.
|
||||
{check.data.data.changesUrl && (
|
||||
@@ -102,17 +102,17 @@ function SettingsLicenseCmp() {
|
||||
</Banner>
|
||||
);
|
||||
|
||||
case 'past_due':
|
||||
case "past_due":
|
||||
return (
|
||||
<Banner color="danger">
|
||||
<strong>Your payment method needs attention.</strong>
|
||||
<br />
|
||||
To re-activate your license, please{' '}
|
||||
To re-activate your license, please{" "}
|
||||
<Link href={check.data.data.billingUrl}>update your billing info</Link>.
|
||||
</Banner>
|
||||
);
|
||||
|
||||
case 'error':
|
||||
case "error":
|
||||
return (
|
||||
<Banner color="danger">
|
||||
License check failed: {check.data.data.message} (Code: {check.data.data.code})
|
||||
@@ -128,7 +128,7 @@ function SettingsLicenseCmp() {
|
||||
{check.error && <Banner color="danger">{check.error}</Banner>}
|
||||
{activate.error && <Banner color="danger">{activate.error}</Banner>}
|
||||
|
||||
{check.data?.status === 'active' ? (
|
||||
{check.data?.status === "active" ? (
|
||||
<HStack space={2}>
|
||||
<Button variant="border" color="secondary" size="sm" onClick={() => deactivate.mutate()}>
|
||||
Deactivate License
|
||||
@@ -136,7 +136,7 @@ function SettingsLicenseCmp() {
|
||||
<Button
|
||||
color="secondary"
|
||||
size="sm"
|
||||
onClick={() => openUrl('https://yaak.app/dashboard?s=support&ref=app.yaak.desktop')}
|
||||
onClick={() => openUrl("https://yaak.app/dashboard?s=support&ref=app.yaak.desktop")}
|
||||
rightSlot={<Icon icon="external_link" />}
|
||||
>
|
||||
Direct Support
|
||||
@@ -153,7 +153,7 @@ function SettingsLicenseCmp() {
|
||||
rightSlot={<Icon icon="external_link" />}
|
||||
onClick={() =>
|
||||
openUrl(
|
||||
`https://yaak.app/pricing?s=purchase&ref=app.yaak.desktop&t=${check.data?.status ?? ''}`,
|
||||
`https://yaak.app/pricing?s=purchase&ref=app.yaak.desktop&t=${check.data?.status ?? ""}`,
|
||||
)
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||
import type { Plugin } from '@yaakapp-internal/models';
|
||||
import { patchModel, pluginsAtom } from '@yaakapp-internal/models';
|
||||
import type { PluginVersion } from '@yaakapp-internal/plugins';
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import type { Plugin } from "@yaakapp-internal/models";
|
||||
import { patchModel, pluginsAtom } from "@yaakapp-internal/models";
|
||||
import type { PluginVersion } from "@yaakapp-internal/plugins";
|
||||
import {
|
||||
checkPluginUpdates,
|
||||
installPlugin,
|
||||
searchPlugins,
|
||||
uninstallPlugin,
|
||||
} from '@yaakapp-internal/plugins';
|
||||
import classNames from 'classnames';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useState } from 'react';
|
||||
import { useDebouncedValue } from '../../hooks/useDebouncedValue';
|
||||
import { useInstallPlugin } from '../../hooks/useInstallPlugin';
|
||||
import { usePluginInfo } from '../../hooks/usePluginInfo';
|
||||
import { usePluginsKey, useRefreshPlugins } from '../../hooks/usePlugins';
|
||||
import { showConfirmDelete } from '../../lib/confirm';
|
||||
import { minPromiseMillis } from '../../lib/minPromiseMillis';
|
||||
import { Button } from '../core/Button';
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import { CountBadge } from '../core/CountBadge';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { Link } from '../core/Link';
|
||||
import { LoadingIcon } from '../core/LoadingIcon';
|
||||
import { PlainInput } from '../core/PlainInput';
|
||||
import { HStack } from '../core/Stacks';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from '../core/Table';
|
||||
import { TabContent, Tabs } from '../core/Tabs/Tabs';
|
||||
import { EmptyStateText } from '../EmptyStateText';
|
||||
import { SelectFile } from '../SelectFile';
|
||||
} from "@yaakapp-internal/plugins";
|
||||
import classNames from "classnames";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useState } from "react";
|
||||
import { useDebouncedValue } from "../../hooks/useDebouncedValue";
|
||||
import { useInstallPlugin } from "../../hooks/useInstallPlugin";
|
||||
import { usePluginInfo } from "../../hooks/usePluginInfo";
|
||||
import { usePluginsKey, useRefreshPlugins } from "../../hooks/usePlugins";
|
||||
import { showConfirmDelete } from "../../lib/confirm";
|
||||
import { minPromiseMillis } from "../../lib/minPromiseMillis";
|
||||
import { Button } from "../core/Button";
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { CountBadge } from "../core/CountBadge";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { InlineCode } from "../core/InlineCode";
|
||||
import { Link } from "../core/Link";
|
||||
import { LoadingIcon } from "../core/LoadingIcon";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { HStack } from "../core/Stacks";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "../core/Table";
|
||||
import { TabContent, Tabs } from "../core/Tabs/Tabs";
|
||||
import { EmptyStateText } from "../EmptyStateText";
|
||||
import { SelectFile } from "../SelectFile";
|
||||
|
||||
interface SettingsPluginsProps {
|
||||
defaultSubtab?: string;
|
||||
@@ -40,8 +40,8 @@ interface SettingsPluginsProps {
|
||||
export function SettingsPlugins({ defaultSubtab }: SettingsPluginsProps) {
|
||||
const [directory, setDirectory] = useState<string | null>(null);
|
||||
const plugins = useAtomValue(pluginsAtom);
|
||||
const bundledPlugins = plugins.filter((p) => p.source === 'bundled');
|
||||
const installedPlugins = plugins.filter((p) => p.source !== 'bundled');
|
||||
const bundledPlugins = plugins.filter((p) => p.source === "bundled");
|
||||
const installedPlugins = plugins.filter((p) => p.source !== "bundled");
|
||||
const createPlugin = useInstallPlugin();
|
||||
const refreshPlugins = useRefreshPlugins();
|
||||
return (
|
||||
@@ -52,15 +52,15 @@ export function SettingsPlugins({ defaultSubtab }: SettingsPluginsProps) {
|
||||
addBorders
|
||||
tabListClassName="px-6 pt-2"
|
||||
tabs={[
|
||||
{ label: 'Discover', value: 'search' },
|
||||
{ label: "Discover", value: "search" },
|
||||
{
|
||||
label: 'Installed',
|
||||
value: 'installed',
|
||||
label: "Installed",
|
||||
value: "installed",
|
||||
rightSlot: <CountBadge count={installedPlugins.length} />,
|
||||
},
|
||||
{
|
||||
label: 'Bundled',
|
||||
value: 'bundled',
|
||||
label: "Bundled",
|
||||
value: "bundled",
|
||||
rightSlot: <CountBadge count={bundledPlugins.length} />,
|
||||
},
|
||||
]}
|
||||
@@ -106,7 +106,7 @@ export function SettingsPlugins({ defaultSubtab }: SettingsPluginsProps) {
|
||||
icon="help"
|
||||
title="View documentation"
|
||||
onClick={() =>
|
||||
openUrl('https://yaak.app/docs/plugin-development/plugins-quick-start')
|
||||
openUrl("https://yaak.app/docs/plugin-development/plugins-quick-start")
|
||||
}
|
||||
/>
|
||||
</HStack>
|
||||
@@ -195,7 +195,7 @@ function PluginTableRow({
|
||||
const updates = usePluginUpdates();
|
||||
const latestVersion = updates.data?.plugins.find((u) => u.name === name)?.version;
|
||||
const installPluginMutation = useMutation({
|
||||
mutationKey: ['install_plugin', name],
|
||||
mutationKey: ["install_plugin", name],
|
||||
mutationFn: (name: string) => installPlugin(name, null),
|
||||
});
|
||||
const uninstall = usePromptUninstall(plugin?.id ?? null, displayName);
|
||||
@@ -207,7 +207,7 @@ function PluginTableRow({
|
||||
<TableCell className="!py-0">
|
||||
<Checkbox
|
||||
hideLabel
|
||||
title={plugin?.enabled ? 'Disable plugin' : 'Enable plugin'}
|
||||
title={plugin?.enabled ? "Disable plugin" : "Enable plugin"}
|
||||
checked={plugin?.enabled ?? false}
|
||||
disabled={plugin == null}
|
||||
onChange={async (enabled) => {
|
||||
@@ -285,10 +285,10 @@ function PluginTableRow({
|
||||
}
|
||||
|
||||
function PluginSearch() {
|
||||
const [query, setQuery] = useState<string>('');
|
||||
const [query, setQuery] = useState<string>("");
|
||||
const debouncedQuery = useDebouncedValue(query);
|
||||
const results = useQuery({
|
||||
queryKey: ['plugins', debouncedQuery],
|
||||
queryKey: ["plugins", debouncedQuery],
|
||||
queryFn: () => searchPlugins(query),
|
||||
});
|
||||
|
||||
@@ -334,7 +334,7 @@ function PluginSearch() {
|
||||
|
||||
function InstalledPlugins({ plugins, className }: { plugins: Plugin[]; className?: string }) {
|
||||
return plugins.length === 0 ? (
|
||||
<div className={classNames(className, 'pb-4')}>
|
||||
<div className={classNames(className, "pb-4")}>
|
||||
<EmptyStateText className="text-center">
|
||||
Plugins extend the functionality of Yaak.
|
||||
<br />
|
||||
@@ -388,14 +388,14 @@ function BundledPlugins({ plugins }: { plugins: Plugin[] }) {
|
||||
|
||||
function usePromptUninstall(pluginId: string | null, name: string) {
|
||||
const mut = useMutation({
|
||||
mutationKey: ['uninstall_plugin', pluginId],
|
||||
mutationKey: ["uninstall_plugin", pluginId],
|
||||
mutationFn: async () => {
|
||||
if (pluginId == null) return;
|
||||
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: `uninstall-plugin-${pluginId}`,
|
||||
title: 'Uninstall Plugin',
|
||||
confirmText: 'Uninstall',
|
||||
title: "Uninstall Plugin",
|
||||
confirmText: "Uninstall",
|
||||
description: (
|
||||
<>
|
||||
Permanently uninstall <InlineCode>{name}</InlineCode>?
|
||||
@@ -413,7 +413,7 @@ function usePromptUninstall(pluginId: string | null, name: string) {
|
||||
|
||||
function usePluginUpdates() {
|
||||
return useQuery({
|
||||
queryKey: ['plugin_updates', usePluginsKey()],
|
||||
queryKey: ["plugin_updates", usePluginsKey()],
|
||||
queryFn: () => checkPluginUpdates(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { patchModel, settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { useAtomValue } from "jotai";
|
||||
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import { Heading } from '../core/Heading';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { PlainInput } from '../core/PlainInput';
|
||||
import { Select } from '../core/Select';
|
||||
import { Separator } from '../core/Separator';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { Heading } from "../core/Heading";
|
||||
import { InlineCode } from "../core/InlineCode";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { Select } from "../core/Select";
|
||||
import { Separator } from "../core/Separator";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
|
||||
export function SettingsProxy() {
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
@@ -26,32 +26,32 @@ export function SettingsProxy() {
|
||||
label="Proxy"
|
||||
hideLabel
|
||||
size="sm"
|
||||
value={settings.proxy?.type ?? 'automatic'}
|
||||
value={settings.proxy?.type ?? "automatic"}
|
||||
onChange={async (v) => {
|
||||
if (v === 'automatic') {
|
||||
if (v === "automatic") {
|
||||
await patchModel(settings, { proxy: undefined });
|
||||
} else if (v === 'enabled') {
|
||||
} else if (v === "enabled") {
|
||||
await patchModel(settings, {
|
||||
proxy: {
|
||||
disabled: false,
|
||||
type: 'enabled',
|
||||
http: '',
|
||||
https: '',
|
||||
auth: { user: '', password: '' },
|
||||
bypass: '',
|
||||
type: "enabled",
|
||||
http: "",
|
||||
https: "",
|
||||
auth: { user: "", password: "" },
|
||||
bypass: "",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await patchModel(settings, { proxy: { type: 'disabled' } });
|
||||
await patchModel(settings, { proxy: { type: "disabled" } });
|
||||
}
|
||||
}}
|
||||
options={[
|
||||
{ label: 'Automatic proxy detection', value: 'automatic' },
|
||||
{ label: 'Custom proxy configuration', value: 'enabled' },
|
||||
{ label: 'No proxy', value: 'disabled' },
|
||||
{ label: "Automatic proxy detection", value: "automatic" },
|
||||
{ label: "Custom proxy configuration", value: "enabled" },
|
||||
{ label: "No proxy", value: "disabled" },
|
||||
]}
|
||||
/>
|
||||
{settings.proxy?.type === 'enabled' && (
|
||||
{settings.proxy?.type === "enabled" && (
|
||||
<VStack space={1.5}>
|
||||
<Checkbox
|
||||
className="my-3"
|
||||
@@ -60,13 +60,13 @@ export function SettingsProxy() {
|
||||
help="Use this to temporarily disable the proxy without losing the configuration"
|
||||
onChange={async (enabled) => {
|
||||
const { proxy } = settings;
|
||||
const http = proxy?.type === 'enabled' ? proxy.http : '';
|
||||
const https = proxy?.type === 'enabled' ? proxy.https : '';
|
||||
const bypass = proxy?.type === 'enabled' ? proxy.bypass : '';
|
||||
const auth = proxy?.type === 'enabled' ? proxy.auth : null;
|
||||
const http = proxy?.type === "enabled" ? proxy.http : "";
|
||||
const https = proxy?.type === "enabled" ? proxy.https : "";
|
||||
const bypass = proxy?.type === "enabled" ? proxy.bypass : "";
|
||||
const auth = proxy?.type === "enabled" ? proxy.auth : null;
|
||||
const disabled = !enabled;
|
||||
await patchModel(settings, {
|
||||
proxy: { type: 'enabled', http, https, auth, disabled, bypass },
|
||||
proxy: { type: "enabled", http, https, auth, disabled, bypass },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -82,13 +82,13 @@ export function SettingsProxy() {
|
||||
defaultValue={settings.proxy?.http}
|
||||
onChange={async (http) => {
|
||||
const { proxy } = settings;
|
||||
const https = proxy?.type === 'enabled' ? proxy.https : '';
|
||||
const bypass = proxy?.type === 'enabled' ? proxy.bypass : '';
|
||||
const auth = proxy?.type === 'enabled' ? proxy.auth : null;
|
||||
const disabled = proxy?.type === 'enabled' ? proxy.disabled : false;
|
||||
const https = proxy?.type === "enabled" ? proxy.https : "";
|
||||
const bypass = proxy?.type === "enabled" ? proxy.bypass : "";
|
||||
const auth = proxy?.type === "enabled" ? proxy.auth : null;
|
||||
const disabled = proxy?.type === "enabled" ? proxy.disabled : false;
|
||||
await patchModel(settings, {
|
||||
proxy: {
|
||||
type: 'enabled',
|
||||
type: "enabled",
|
||||
http,
|
||||
https,
|
||||
auth,
|
||||
@@ -109,12 +109,12 @@ export function SettingsProxy() {
|
||||
defaultValue={settings.proxy?.https}
|
||||
onChange={async (https) => {
|
||||
const { proxy } = settings;
|
||||
const http = proxy?.type === 'enabled' ? proxy.http : '';
|
||||
const bypass = proxy?.type === 'enabled' ? proxy.bypass : '';
|
||||
const auth = proxy?.type === 'enabled' ? proxy.auth : null;
|
||||
const disabled = proxy?.type === 'enabled' ? proxy.disabled : false;
|
||||
const http = proxy?.type === "enabled" ? proxy.http : "";
|
||||
const bypass = proxy?.type === "enabled" ? proxy.bypass : "";
|
||||
const auth = proxy?.type === "enabled" ? proxy.auth : null;
|
||||
const disabled = proxy?.type === "enabled" ? proxy.disabled : false;
|
||||
await patchModel(settings, {
|
||||
proxy: { type: 'enabled', http, https, auth, disabled, bypass },
|
||||
proxy: { type: "enabled", http, https, auth, disabled, bypass },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -125,13 +125,13 @@ export function SettingsProxy() {
|
||||
title="Enable authentication"
|
||||
onChange={async (enabled) => {
|
||||
const { proxy } = settings;
|
||||
const http = proxy?.type === 'enabled' ? proxy.http : '';
|
||||
const https = proxy?.type === 'enabled' ? proxy.https : '';
|
||||
const disabled = proxy?.type === 'enabled' ? proxy.disabled : false;
|
||||
const bypass = proxy?.type === 'enabled' ? proxy.bypass : '';
|
||||
const auth = enabled ? { user: '', password: '' } : null;
|
||||
const http = proxy?.type === "enabled" ? proxy.http : "";
|
||||
const https = proxy?.type === "enabled" ? proxy.https : "";
|
||||
const disabled = proxy?.type === "enabled" ? proxy.disabled : false;
|
||||
const bypass = proxy?.type === "enabled" ? proxy.bypass : "";
|
||||
const auth = enabled ? { user: "", password: "" } : null;
|
||||
await patchModel(settings, {
|
||||
proxy: { type: 'enabled', http, https, auth, disabled, bypass },
|
||||
proxy: { type: "enabled", http, https, auth, disabled, bypass },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -146,14 +146,14 @@ export function SettingsProxy() {
|
||||
defaultValue={settings.proxy.auth.user}
|
||||
onChange={async (user) => {
|
||||
const { proxy } = settings;
|
||||
const http = proxy?.type === 'enabled' ? proxy.http : '';
|
||||
const https = proxy?.type === 'enabled' ? proxy.https : '';
|
||||
const disabled = proxy?.type === 'enabled' ? proxy.disabled : false;
|
||||
const bypass = proxy?.type === 'enabled' ? proxy.bypass : '';
|
||||
const password = proxy?.type === 'enabled' ? (proxy.auth?.password ?? '') : '';
|
||||
const http = proxy?.type === "enabled" ? proxy.http : "";
|
||||
const https = proxy?.type === "enabled" ? proxy.https : "";
|
||||
const disabled = proxy?.type === "enabled" ? proxy.disabled : false;
|
||||
const bypass = proxy?.type === "enabled" ? proxy.bypass : "";
|
||||
const password = proxy?.type === "enabled" ? (proxy.auth?.password ?? "") : "";
|
||||
const auth = { user, password };
|
||||
await patchModel(settings, {
|
||||
proxy: { type: 'enabled', http, https, auth, disabled, bypass },
|
||||
proxy: { type: "enabled", http, https, auth, disabled, bypass },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -165,20 +165,20 @@ export function SettingsProxy() {
|
||||
defaultValue={settings.proxy.auth.password}
|
||||
onChange={async (password) => {
|
||||
const { proxy } = settings;
|
||||
const http = proxy?.type === 'enabled' ? proxy.http : '';
|
||||
const https = proxy?.type === 'enabled' ? proxy.https : '';
|
||||
const disabled = proxy?.type === 'enabled' ? proxy.disabled : false;
|
||||
const bypass = proxy?.type === 'enabled' ? proxy.bypass : '';
|
||||
const user = proxy?.type === 'enabled' ? (proxy.auth?.user ?? '') : '';
|
||||
const http = proxy?.type === "enabled" ? proxy.http : "";
|
||||
const https = proxy?.type === "enabled" ? proxy.https : "";
|
||||
const disabled = proxy?.type === "enabled" ? proxy.disabled : false;
|
||||
const bypass = proxy?.type === "enabled" ? proxy.bypass : "";
|
||||
const user = proxy?.type === "enabled" ? (proxy.auth?.user ?? "") : "";
|
||||
const auth = { user, password };
|
||||
await patchModel(settings, {
|
||||
proxy: { type: 'enabled', http, https, auth, disabled, bypass },
|
||||
proxy: { type: "enabled", http, https, auth, disabled, bypass },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
)}
|
||||
{settings.proxy.type === 'enabled' && (
|
||||
{settings.proxy.type === "enabled" && (
|
||||
<>
|
||||
<Separator className="my-6" />
|
||||
<PlainInput
|
||||
@@ -188,14 +188,14 @@ export function SettingsProxy() {
|
||||
placeholder="127.0.0.1, *.example.com, localhost:3000"
|
||||
onChange={async (bypass) => {
|
||||
const { proxy } = settings;
|
||||
const http = proxy?.type === 'enabled' ? proxy.http : '';
|
||||
const https = proxy?.type === 'enabled' ? proxy.https : '';
|
||||
const disabled = proxy?.type === 'enabled' ? proxy.disabled : false;
|
||||
const user = proxy?.type === 'enabled' ? (proxy.auth?.user ?? '') : '';
|
||||
const password = proxy?.type === 'enabled' ? (proxy.auth?.password ?? '') : '';
|
||||
const http = proxy?.type === "enabled" ? proxy.http : "";
|
||||
const https = proxy?.type === "enabled" ? proxy.https : "";
|
||||
const disabled = proxy?.type === "enabled" ? proxy.disabled : false;
|
||||
const user = proxy?.type === "enabled" ? (proxy.auth?.user ?? "") : "";
|
||||
const password = proxy?.type === "enabled" ? (proxy.auth?.password ?? "") : "";
|
||||
const auth = { user, password };
|
||||
await patchModel(settings, {
|
||||
proxy: { type: 'enabled', http, https, auth, disabled, bypass },
|
||||
proxy: { type: "enabled", http, https, auth, disabled, bypass },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
import { patchModel, settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { activeWorkspaceAtom } from '../../hooks/useActiveWorkspace';
|
||||
import { useResolvedAppearance } from '../../hooks/useResolvedAppearance';
|
||||
import { useResolvedTheme } from '../../hooks/useResolvedTheme';
|
||||
import type { ButtonProps } from '../core/Button';
|
||||
import { Heading } from '../core/Heading';
|
||||
import type { IconProps } from '../core/Icon';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import { Link } from '../core/Link';
|
||||
import type { SelectProps } from '../core/Select';
|
||||
import { Select } from '../core/Select';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { lazy, Suspense } from "react";
|
||||
import { activeWorkspaceAtom } from "../../hooks/useActiveWorkspace";
|
||||
import { useResolvedAppearance } from "../../hooks/useResolvedAppearance";
|
||||
import { useResolvedTheme } from "../../hooks/useResolvedTheme";
|
||||
import type { ButtonProps } from "../core/Button";
|
||||
import { Heading } from "../core/Heading";
|
||||
import type { IconProps } from "../core/Icon";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { Link } from "../core/Link";
|
||||
import type { SelectProps } from "../core/Select";
|
||||
import { Select } from "../core/Select";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
|
||||
const Editor = lazy(() => import('../core/Editor/Editor').then((m) => ({ default: m.Editor })));
|
||||
const Editor = lazy(() => import("../core/Editor/Editor").then((m) => ({ default: m.Editor })));
|
||||
|
||||
const buttonColors: ButtonProps['color'][] = [
|
||||
'primary',
|
||||
'info',
|
||||
'success',
|
||||
'notice',
|
||||
'warning',
|
||||
'danger',
|
||||
'secondary',
|
||||
'default',
|
||||
const buttonColors: ButtonProps["color"][] = [
|
||||
"primary",
|
||||
"info",
|
||||
"success",
|
||||
"notice",
|
||||
"warning",
|
||||
"danger",
|
||||
"secondary",
|
||||
"default",
|
||||
];
|
||||
|
||||
const icons: IconProps['icon'][] = [
|
||||
'info',
|
||||
'box',
|
||||
'update',
|
||||
'alert_triangle',
|
||||
'arrow_big_right_dash',
|
||||
'download',
|
||||
'copy',
|
||||
'magic_wand',
|
||||
'settings',
|
||||
'trash',
|
||||
'sparkles',
|
||||
'pencil',
|
||||
'paste',
|
||||
'search',
|
||||
'send_horizontal',
|
||||
const icons: IconProps["icon"][] = [
|
||||
"info",
|
||||
"box",
|
||||
"update",
|
||||
"alert_triangle",
|
||||
"arrow_big_right_dash",
|
||||
"download",
|
||||
"copy",
|
||||
"magic_wand",
|
||||
"settings",
|
||||
"trash",
|
||||
"sparkles",
|
||||
"pencil",
|
||||
"paste",
|
||||
"search",
|
||||
"send_horizontal",
|
||||
];
|
||||
|
||||
export function SettingsTheme() {
|
||||
@@ -55,14 +55,14 @@ export function SettingsTheme() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const lightThemes: SelectProps<string>['options'] = activeTheme.data.themes
|
||||
const lightThemes: SelectProps<string>["options"] = activeTheme.data.themes
|
||||
.filter((theme) => !theme.dark)
|
||||
.map((theme) => ({
|
||||
label: theme.label,
|
||||
value: theme.id,
|
||||
}));
|
||||
|
||||
const darkThemes: SelectProps<string>['options'] = activeTheme.data.themes
|
||||
const darkThemes: SelectProps<string>["options"] = activeTheme.data.themes
|
||||
.filter((theme) => theme.dark)
|
||||
.map((theme) => ({
|
||||
label: theme.label,
|
||||
@@ -74,7 +74,7 @@ export function SettingsTheme() {
|
||||
<div className="mb-3">
|
||||
<Heading>Theme</Heading>
|
||||
<p className="text-text-subtle">
|
||||
Make Yaak your own by selecting a theme, or{' '}
|
||||
Make Yaak your own by selecting a theme, or{" "}
|
||||
<Link href="https://yaak.app/docs/plugin-development/plugins-quick-start">
|
||||
Create Your Own
|
||||
</Link>
|
||||
@@ -88,13 +88,13 @@ export function SettingsTheme() {
|
||||
value={settings.appearance}
|
||||
onChange={(appearance) => patchModel(settings, { appearance })}
|
||||
options={[
|
||||
{ label: 'Automatic', value: 'system' },
|
||||
{ label: 'Light', value: 'light' },
|
||||
{ label: 'Dark', value: 'dark' },
|
||||
{ label: "Automatic", value: "system" },
|
||||
{ label: "Light", value: "light" },
|
||||
{ label: "Dark", value: "dark" },
|
||||
]}
|
||||
/>
|
||||
<HStack space={2}>
|
||||
{(settings.appearance === 'system' || settings.appearance === 'light') && (
|
||||
{(settings.appearance === "system" || settings.appearance === "light") && (
|
||||
<Select
|
||||
hideLabel
|
||||
leftSlot={<Icon icon="sun" color="secondary" />}
|
||||
@@ -107,7 +107,7 @@ export function SettingsTheme() {
|
||||
onChange={(themeLight) => patchModel(settings, { themeLight })}
|
||||
/>
|
||||
)}
|
||||
{(settings.appearance === 'system' || settings.appearance === 'dark') && (
|
||||
{(settings.appearance === "system" || settings.appearance === "dark") && (
|
||||
<Select
|
||||
hideLabel
|
||||
name="darkTheme"
|
||||
@@ -127,7 +127,7 @@ export function SettingsTheme() {
|
||||
className="mt-3 w-full bg-surface p-3 border border-dashed border-border-subtle rounded overflow-x-auto"
|
||||
>
|
||||
<HStack className="text" space={1.5}>
|
||||
<Icon icon={appearance === 'dark' ? 'moon' : 'sun'} />
|
||||
<Icon icon={appearance === "dark" ? "moon" : "sun"} />
|
||||
<strong>{activeTheme.data.active.label}</strong>
|
||||
<em>(preview)</em>
|
||||
</HStack>
|
||||
@@ -138,7 +138,7 @@ export function SettingsTheme() {
|
||||
color={c}
|
||||
size="2xs"
|
||||
iconSize="xs"
|
||||
icon={icons[i % icons.length] ?? 'info'}
|
||||
icon={icons[i % icons.length] ?? "info"}
|
||||
iconClassName="text"
|
||||
title={`${c}`}
|
||||
/>
|
||||
@@ -150,7 +150,7 @@ export function SettingsTheme() {
|
||||
variant="border"
|
||||
size="2xs"
|
||||
iconSize="xs"
|
||||
icon={icons[i % icons.length] ?? 'info'}
|
||||
icon={icons[i % icons.length] ?? "info"}
|
||||
iconClassName="text"
|
||||
title={`${c}`}
|
||||
/>
|
||||
@@ -159,11 +159,11 @@ export function SettingsTheme() {
|
||||
<Suspense>
|
||||
<Editor
|
||||
defaultValue={[
|
||||
'let foo = { // Demo code editor',
|
||||
"let foo = { // Demo code editor",
|
||||
' foo: ("bar" || "baz" ?? \'qux\'),',
|
||||
' baz: [1, 10.2, null, false, true],',
|
||||
'};',
|
||||
].join('\n')}
|
||||
" baz: [1, 10.2, null, false, true],",
|
||||
"};",
|
||||
].join("\n")}
|
||||
heightMode="auto"
|
||||
language="javascript"
|
||||
stateKey={null}
|
||||
|
||||
Reference in New Issue
Block a user