Skip to content

Commit 1ef8869

Browse files
committed
Add thresholdPercent and baseFontFamily to PluginSettings; update related components for new settings
1 parent 92fc5d6 commit 1ef8869

File tree

5 files changed

+63
-180
lines changed

5 files changed

+63
-180
lines changed

apps/plugin/plugin-src/code.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export const defaultPluginSettings: PluginSettings = {
3636
tailwindGenerationMode: "jsx",
3737
baseFontSize: 16,
3838
useTailwind4: false,
39+
thresholdPercent: 15,
40+
baseFontFamily: "",
3941
};
4042

4143
// A helper type guard to ensure the key belongs to the PluginSettings type

packages/backend/src/tailwind/conversionTables.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const nearestValue = (goal: number, array: Array<number>): number => {
1414
export const nearestValueWithThreshold = (
1515
goal: number,
1616
array: Array<number>,
17-
thresholdPercent: number = 15,
17+
thresholdPercent: number = localTailwindSettings.thresholdPercent,
1818
): number | null => {
1919
const nearest = nearestValue(goal, array);
2020
const diff = Math.abs(nearest - goal);
@@ -61,7 +61,7 @@ const pxToRemToTailwind = (
6161
return conversionMap[convertedValue];
6262
} else if (localTailwindSettings.roundTailwindValues) {
6363
// Only round if the nearest value is within acceptable threshold
64-
const thresholdValue = nearestValueWithThreshold(remValue, keys, 15);
64+
const thresholdValue = nearestValueWithThreshold(remValue, keys);
6565

6666
if (thresholdValue !== null) {
6767
return conversionMap[thresholdValue];
@@ -82,7 +82,7 @@ const pxToTailwind = (
8282
return conversionMap[convertedValue];
8383
} else if (localTailwindSettings.roundTailwindValues) {
8484
// Only round if the nearest value is within acceptable threshold
85-
const thresholdValue = nearestValueWithThreshold(value, keys, 15);
85+
const thresholdValue = nearestValueWithThreshold(value, keys);
8686

8787
if (thresholdValue !== null) {
8888
return conversionMap[thresholdValue];
@@ -106,8 +106,8 @@ export const pxToFontSize = (value: number): string => {
106106

107107
export const pxToBorderRadius = (value: number): string => {
108108
const conversionMap = localTailwindSettings.useTailwind4
109-
? config.borderRadiusV4
110-
: config.borderRadius;
109+
? config.borderRadiusV4
110+
: config.borderRadius;
111111
return pxToRemToTailwind(value, conversionMap);
112112
};
113113

@@ -121,8 +121,8 @@ export const pxToOutline = (value: number): string | null => {
121121

122122
export const pxToBlur = (value: number): string | null => {
123123
const conversionMap = localTailwindSettings.useTailwind4
124-
? config.blurV4
125-
: config.blur;
124+
? config.blurV4
125+
: config.blur;
126126
return pxToTailwind(value, conversionMap);
127127
};
128128

packages/backend/src/tailwind/tailwindTextBuilder.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder";
1313
import { config } from "./tailwindConfig";
1414
import { StyledTextSegmentSubset } from "types";
15+
import { localTailwindSettings } from "./tailwindMain";
1516

1617
export class TailwindTextBuilder extends TailwindDefaultBuilder {
1718
getTextSegments(node: TextNode): {
@@ -93,6 +94,15 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder {
9394
};
9495

9596
fontFamily = (fontName: FontName): string => {
97+
// Check if the font matches the base font family setting
98+
const baseFontFamily = localTailwindSettings.baseFontFamily;
99+
100+
// If the font matches exactly the base font, don't add a class
101+
if (baseFontFamily && fontName.family.toLowerCase() === baseFontFamily.toLowerCase()) {
102+
return "";
103+
}
104+
105+
// Check if the font is in one of the Tailwind default font stacks
96106
if (config.fontFamily.sans.includes(fontName.family)) {
97107
return "font-sans";
98108
}

packages/plugin-ui/src/components/TailwindSettings.tsx

Lines changed: 42 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -1,179 +1,6 @@
1-
import { ChevronDown, ChevronRight, HelpCircle, Check } from "lucide-react";
2-
import { useState, useRef, useEffect } from "react";
31
import { PluginSettings } from "types";
42
import FormField from "./CustomPrefixInput"; // Still importing from the same file
53

6-
// Added InputGroup component
7-
interface InputGroupProps {
8-
label: string;
9-
children: React.ReactNode;
10-
}
11-
12-
const InputGroup: React.FC<InputGroupProps> = ({ label, children }) => (
13-
<div className="mb-2">
14-
<div className="flex items-center gap-1.5 mb-1.5">
15-
<label className="text-xs font-medium text-gray-700 dark:text-gray-300">
16-
{label}
17-
</label>
18-
19-
{/* This is where the success message will appear, rendered by the child component */}
20-
</div>
21-
{children}
22-
</div>
23-
);
24-
25-
// Enhanced InputWithText component
26-
interface InputWithTextProps {
27-
value: string | number;
28-
onChange: (value: number) => void;
29-
placeholder?: string;
30-
suffix?: string;
31-
min?: number;
32-
max?: number;
33-
}
34-
35-
const InputWithText: React.FC<InputWithTextProps> = ({
36-
value,
37-
onChange,
38-
placeholder,
39-
suffix,
40-
min = 1,
41-
max = 100,
42-
}) => {
43-
const [inputValue, setInputValue] = useState(String(value));
44-
const [isFocused, setIsFocused] = useState(false);
45-
const [hasChanges, setHasChanges] = useState(false);
46-
const [showSuccess, setShowSuccess] = useState(false);
47-
const [hasError, setHasError] = useState(false);
48-
const [errorMessage, setErrorMessage] = useState("");
49-
const inputRef = useRef<HTMLInputElement>(null);
50-
51-
// Update internal state when value changes (from parent)
52-
useEffect(() => {
53-
setInputValue(String(value));
54-
setHasChanges(false);
55-
}, [value]);
56-
57-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
58-
const newValue = e.target.value;
59-
setInputValue(newValue);
60-
61-
// Check for non-numeric characters
62-
if (/[^0-9]/.test(newValue)) {
63-
setHasError(true);
64-
setErrorMessage("Only numbers are allowed");
65-
setHasChanges(newValue !== String(value));
66-
return;
67-
}
68-
69-
const numValue = parseInt(newValue, 10);
70-
71-
if (isNaN(numValue)) {
72-
setHasError(true);
73-
setErrorMessage("Please enter a valid number");
74-
} else if (numValue < min) {
75-
setHasError(true);
76-
setErrorMessage(`Minimum value is ${min}`);
77-
} else if (numValue > max) {
78-
setHasError(true);
79-
setErrorMessage(`Maximum value is ${max}`);
80-
} else {
81-
setHasError(false);
82-
setErrorMessage("");
83-
}
84-
85-
setHasChanges(newValue !== String(value));
86-
};
87-
88-
const applyChanges = () => {
89-
if (hasError) return;
90-
91-
const numValue = parseInt(inputValue, 10);
92-
if (!isNaN(numValue) && numValue >= min && numValue <= max) {
93-
onChange(numValue);
94-
95-
// Show success indicator briefly
96-
setShowSuccess(true);
97-
setTimeout(() => setShowSuccess(false), 1500);
98-
setHasChanges(false);
99-
}
100-
};
101-
102-
const handleBlur = () => {
103-
setIsFocused(false);
104-
};
105-
106-
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
107-
if (e.key === "Enter") {
108-
e.preventDefault();
109-
applyChanges();
110-
inputRef.current?.blur();
111-
}
112-
};
113-
114-
return (
115-
<div className="flex flex-col w-full">
116-
{showSuccess && (
117-
<span className="text-xs text-green-500 flex items-center gap-1 animate-fade-in-out ml-auto mb-1">
118-
<Check className="w-3 h-3" /> Applied
119-
</span>
120-
)}
121-
122-
<div className="flex items-start gap-2">
123-
<div className="flex-1 flex flex-col">
124-
<div className="flex items-center">
125-
<input
126-
ref={inputRef}
127-
type="text"
128-
value={inputValue}
129-
onChange={handleChange}
130-
onFocus={() => setIsFocused(true)}
131-
onBlur={handleBlur}
132-
onKeyDown={handleKeyDown}
133-
placeholder={placeholder}
134-
className={`p-1.5 px-2.5 w-full transition-all focus:outline-hidden ${
135-
suffix ? "rounded-l-md" : "rounded-md"
136-
} ${
137-
hasError
138-
? "border border-red-300 dark:border-red-700 bg-red-50 dark:bg-red-900/20"
139-
: isFocused
140-
? "border border-green-400 dark:border-green-600 ring-1 ring-green-300 dark:ring-green-800 bg-white dark:bg-neutral-800"
141-
: "border border-gray-300 dark:border-gray-600 bg-white dark:bg-neutral-800 hover:border-gray-400 dark:hover:border-gray-500"
142-
}`}
143-
/>
144-
{suffix && (
145-
<span
146-
className="py-1.5 px-2.5 text-sm border border-l-0 border-gray-300 dark:border-gray-600
147-
bg-gray-100 dark:bg-gray-700 rounded-r-md text-gray-700 dark:text-gray-300"
148-
>
149-
{suffix}
150-
</span>
151-
)}
152-
</div>
153-
154-
{hasError && (
155-
<p className="text-xs text-red-500 mt-1">{errorMessage}</p>
156-
)}
157-
</div>
158-
159-
{hasChanges && (
160-
<button
161-
onClick={applyChanges}
162-
disabled={hasError}
163-
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
164-
hasError
165-
? "bg-gray-200 text-gray-500 dark:bg-gray-800 dark:text-gray-600 cursor-not-allowed"
166-
: "bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400 dark:hover:bg-green-900/50"
167-
}`}
168-
>
169-
Done
170-
</button>
171-
)}
172-
</div>
173-
</div>
174-
);
175-
};
176-
1774
interface TailwindSettingsProps {
1785
settings: PluginSettings | null;
1796
onPreferenceChanged: (
@@ -194,6 +21,12 @@ export const TailwindSettings: React.FC<TailwindSettingsProps> = ({
19421
const handleBaseFontSizeChange = (value: number) => {
19522
onPreferenceChanged("baseFontSize", value);
19623
};
24+
const handleThresholdPercentChange = (value: number) => {
25+
onPreferenceChanged("thresholdPercent", value);
26+
};
27+
const handleBaseFontFamilyChange = (newValue: string) => {
28+
onPreferenceChanged("baseFontFamily", newValue);
29+
};
19730

19831
return (
19932
<div className="mt-2">
@@ -239,6 +72,42 @@ export const TailwindSettings: React.FC<TailwindSettingsProps> = ({
23972
Use this value to calculate rem values (default: 16px)
24073
</p>
24174
</div>
75+
76+
{/* Threshold percent setting */}
77+
<div className="mb-3">
78+
<FormField
79+
label="Rounding Threshold"
80+
initialValue={settings.thresholdPercent || 15}
81+
onValueChange={(d) => {
82+
handleThresholdPercentChange(d as any);
83+
}}
84+
placeholder="15"
85+
suffix="%"
86+
type="number"
87+
min={0}
88+
max={50}
89+
/>
90+
<p className="text-xs text-neutral-500 mt-1">
91+
Maximum allowed difference when rounding values (default: 15%)
92+
</p>
93+
</div>
94+
95+
{/* Base font family setting */}
96+
<div className="mb-3">
97+
<FormField
98+
label="Base Font Family"
99+
initialValue={settings.baseFontFamily || ''}
100+
onValueChange={(d) => {
101+
handleBaseFontFamilyChange(String(d));
102+
}}
103+
placeholder="sans-serif"
104+
helpText="Font family that won't be included in generated classes."
105+
type="text"
106+
/>
107+
<p className="text-xs text-neutral-500 mt-1">
108+
{`Elements with this font won't have "font-[<value>]" class added`}
109+
</p>
110+
</div>
242111
</div>
243112
</div>
244113
);

packages/types/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export interface TailwindSettings extends HTMLSettings {
1717
embedVectors: boolean;
1818
baseFontSize: number;
1919
useTailwind4: boolean;
20+
thresholdPercent: number;
21+
baseFontFamily: string;
2022
}
2123
export interface FlutterSettings {
2224
flutterGenerationMode: "fullApp" | "stateless" | "snippet";

0 commit comments

Comments
 (0)