{label}
diff --git a/packages/studio/src/components/editor/propertyPanelFont.test.tsx b/packages/studio/src/components/editor/propertyPanelFont.test.tsx
new file mode 100644
index 000000000..fe4d32a68
--- /dev/null
+++ b/packages/studio/src/components/editor/propertyPanelFont.test.tsx
@@ -0,0 +1,30 @@
+// @vitest-environment happy-dom
+
+import React, { act } from "react";
+import { createRoot } from "react-dom/client";
+import { afterEach, describe, expect, it, vi } from "vitest";
+import { FontFamilyField } from "./propertyPanelFont";
+
+(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
+
+afterEach(() => {
+ document.body.innerHTML = "";
+});
+
+describe("FontFamilyField flat trigger", () => {
+ it("renders as a label/value row with a trailing dropdown caret, no boxed border", () => {
+ const host = document.createElement("div");
+ document.body.append(host);
+ const root = createRoot(host);
+ act(() => {
+ root.render(
+
,
+ );
+ });
+ const trigger = host.querySelector
('[data-flat-font-trigger="true"]');
+ expect(trigger).not.toBeNull();
+ expect(trigger?.className).not.toContain("border-neutral-800");
+ expect(host.textContent).toContain("JetBrains Mono");
+ act(() => root.unmount());
+ });
+});
diff --git a/packages/studio/src/components/editor/propertyPanelFont.tsx b/packages/studio/src/components/editor/propertyPanelFont.tsx
index a0fd50d7a..47f9cce4c 100644
--- a/packages/studio/src/components/editor/propertyPanelFont.tsx
+++ b/packages/studio/src/components/editor/propertyPanelFont.tsx
@@ -123,12 +123,14 @@ function loadImportedFontStylesheet(asset: ImportedFontAsset): void {
export function FontFamilyField({
value,
disabled,
+ flat,
importedFonts,
onImportFonts,
onCommit,
}: {
value: string;
disabled?: boolean;
+ flat?: boolean;
importedFonts: ImportedFontAsset[];
onImportFonts?: (files: FileList | File[]) => Promise;
onCommit: (nextValue: string) => void;
@@ -366,6 +368,130 @@ export function FontFamilyField({
setOpen(false);
};
+ const dropdown = open && (
+
+
+ setQuery(e.target.value)}
+ onKeyDown={(e) => {
+ if (e.key === "Escape") {
+ e.preventDefault();
+ setOpen(false);
+ }
+ if (e.key === "Enter" && filteredOptions[0]) {
+ e.preventDefault();
+ commitFamily(filteredOptions[0]);
+ }
+ }}
+ className="min-w-0 rounded-lg border border-neutral-800 bg-neutral-900 px-2.5 py-2 text-[11px] font-medium text-neutral-100 outline-none placeholder:text-neutral-600 focus:border-neutral-600"
+ />
+ {canQueryLocalFonts && (
+
+ )}
+
+ {
+ await handleImportFonts(event.target.files);
+ event.target.value = "";
+ }}
+ />
+
+ {fontNotice && (
+
+ {fontNotice}
+
+ )}
+
+ {filteredOptions.length === 0 ? (
+
No fonts found.
+ ) : (
+ filteredOptions.map((option) => (
+
+ ))
+ )}
+
+
+ );
+
+ if (flat) {
+ return (
+
+
Font
+
+ {dropdown}
+
+ );
+ }
+
return (
Font family
@@ -385,98 +511,7 @@ export function FontFamilyField({
Font
-
- {open && (
-
-
- setQuery(e.target.value)}
- onKeyDown={(e) => {
- if (e.key === "Escape") {
- e.preventDefault();
- setOpen(false);
- }
- if (e.key === "Enter" && filteredOptions[0]) {
- e.preventDefault();
- commitFamily(filteredOptions[0]);
- }
- }}
- className="min-w-0 rounded-lg border border-neutral-800 bg-neutral-900 px-2.5 py-2 text-[11px] font-medium text-neutral-100 outline-none placeholder:text-neutral-600 focus:border-neutral-600"
- />
- {canQueryLocalFonts && (
-
- )}
-
- {
- await handleImportFonts(event.target.files);
- event.target.value = "";
- }}
- />
-
- {fontNotice && (
-
- {fontNotice}
-
- )}
-
- {filteredOptions.length === 0 ? (
-
No fonts found.
- ) : (
- filteredOptions.map((option) => (
-
- ))
- )}
-
-
- )}
+ {dropdown}
);
}
diff --git a/packages/studio/src/components/editor/propertyPanelSections.tsx b/packages/studio/src/components/editor/propertyPanelSections.tsx
index 2eaaa1792..e81a5aad8 100644
--- a/packages/studio/src/components/editor/propertyPanelSections.tsx
+++ b/packages/studio/src/components/editor/propertyPanelSections.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useRef, useState, type ChangeEvent } from "react";
import { Plus, Type } from "../../icons/SystemIcons";
import { isTextEditableSelection, type DomEditSelection } from "./domEditing";
import type { ImportedFontAsset } from "./fontAssets";
@@ -58,17 +58,19 @@ function detectAvailableWeights(fontFamily: string): string[] {
return available.length > 0 ? available : ALL_WEIGHTS;
}
-function TextAreaField({
+export function TextAreaField({
label,
value,
disabled,
autoFocus,
+ flat,
onCommit,
}: {
label: string;
value: string;
disabled?: boolean;
autoFocus?: boolean;
+ flat?: boolean;
onCommit: (nextValue: string) => void;
}) {
const [draft, setDraft] = useState(value);
@@ -104,6 +106,38 @@ function TextAreaField({
}, 120);
};
+ const handleFocus = () => {
+ focusedRef.current = true;
+ };
+ const handleChange = (e: ChangeEvent) => {
+ setDraft(e.target.value);
+ scheduleCommit(e.target.value);
+ };
+ const handleBlur = () => {
+ focusedRef.current = false;
+ commitDraft(draft);
+ };
+
+ if (flat) {
+ return (
+
+ );
+ }
+
return (