Compare commits

...
Author SHA1 Message Date
layue13 b74b53fbf9 fix(ui): hard-fix JFXButton pressed feedback
Mirror Repository / Mirror to CNB (push) Skipped
Mirror Repository / Mirror to Gitee (push) Skipped
Check Codes / build (push) Successful in 3m28s
Check Codes / build (pull_request) Successful in 2m42s
Java CI / build (push) Failing after 3h0m6s
Java CI / build (pull_request) Failing after 3h0m8s
Stop JFXButtonSkin wiping Modena/CSS press fills to transparent; add
armed overlay (bg+border) and last-loaded press-feedback.css so every
icon/toolbar/launch button shows obvious :pressed/:armed state.
2026-11-07 20:57:18 +00:00
layue13 22d982a089 fix(ui): skill palette chrome + icon press feedback
Mirror Repository / Mirror to CNB (push) Skipped
Mirror Repository / Mirror to Gitee (push) Skipped
Check Codes / build (push) Successful in 2m59s
Check Codes / build (pull_request) Successful in 2m52s
Java CI / build (push) Failing after 3h0m2s
Java CI / build (pull_request) Failing after 3h0m2s
Pin Java title/frame surfaces to skill warm light/dark hex (kill Monet
pink/teal drift), override secondary/tertiary to orange-only, and add
visible pressed washes for toggle-icon / toolbar icon buttons.
2026-11-07 20:52:15 +00:00
layue13 07369a3aba fix(ui): golden-ratio chrome layout hierarchy
Mirror Repository / Mirror to CNB (push) Skipped
Mirror Repository / Mirror to Gitee (push) Skipped
Check Codes / build (push) Failing after 2m45s
Check Codes / build (pull_request) Successful in 3m5s
Java CI / build (push) Failing after 3h0m1s
Java CI / build (pull_request) Failing after 3h0m1s
Apply φ≈1.618 sidebar:content splits and φ spacing ladder for
title:body tool-density chrome (LayoutMetrics), keeping tactility.
2026-11-07 20:46:46 +00:00
layue13 e69464105a fix(ui): align launcher chrome to anthropic-style-cn skill
Mirror Repository / Mirror to CNB (push) Skipped
Mirror Repository / Mirror to Gitee (push) Skipped
Check Codes / build (push) Successful in 2m49s
Check Codes / build (pull_request) Successful in 2m55s
Java CI / build (push) Failing after 3h0m1s
Java CI / build (pull_request) Failing after 3h0m2s
Map skill light/dark surface/text/border pairs onto Monet vars, lock
warm orange CTA #D97757, skill motion (150/250/400 + cubic 0.16,1,0.3,1),
tool-density spacing, and keep Source Han sans/serif roles.
2026-11-07 20:43:01 +00:00
layue13 38d764c7d1 fix(ui): dual-mode tokens and tactile button feedback
Replace light-only hardcodes with Monet/Anthropic tokens for light and
dark, inject hover/press/focus washes from the active scheme, and restore
subtle scale+tone feedback without Material ripples.
2026-11-07 20:40:53 +00:00
layue13 69b415e5ef fix(ui): calm Material motion for Anthropic chrome
Mirror Repository / Mirror to CNB (push) Skipped
Mirror Repository / Mirror to Gitee (push) Skipped
Check Codes / build (push) Successful in 2m45s
Check Codes / build (pull_request) Successful in 4m56s
Java CI / build (push) Failing after 3h0m2s
Java CI / build (pull_request) Failing after 3h0m1s
Disable JFX ripples, replace emphasized/overshoot transitions with
short ease-out fades, and soften window open/minimize/close scaling.
2026-11-07 20:36:21 +00:00
layue13 64c63b2af0 feat(ui): Anthropic-inspired chrome with Source Han pairing
Calm olive-slate Monet seed, surface title bar, quieter cards, TDLM
branding, and hard-bound Source Han Sans Bold (titles) + Source Han
Serif (body) via UI-charset OFL subsets (~2MB).
2026-11-07 20:36:17 +00:00
33 changed files with 1289 additions and 276 deletions

No files matched your search

@@ -23,6 +23,10 @@ import javafx.scene.control.Label;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
@@ -30,8 +34,20 @@ import javafx.scene.shape.Shape;
import javafx.util.Duration;
import org.jackhuang.hmcl.ui.FXUtils;
/// JFoenix button skin with Anthropic skill tactile press feedback.
///
/// Upstream skin rewrote Modena's cyan `:pressed` fill to transparent and thereby
/// also ate author CSS press backgrounds. This build keeps CSS fills, maps the
/// Modena cyan to a skill wash, and paints an unmistakable armed overlay
/// (background + border + opacity) on every JFXButton.
public class JFXButtonSkin extends ButtonSkin {
private static final Color SKILL_PRESS_FILL = Color.web("#D97757", 0.40);
private static final Color SKILL_PRESS_BORDER = Color.web("#C96442");
private static final Color SKILL_PRESS_DIM = Color.web("#141413", 0.28);
private final StackPane buttonContainer = new StackPane();
/// Full-bleed press veil — opacity toggled from armed/pressed, independent of CSS.
private final StackPane pressOverlay = new StackPane();
private final JFXRippler buttonRippler = new JFXRippler(new StackPane()) {
protected Node getMask() {
StackPane mask = new StackPane();
@@ -64,6 +80,17 @@ public class JFXButtonSkin extends ButtonSkin {
public JFXButtonSkin(JFXButton button) {
super(button);
this.pressOverlay.setMouseTransparent(true);
this.pressOverlay.setOpacity(0);
this.pressOverlay.setBackground(new Background(new BackgroundFill(
SKILL_PRESS_FILL, this.defaultRadii, Insets.EMPTY)));
this.pressOverlay.setBorder(new Border(new BorderStroke(
SKILL_PRESS_BORDER,
BorderStrokeStyle.SOLID,
this.defaultRadii,
new BorderWidths(2.0))));
this.getSkinnable().armedProperty().addListener((o, oldVal, newVal) -> {
if (newVal) {
this.releaseManualRippler = this.buttonRippler.createManualRipple();
@@ -81,21 +108,23 @@ public class JFXButtonSkin extends ButtonSkin {
this.clickedAnimation.play();
}
}
this.updatePressOverlay(button);
});
this.buttonContainer.getChildren().add(this.buttonRippler);
this.buttonContainer.getChildren().addAll(this.buttonRippler, this.pressOverlay);
button.buttonTypeProperty().addListener((o, oldVal, newVal) -> this.updateButtonType(newVal));
button.setOnMousePressed((e) -> {
if (this.clickedAnimation != null) {
this.clickedAnimation.setRate(1.0F);
this.clickedAnimation.play();
}
this.updatePressOverlay(button);
});
button.setOnMouseReleased((e) -> {
if (this.clickedAnimation != null) {
this.clickedAnimation.setRate(-1.0F);
this.clickedAnimation.play();
}
this.updatePressOverlay(button);
});
ReadOnlyBooleanProperty focusVisibleProperty = FXUtils.focusVisibleProperty(button);
@@ -111,20 +140,34 @@ public class JFXButtonSkin extends ButtonSkin {
this.buttonRippler.hideOverlay();
}
});
button.pressedProperty().addListener((o, oldVal, newVal) -> this.buttonRippler.hideOverlay());
button.pressedProperty().addListener((o, oldVal, newVal) -> {
this.buttonRippler.hideOverlay();
this.updatePressOverlay(button);
});
button.setPickOnBounds(false);
this.buttonContainer.setPickOnBounds(false);
this.buttonContainer.shapeProperty().bind(this.getSkinnable().shapeProperty());
this.buttonContainer.borderProperty().bind(this.getSkinnable().borderProperty());
// Mirror CSS background onto the container. Never call button.setBackground() here —
// that used to stomp author :pressed fills (and wiped Modena cyan to transparent).
this.buttonContainer.backgroundProperty().bind(Bindings.createObjectBinding(() -> {
if (button.getBackground() == null || this.isJavaDefaultBackground(button.getBackground()) || this.isJavaDefaultClickedBackground(button.getBackground())) {
button.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, this.defaultRadii, null)));
Background background = button.getBackground();
if (background == null || background.getFills().isEmpty() || this.isJavaDefaultBackground(background)) {
return new Background(new BackgroundFill(Color.TRANSPARENT, this.defaultRadii, Insets.EMPTY));
}
if (this.isJavaDefaultClickedBackground(background)) {
CornerRadii radii = background.getFills().get(0).getRadii();
return new Background(new BackgroundFill(SKILL_PRESS_FILL,
radii != null ? radii : this.defaultRadii, Insets.EMPTY));
}
try {
return new Background(new BackgroundFill(this.getSkinnable().getBackground() != null ? this.getSkinnable().getBackground().getFills().get(0).getFill() : Color.TRANSPARENT, this.getSkinnable().getBackground() != null ? this.getSkinnable().getBackground().getFills().get(0).getRadii() : this.defaultRadii, Insets.EMPTY));
BackgroundFill fill = background.getFills().get(0);
return new Background(new BackgroundFill(
fill.getFill(),
fill.getRadii() != null ? fill.getRadii() : this.defaultRadii,
Insets.EMPTY));
} catch (Exception var3) {
return this.getSkinnable().getBackground();
return background;
}
}, this.getSkinnable().backgroundProperty()));
button.ripplerFillProperty().addListener((o, oldVal, newVal) -> this.buttonRippler.setRipplerFill(newVal));
@@ -134,6 +177,40 @@ public class JFXButtonSkin extends ButtonSkin {
this.updateButtonType(button.getButtonType());
this.updateChildren();
this.updatePressOverlay(button);
}
/// Shows a hard-to-miss press veil: orange wash + warm border; dimming on raised CTAs.
private void updatePressOverlay(JFXButton button) {
boolean active = !button.isDisabled() && (button.isArmed() || button.isPressed());
boolean raised = button.getButtonType() == JFXButton.ButtonType.RAISED
|| button.getStyleClass().contains("jfx-button-raised")
|| button.getStyleClass().contains("launch-button")
|| button.getStyleClass().contains("menu-button");
CornerRadii radii = this.defaultRadii;
Background bg = button.getBackground();
if (bg != null && !bg.getFills().isEmpty() && bg.getFills().get(0).getRadii() != null) {
radii = bg.getFills().get(0).getRadii();
}
Color fill = raised ? SKILL_PRESS_DIM : SKILL_PRESS_FILL;
this.pressOverlay.setBackground(new Background(new BackgroundFill(fill, radii, Insets.EMPTY)));
this.pressOverlay.setBorder(new Border(new BorderStroke(
SKILL_PRESS_BORDER,
BorderStrokeStyle.SOLID,
radii,
new BorderWidths(active ? 2.0 : 0.0))));
this.pressOverlay.setOpacity(active ? 1.0 : 0.0);
// Drive opacity via style class so CSS keeps ownership (no setOpacity stomping).
final String armedClass = "anthropic-armed";
if (active) {
if (!button.getStyleClass().contains(armedClass)) {
button.getStyleClass().add(armedClass);
}
} else {
button.getStyleClass().remove(armedClass);
}
}
protected void updateChildren() {
@@ -186,6 +263,7 @@ public class JFXButtonSkin extends ButtonSkin {
private boolean isJavaDefaultClickedBackground(Background background) {
try {
// Modena default armed cyan — replace with skill wash instead of wiping to transparent.
return "0x039ed3ff".equals(background.getFills().get(0).getFill().toString());
} catch (Exception var3) {
return false;
@@ -19,8 +19,8 @@ public final class Metadata {
private Metadata() {
}
public static final String NAME = "HMCL";
public static final String FULL_NAME = "Hello Minecraft! Launcher";
public static final String NAME = "TDLM";
public static final String FULL_NAME = "TDLM";
public static final String VERSION = System.getProperty("hmcl.version.override", JarUtils.getAttribute("hmcl.version", "@develop@"));
/// Explicit Application User Model ID used for Windows taskbar grouping and pinning.
@@ -36,7 +36,22 @@ public final class FontManager {
public static final double DEFAULT_FONT_SIZE = 12.0f;
/// Bundled Anthropic UI title face (Source Han Sans SC Bold subset).
public static final String TITLE_FONT_FAMILY = "Source Han Sans SC";
/// Bundled Anthropic UI body face (Source Han Serif SC Regular subset).
public static final String BODY_FONT_FAMILY = "Source Han Serif SC";
private static final String BUNDLED_TITLE_FONT = "/assets/fonts/SourceHanSansSC-Bold.otf";
private static final String BUNDLED_BODY_FONT = "/assets/fonts/SourceHanSerifSC-Regular.otf";
private static volatile boolean bundledFontsLoaded;
private static volatile @Nullable String loadedTitleFamily;
private static volatile @Nullable String loadedBodyFamily;
private static final Lazy<Font> DEFAULT_FONT = new Lazy<>(() -> {
ensureBundledFontsLoaded();
Font font;
// Recommended
@@ -70,6 +85,11 @@ public final class FontManager {
return font;
}
// Anthropic UI hard-bind: bundled Source Han Serif body face
if (loadedBodyFamily != null) {
return Font.font(loadedBodyFamily, DEFAULT_FONT_SIZE);
}
// Default
String fcMatchPattern;
@@ -83,8 +103,62 @@ public final class FontManager {
private static final ObjectProperty<FontReference> font = new SimpleObjectProperty<>();
static {
ensureBundledFontsLoaded();
updateFont();
LOG.info("Font: " + (font.get() != null ? font.get().family() : "System"));
LOG.info("Font: " + (font.get() != null ? font.get().family() : "System")
+ ", title=" + Objects.requireNonNullElse(loadedTitleFamily, "unavailable")
+ ", body=" + Objects.requireNonNullElse(loadedBodyFamily, "unavailable"));
}
/// Registers the bundled Source Han subsets so CSS family names resolve.
public static void ensureBundledFontsLoaded() {
if (bundledFontsLoaded) {
return;
}
synchronized (FontManager.class) {
if (bundledFontsLoaded) {
return;
}
loadedTitleFamily = loadBundledFont(BUNDLED_TITLE_FONT, TITLE_FONT_FAMILY);
loadedBodyFamily = loadBundledFont(BUNDLED_BODY_FONT, BODY_FONT_FAMILY);
bundledFontsLoaded = true;
}
}
/// Returns the resolved title font family after bundled registration, or the hard-bind name.
public static String getTitleFontFamily() {
ensureBundledFontsLoaded();
return Objects.requireNonNullElse(loadedTitleFamily, TITLE_FONT_FAMILY);
}
/// Returns the resolved body font family after bundled registration, or the hard-bind name.
public static String getBodyFontFamily() {
ensureBundledFontsLoaded();
return Objects.requireNonNullElse(loadedBodyFamily, BODY_FONT_FAMILY);
}
private static @Nullable String loadBundledFont(String resourcePath, String expectedFamily) {
try (var input = FontManager.class.getResourceAsStream(resourcePath)) {
if (input == null) {
LOG.warning("Bundled font missing: " + resourcePath);
return null;
}
Font loaded = Font.loadFont(input, DEFAULT_FONT_SIZE);
if (loaded == null) {
LOG.warning("Failed to decode bundled font: " + resourcePath);
return null;
}
String family = loaded.getFamily();
LOG.info("Loaded bundled font " + resourcePath + " as family '" + family + "'");
if (!expectedFamily.equalsIgnoreCase(family)) {
LOG.warning("Bundled font family mismatch for " + resourcePath
+ ": expected '" + expectedFamily + "', got '" + family + "'");
}
return family;
} catch (Exception e) {
LOG.warning("Failed to load bundled font " + resourcePath, e);
return null;
}
}
private static void updateFont() {
@@ -96,7 +170,7 @@ public final class FontManager {
if (fontFamily == null) {
Font defaultFont = DEFAULT_FONT.get();
font.set(defaultFont != null ? new FontReference(defaultFont) : null);
font.set(defaultFont != null ? new FontReference(defaultFont) : new FontReference(BODY_FONT_FAMILY));
} else {
font.set(new FontReference(fontFamily));
}
@@ -10,7 +10,6 @@ import javafx.scene.paint.Color;
import org.glavo.monetfx.Brightness;
import org.glavo.monetfx.ColorRole;
import org.glavo.monetfx.ColorScheme;
import org.jackhuang.hmcl.theme.ResolvedTheme;
import org.jackhuang.hmcl.theme.ThemeColor;
import org.jackhuang.hmcl.theme.Themes;
@@ -26,6 +25,8 @@ public final class StyleSheets {
private static final int FONT_STYLE_SHEET_INDEX = 0;
private static final int THEME_STYLE_SHEET_INDEX = 1;
private static final int BRIGHTNESS_SHEET_INDEX = 2;
/// Loaded after root.css so solid :pressed fills beat Monet / Modena cyan.
private static final int PRESS_FEEDBACK_SHEET_INDEX = 4;
private static final ObservableList<String> stylesheets;
@@ -34,9 +35,15 @@ public final class StyleSheets {
getFontStyleSheet(),
getThemeStyleSheet(),
getBrightnessStyleSheet(),
"/assets/css/root.css"
"/assets/css/root.css",
"/assets/css/press-feedback.css"
};
stylesheets = FXCollections.observableList(Arrays.asList(array));
// Keep the constant honest for future inserts ahead of press-feedback.
if (stylesheets.size() <= PRESS_FEEDBACK_SHEET_INDEX
|| !"/assets/css/press-feedback.css".equals(stylesheets.get(PRESS_FEEDBACK_SHEET_INDEX))) {
throw new IllegalStateException("press-feedback.css must remain the last stylesheet");
}
FontManager.fontProperty().addListener(o -> stylesheets.set(FONT_STYLE_SHEET_INDEX, getFontStyleSheet()));
Themes.colorSchemeProperty().addListener(o -> {
@@ -50,51 +57,72 @@ public final class StyleSheets {
}
private static String getFontStyleSheet() {
final String defaultCss = "/assets/css/font.css";
FontManager.ensureBundledFontsLoaded();
final FontManager.FontReference font = FontManager.getFont();
if (font == null || "System".equals(font.family()))
return defaultCss;
String fontFamily = font.family();
String style = font.style();
String bodyFamily = FontManager.getBodyFontFamily();
String titleFamily = FontManager.getTitleFontFamily();
String weight = null;
String posture = null;
if (style != null) {
style = style.toLowerCase(Locale.ROOT);
// Explicit user / env override replaces body face only; titles stay Source Han Sans Bold.
if (font != null && !"System".equals(font.family())
&& !FontManager.BODY_FONT_FAMILY.equalsIgnoreCase(font.family())
&& !FontManager.TITLE_FONT_FAMILY.equalsIgnoreCase(font.family())) {
bodyFamily = font.family();
String style = font.style();
if (style != null) {
style = style.toLowerCase(Locale.ROOT);
if (style.contains("thin"))
weight = "100";
else if (style.contains("extralight") || style.contains("extra light") || style.contains("ultralight") | style.contains("ultra light"))
weight = "200";
else if (style.contains("medium"))
weight = "500";
else if (style.contains("semibold") || style.contains("semi bold") || style.contains("demibold") || style.contains("demi bold"))
weight = "600";
else if (style.contains("extrabold") || style.contains("extra bold") || style.contains("ultrabold") || style.contains("ultra bold"))
weight = "800";
else if (style.contains("black") || style.contains("heavy"))
weight = "900";
else if (style.contains("light"))
weight = "lighter";
else if (style.contains("bold"))
weight = "bold";
if (style.contains("thin"))
weight = "100";
else if (style.contains("extralight") || style.contains("extra light") || style.contains("ultralight") | style.contains("ultra light"))
weight = "200";
else if (style.contains("medium"))
weight = "500";
else if (style.contains("semibold") || style.contains("semi bold") || style.contains("demibold") || style.contains("demi bold"))
weight = "600";
else if (style.contains("extrabold") || style.contains("extra bold") || style.contains("ultrabold") || style.contains("ultra bold"))
weight = "800";
else if (style.contains("black") || style.contains("heavy"))
weight = "900";
else if (style.contains("light"))
weight = "lighter";
else if (style.contains("bold"))
weight = "bold";
posture = style.contains("italic") || style.contains("oblique") ? "italic" : null;
posture = style.contains("italic") || style.contains("oblique") ? "italic" : null;
}
}
StringBuilder builder = new StringBuilder();
builder.append(".root {");
builder.append("-fx-font-family:\"").append(fontFamily).append("\";");
builder.append("-fx-font-family:\"").append(bodyFamily).append("\", \"Source Han Serif\", serif;");
if (weight != null)
builder.append("-fx-font-weight:").append(weight).append(";");
if (posture != null)
builder.append("-fx-font-style:").append(posture).append(";");
builder.append("}\n");
builder.append('}');
builder.append("""
.jfx-decorator-title,
.jfx-tool-bar-second .label,
.title-label,
.class-title,
.class-title Text,
.jfx-layout-heading,
.launch-pane > .launch-button .label,
.announcement .title,
.method-title,
.radio-button-title-label,
.two-line-list-item > .title,
.advanced-list-item > .rippler-container > .container > .title,
.jfx-button-raised,
.jfx-button-raised * {
""");
builder.append("-fx-font-family:\"").append(titleFamily).append("\", \"Source Han Sans\", \"思源黑体\", sans-serif;");
builder.append("-fx-font-weight:bold;");
builder.append("}\n");
return toStyleSheetUri(builder.toString());
}
@@ -121,13 +149,133 @@ public final class StyleSheets {
.append(";\n");
}
private static void putHex(StringBuilder builder, String name, String hex) {
builder.append(" ").append(name).append(": ").append(hex).append(";\n");
}
/// Maps anthropic-style-cn skill base.css light/dark pairs onto Monet roles so existing CSS flips end-to-end.
private static void applySkillTokenOverrides(StringBuilder builder, boolean dark) {
// Skill accent (CTA / focus) — never Material blue.
final String accent = "#D97757";
final String accentWarm = "#C96442";
final String error = "#C0453A";
final String onAccent = "#FAF9F5";
if (dark) {
putHex(builder, "-monet-surface", "#1A1916");
putHex(builder, "-monet-background", "#1A1916");
putHex(builder, "-monet-surface-bright", "#2C2B26");
putHex(builder, "-monet-surface-dim", "#141413");
putHex(builder, "-monet-surface-container-lowest", "#141413");
putHex(builder, "-monet-surface-container-low", "#222119");
putHex(builder, "-monet-surface-container", "#222119");
putHex(builder, "-monet-surface-container-high", "#2C2B26");
putHex(builder, "-monet-surface-container-highest", "#2C2B26");
putHex(builder, "-monet-surface-variant", "#2C2B26");
putHex(builder, "-monet-on-surface", "#EAE7DC");
putHex(builder, "-monet-on-background", "#EAE7DC");
putHex(builder, "-monet-on-surface-variant", "#9D9A91");
putHex(builder, "-monet-outline", "#3A3830");
putHex(builder, "-monet-outline-variant", "#2E2D27");
putHex(builder, "-monet-inverse-surface", "#F5F3EC");
putHex(builder, "-monet-inverse-on-surface", "#141413");
putHex(builder, "-monet-secondary-container", "#2C2B26");
putHex(builder, "-monet-on-secondary-container", "#EAE7DC");
putHex(builder, "-anthropic-shadow", "rgba(0, 0, 0, 0.50)");
putHex(builder, "-anthropic-shadow-strong", "rgba(0, 0, 0, 0.65)");
putHex(builder, "-anthropic-scrollbar-track", "#222119");
putHex(builder, "-anthropic-scrollbar-thumb", "#5C5A54");
putHex(builder, "-anthropic-hover-wash", "rgba(234, 231, 220, 0.08)");
putHex(builder, "-anthropic-press-wash", "rgba(234, 231, 220, 0.14)");
} else {
putHex(builder, "-monet-surface", "#ECE9E0");
putHex(builder, "-monet-background", "#ECE9E0");
putHex(builder, "-monet-surface-bright", "#FDFCF8");
putHex(builder, "-monet-surface-dim", "#D8D5CC");
putHex(builder, "-monet-surface-container-lowest", "#FDFCF8");
putHex(builder, "-monet-surface-container-low", "#F5F3EC");
putHex(builder, "-monet-surface-container", "#F5F3EC");
putHex(builder, "-monet-surface-container-high", "#FDFCF8");
putHex(builder, "-monet-surface-container-highest", "#FDFCF8");
putHex(builder, "-monet-surface-variant", "#E8E6DC");
putHex(builder, "-monet-on-surface", "#141413");
putHex(builder, "-monet-on-background", "#141413");
putHex(builder, "-monet-on-surface-variant", "#6B6860");
putHex(builder, "-monet-outline", "#D8D5CC");
putHex(builder, "-monet-outline-variant", "#E8E6DC");
putHex(builder, "-monet-inverse-surface", "#141413");
putHex(builder, "-monet-inverse-on-surface", "#FAF9F5");
putHex(builder, "-monet-secondary-container", "#E8E6DC");
putHex(builder, "-monet-on-secondary-container", "#141413");
putHex(builder, "-anthropic-shadow", "rgba(20, 20, 19, 0.10)");
putHex(builder, "-anthropic-shadow-strong", "rgba(20, 20, 19, 0.16)");
putHex(builder, "-anthropic-scrollbar-track", "#E8E6DC");
putHex(builder, "-anthropic-scrollbar-thumb", "#9B9890");
putHex(builder, "-anthropic-hover-wash", "rgba(20, 20, 19, 0.06)");
putHex(builder, "-anthropic-press-wash", "rgba(20, 20, 19, 0.12)");
}
putHex(builder, "-monet-primary", accent);
putHex(builder, "-monet-primary-seed", accent);
putHex(builder, "-monet-on-primary", onAccent);
putHex(builder, "-monet-primary-container", accentWarm);
putHex(builder, "-monet-on-primary-container", onAccent);
putHex(builder, "-monet-primary-fixed", dark ? "#4A2E24" : "#F6E0D6");
putHex(builder, "-monet-primary-fixed-dim", dark ? "#C96442" : accent);
putHex(builder, "-monet-on-primary-fixed", dark ? "#FAF9F5" : "#141413");
putHex(builder, "-monet-on-primary-fixed-variant", dark ? "#EAE7DC" : "#6B6860");
putHex(builder, "-monet-inverse-primary", accent);
putHex(builder, "-monet-surface-tint", accent);
// Secondary / tertiary: warm orange family only — kill Monet teal/purple companions.
putHex(builder, "-monet-secondary", accentWarm);
putHex(builder, "-monet-on-secondary", onAccent);
putHex(builder, "-monet-secondary-fixed", dark ? "#3A2A22" : "#F0E4DC");
putHex(builder, "-monet-secondary-fixed-dim", dark ? accentWarm : accent);
putHex(builder, "-monet-on-secondary-fixed", dark ? "#EAE7DC" : "#141413");
putHex(builder, "-monet-on-secondary-fixed-variant", dark ? "#9D9A91" : "#6B6860");
putHex(builder, "-monet-tertiary", accent);
putHex(builder, "-monet-on-tertiary", onAccent);
putHex(builder, "-monet-tertiary-container", accentWarm);
putHex(builder, "-monet-on-tertiary-container", onAccent);
putHex(builder, "-monet-tertiary-fixed", dark ? "#4A2E24" : "#F6E0D6");
putHex(builder, "-monet-tertiary-fixed-dim", dark ? accentWarm : accent);
putHex(builder, "-monet-on-tertiary-fixed", dark ? "#FAF9F5" : "#141413");
putHex(builder, "-monet-on-tertiary-fixed-variant", dark ? "#EAE7DC" : "#6B6860");
putHex(builder, "-monet-error", error);
putHex(builder, "-monet-on-error", onAccent);
putHex(builder, "-monet-error-container", dark ? "#4A1A1F" : "#FCEBEB");
putHex(builder, "-monet-on-error-container", dark ? "#F9DEDC" : "#410E0B");
putHex(builder, "-anthropic-accent", accent);
putHex(builder, "-anthropic-accent-warm", accentWarm);
putHex(builder, "-anthropic-focus-ring", accent);
putHex(builder, "-anthropic-focus-glow", "rgba(217, 119, 87, 0.35)");
putHex(builder, "-anthropic-primary-hover", dark ? "#E08A6C" : accentWarm);
putHex(builder, "-anthropic-primary-press", dark ? "#EFA08A" : "#B4573A");
putHex(builder, "-anthropic-primary-container-hover", dark ? "#D97757" : "#B4573A");
putHex(builder, "-anthropic-primary-container-press", dark ? "#E08A6C" : "#9A4A32");
putHex(builder, "-anthropic-selected-wash", dark ? "rgba(217, 119, 87, 0.22)" : "rgba(217, 119, 87, 0.14)");
// Stronger wash for tiny icon buttons where 6–8% alpha is invisible.
putHex(builder, "-anthropic-icon-hover-wash", dark ? "rgba(234, 231, 220, 0.14)" : "rgba(20, 20, 19, 0.10)");
putHex(builder, "-anthropic-icon-press-wash", dark ? "rgba(217, 119, 87, 0.32)" : "rgba(217, 119, 87, 0.22)");
// Transparent helpers consumed by root.css
putHex(builder, "-monet-primary-transparent-50", accent + "80");
putHex(builder, "-monet-primary-transparent-80", accent + "CC");
putHex(builder, "-monet-surface-transparent-50", dark ? "#1A191680" : "#ECE9E080");
putHex(builder, "-monet-surface-transparent-80", dark ? "#1A1916CC" : "#ECE9E0CC");
putHex(builder, "-monet-surface-container-low-transparent-80", dark ? "#222119CC" : "#F5F3ECCC");
putHex(builder, "-monet-secondary-container-transparent-50", dark ? "#2C2B2680" : "#E8E6DC80");
putHex(builder, "-monet-secondary-container-transparent-80", dark ? "#2C2B26CC" : "#E8E6DCCC");
putHex(builder, "-monet-on-surface-variant-transparent-38", dark ? "#9D9A9161" : "#6B686061");
putHex(builder, "-monet-inverse-surface-transparent-80", dark ? "#F5F3ECCC" : "#141413CC");
}
private static String getThemeStyleSheet() {
final String blueCss = "/assets/css/blue.css";
if (ResolvedTheme.DEFAULT.equals(Themes.getTheme()))
return blueCss;
ColorScheme scheme = Themes.getColorScheme();
boolean dark = scheme.getBrightness() == Brightness.DARK;
StringBuilder builder = new StringBuilder();
builder.append("* {\n");
@@ -147,6 +295,9 @@ public final class StyleSheets {
addColor(builder, scheme, ColorRole.SECONDARY_CONTAINER, 0.8);
addColor(builder, scheme, ColorRole.INVERSE_SURFACE, 0.8);
// Skill authority: overwrite Monet surfaces/text/borders/accent for light+dark.
applySkillTokenOverrides(builder, dark);
builder.append("}\n");
return toStyleSheetUri(builder.toString());
}
@@ -24,10 +24,19 @@ public record ResolvedTheme(ThemeColor primaryColorSeed,
/// Converts these resolved values to a MonetFX color scheme.
///
/// Warm secondary / tertiary / neutral seeds keep Monet from inventing teal or purple
/// companions next to the orange accent; CSS + [SkillPalette] still pin exact skill hex.
///
/// @return the generated color scheme
public ColorScheme toColorScheme() {
boolean darkNeutrals = brightness == Brightness.DARK;
return ColorScheme.newBuilder()
.setPrimaryColorSeed(primaryColorSeed.color())
.setSecondaryColorSeed(SkillPalette.ACCENT_WARM)
.setTertiaryColorSeed(SkillPalette.ACCENT)
.setNeutralColorSeed(darkNeutrals ? SkillPalette.DARK_SURFACE : SkillPalette.LIGHT_SURFACE)
.setNeutralVariantColorSeed(darkNeutrals ? SkillPalette.DARK_OUTLINE : SkillPalette.LIGHT_OUTLINE)
.setErrorColorSeed(SkillPalette.ERROR)
.setColorStyle(colorStyle)
.setBrightness(brightness)
.setSpecVersion(ColorSpecVersion.SPEC_2025)
@@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: Copyright (C) 2025 The HMCL Authors
// SPDX-License-Identifier: GPL-3.0-or-later WITH AdditionRef-HMCL
package org.jackhuang.hmcl.theme;
import javafx.scene.paint.Color;
import org.glavo.monetfx.Brightness;
import org.glavo.monetfx.ColorScheme;
import org.jetbrains.annotations.NotNullByDefault;
/// Anthropic-style-cn skill light/dark palette used for JavaFX chrome that bypasses CSS.
///
/// Monet's generated scheme from an orange seed still drifts pink / teal; these exact
/// skill tokens keep title bar, frame, and theme-color backgrounds aligned with
/// [org.jackhuang.hmcl.setting.StyleSheets] CSS overrides.
@NotNullByDefault
public final class SkillPalette {
public static final String ACCENT_HEX = "#D97757";
public static final String ACCENT_WARM_HEX = "#C96442";
public static final String ERROR_HEX = "#C0453A";
public static final String ON_ACCENT_HEX = "#FAF9F5";
public static final Color ACCENT = Color.web(ACCENT_HEX);
public static final Color ACCENT_WARM = Color.web(ACCENT_WARM_HEX);
public static final Color ERROR = Color.web(ERROR_HEX);
public static final Color ON_ACCENT = Color.web(ON_ACCENT_HEX);
public static final Color LIGHT_SURFACE = Color.web("#ECE9E0");
public static final Color LIGHT_SURFACE_RAISED = Color.web("#F5F3EC");
public static final Color LIGHT_SURFACE_OVERLAY = Color.web("#FDFCF8");
public static final Color LIGHT_SURFACE_VARIANT = Color.web("#E8E6DC");
public static final Color LIGHT_ON_SURFACE = Color.web("#141413");
public static final Color LIGHT_ON_SURFACE_VARIANT = Color.web("#6B6860");
public static final Color LIGHT_OUTLINE = Color.web("#D8D5CC");
public static final Color LIGHT_OUTLINE_VARIANT = Color.web("#E8E6DC");
public static final Color DARK_SURFACE = Color.web("#1A1916");
public static final Color DARK_SURFACE_RAISED = Color.web("#222119");
public static final Color DARK_SURFACE_OVERLAY = Color.web("#2C2B26");
public static final Color DARK_SURFACE_VARIANT = Color.web("#2C2B26");
public static final Color DARK_ON_SURFACE = Color.web("#EAE7DC");
public static final Color DARK_ON_SURFACE_VARIANT = Color.web("#9D9A91");
public static final Color DARK_OUTLINE = Color.web("#3A3830");
public static final Color DARK_OUTLINE_VARIANT = Color.web("#2E2D27");
private SkillPalette() {
}
public static boolean isDark(ColorScheme scheme) {
return scheme.getBrightness() == Brightness.DARK;
}
public static boolean isDark() {
return isDark(Themes.getColorScheme());
}
public static Color surface(boolean dark) {
return dark ? DARK_SURFACE : LIGHT_SURFACE;
}
public static Color surfaceContainer(boolean dark) {
return dark ? DARK_SURFACE_RAISED : LIGHT_SURFACE_RAISED;
}
public static Color surfaceContainerHigh(boolean dark) {
return dark ? DARK_SURFACE_OVERLAY : LIGHT_SURFACE_OVERLAY;
}
public static Color onSurface(boolean dark) {
return dark ? DARK_ON_SURFACE : LIGHT_ON_SURFACE;
}
public static Color onSurfaceVariant(boolean dark) {
return dark ? DARK_ON_SURFACE_VARIANT : LIGHT_ON_SURFACE_VARIANT;
}
public static Color outline(boolean dark) {
return dark ? DARK_OUTLINE : LIGHT_OUTLINE;
}
public static Color surface() {
return surface(isDark());
}
public static Color surfaceContainer() {
return surfaceContainer(isDark());
}
public static Color onSurface() {
return onSurface(isDark());
}
}
@@ -26,10 +26,13 @@ import java.util.Objects;
@JsonSerializable
public record ThemeColor(@NotNull String name, @NotNull Color color) {
public static final ThemeColor DEFAULT = new ThemeColor("blue", Color.web("#5C6BC0"));
/// Default Anthropic accent seed (skill: `--color-accent-orange`).
public static final ThemeColor DEFAULT = new ThemeColor("anthropic_orange", Color.web("#D97757"));
public static final List<ThemeColor> STANDARD_COLORS = List.of(
DEFAULT,
new ThemeColor("stone", Color.web("#3D453C")),
new ThemeColor("blue", Color.web("#5C6BC0")),
new ThemeColor("darker_blue", Color.web("#283593")),
new ThemeColor("green", Color.web("#43A047")),
new ThemeColor("orange", Color.web("#E67E22")),
@@ -29,7 +29,6 @@ import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import org.glavo.monetfx.Brightness;
import org.glavo.monetfx.ColorScheme;
import org.glavo.monetfx.ColorRole;
import org.glavo.monetfx.ColorStyle;
import org.glavo.monetfx.Contrast;
import org.glavo.monetfx.beans.property.ColorSchemeProperty;
@@ -800,7 +799,8 @@ public final class Themes {
opacity = background.opacity();
}
Paint themeColorPaint = appearance.toResolvedTheme(context).toColorScheme().getColor(ColorRole.SURFACE_CONTAINER);
boolean darkFallback = appearance.toResolvedTheme(context).toColorScheme().getBrightness() == Brightness.DARK;
Paint themeColorPaint = SkillPalette.surfaceContainer(darkFallback);
BackgroundType fallbackType = Objects.requireNonNullElse(
settings().backgroundFallbackTypeProperty().get(),
BackgroundType.BUILTIN);
@@ -960,7 +960,7 @@ public final class Themes {
/// Creates a JavaFX background from the current theme color scheme surface container.
private static LauncherBackground createThemeColorBackground(double opacity) {
return createPaintBackground(getColorScheme().getColor(ColorRole.SURFACE_CONTAINER), opacity);
return createPaintBackground(SkillPalette.surfaceContainer(), opacity);
}
/// Creates a JavaFX image background with a separately stored node opacity.
@@ -1103,10 +1103,11 @@ public final class Themes {
);
/// The title text fill derived from the current color scheme.
///
/// Title chrome uses surface tones (Anthropic-like calm bar), so fill stays on-surface
/// whether or not the bar is translucent.
private static final ObjectBinding<Color> titleFill = Bindings.createObjectBinding(
() -> titleBarTransparent.get()
? getColorScheme().getOnSurface()
: getColorScheme().getOnPrimaryContainer(),
() -> SkillPalette.onSurface(),
colorSchemeProperty(),
titleBarTransparent
);
@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright (C) 2020 The HMCL Authors
// SPDX-License-Identifier: GPL-3.0-or-later WITH AdditionRef-HMCL
package org.jackhuang.hmcl.ui;
/// Golden-ratio layout metrics for Anthropic tool-mode chrome.
///
/// Primary splits use φ ≈ 1.618 (sidebar:content = 1:φ). Spacing uses the φ ladder
/// from an 8px base so hierarchy stays dense — not landing-page whitespace.
public final class LayoutMetrics {
/// Golden ratio.
public static final double PHI = 1.618033988749895;
/// Sidebar share of a page: 1 / (1 + φ) ≈ 0.382 so sidebar:content = 1:φ.
public static final double SIDEBAR_FRACTION = 1.0 / (1.0 + PHI);
/// Design-minimum page width used when the node has not been laid out yet.
public static final double DESIGN_PAGE_WIDTH = 800.0;
/// Tool-density clamp: keep the rail usable without eating the content column.
public static final double SIDEBAR_MIN = 220.0;
/// φ share of [#DESIGN_PAGE_WIDTH] — exact 1:φ at the launcher minimum width.
public static final double SIDEBAR_MAX = Math.rint(DESIGN_PAGE_WIDTH * SIDEBAR_FRACTION); // 306
/// φ spacing ladder from an 8px base (…, 5, 8, 13, 21, 34).
public static final double SPACE_XS = 5.0;
public static final double SPACE_SM = 8.0;
public static final double SPACE_MD = 13.0;
public static final double SPACE_LG = 21.0;
public static final double SPACE_XL = 34.0;
/// Title-bar / touch target height (a11y floor from the skill).
public static final double TITLE_BAR = 44.0;
/// Primary CTA height on the home page (Fibonacci neighbour of title bar / φ body).
public static final double LAUNCH_HEIGHT = 55.0;
/// Primary CTA width (Fibonacci; pairs with the φ sidebar rail).
public static final double LAUNCH_WIDTH = 233.0;
private LayoutMetrics() {
}
/// Resolves the fixed sidebar width for a page of the given width.
///
/// @param pageWidth the animated page width in pixels; values ≤ 1 fall back to the design minimum
/// @return a width clamped to [{@link #SIDEBAR_MIN}, {@link #SIDEBAR_MAX}] on the 1:φ fraction
public static double sidebarWidth(double pageWidth) {
double width = pageWidth > 1.0 ? pageWidth : DESIGN_PAGE_WIDTH;
return Math.rint(Math.max(SIDEBAR_MIN, Math.min(SIDEBAR_MAX, width * SIDEBAR_FRACTION)));
}
}
@@ -121,7 +121,7 @@ public enum ContainerAnimations implements TransitionPane.AnimationProducer {
/// Animates forward navigation with temporary incoming-page caching.
@Override
public Timeline animate(Pane container, Node previousNode, Node nextNode) {
double offset = container.getWidth() > 0 ? container.getWidth() * 0.2 : 50;
double offset = container.getWidth() > 0 ? container.getWidth() * 0.06 : 16;
return new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(nextNode.cacheProperty(), isCacheAllowed(nextNode), Interpolator.DISCRETE),
@@ -147,12 +147,12 @@ public enum ContainerAnimations implements TransitionPane.AnimationProducer {
}
},
/// @see <a href="https://m3.material.io/styles/motion/transitions/transition-patterns">Transitions - Material Design 3</a>
/// Soft reverse of [#FORWARD] (reduced travel distance for Anthropic-calm chrome).
BACKWARD {
/// Animates backward navigation with temporary incoming-page caching.
@Override
public Timeline animate(Pane container, Node previousNode, Node nextNode) {
double offset = container.getWidth() > 0 ? container.getWidth() * 0.2 : 50;
double offset = container.getWidth() > 0 ? container.getWidth() * 0.06 : 16;
return new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(nextNode.cacheProperty(), isCacheAllowed(nextNode), Interpolator.DISCRETE),
@@ -210,8 +210,8 @@ public enum ContainerAnimations implements TransitionPane.AnimationProducer {
new KeyValue(left.translateXProperty(), 0, DEFAULT_INTERPOLATOR),
new KeyValue(center.translateXProperty(), 0, DEFAULT_INTERPOLATOR)));
timeline.getKeyFrames().add(new KeyFrame(DEFAULT_DURATION_HALF,
new KeyValue(left.translateXProperty(), -30, DEFAULT_INTERPOLATOR),
new KeyValue(center.translateXProperty(), 30, DEFAULT_INTERPOLATOR)));
new KeyValue(left.translateXProperty(), -10, DEFAULT_INTERPOLATOR),
new KeyValue(center.translateXProperty(), 10, DEFAULT_INTERPOLATOR)));
timeline.getKeyFrames().add(new KeyFrame(DEFAULT_DURATION,
new KeyValue(left.cacheProperty(), left.isCache(), Interpolator.DISCRETE),
new KeyValue(center.cacheProperty(), center.isCache(), Interpolator.DISCRETE)));
@@ -233,8 +233,8 @@ public enum ContainerAnimations implements TransitionPane.AnimationProducer {
new KeyValue(left.cacheProperty(), isCacheAllowed(left), Interpolator.DISCRETE),
new KeyValue(center.cacheProperty(), cacheCenter, Interpolator.DISCRETE)));
timeline.getKeyFrames().add(new KeyFrame(DEFAULT_DURATION_HALF,
new KeyValue(left.translateXProperty(), -30, DEFAULT_INTERPOLATOR),
new KeyValue(center.translateXProperty(), 30, DEFAULT_INTERPOLATOR)));
new KeyValue(left.translateXProperty(), -10, DEFAULT_INTERPOLATOR),
new KeyValue(center.translateXProperty(), 10, DEFAULT_INTERPOLATOR)));
timeline.getKeyFrames().add(new KeyFrame(DEFAULT_DURATION,
new KeyValue(left.cacheProperty(), left.isCache(), Interpolator.DISCRETE),
new KeyValue(center.cacheProperty(), center.isCache(), Interpolator.DISCRETE),
@@ -22,6 +22,18 @@ public final class Motion {
/// @see <a href="https://flutter.github.io/assets-for-api-docs/assets/animation/curve_linear.mp4">curve_linear.mp4</a>
public static final Interpolator LINEAR = Interpolator.LINEAR;
/// Anthropic skill default ease: `cubic-bezier(0.16, 1, 0.3, 1)` — transform/opacity only.
public static final Interpolator ANTHROPIC = new Cubic(0.16, 1.0, 0.3, 1.0);
/// Skill `--duration-fast` (150ms).
public static final Duration FAST = Duration.millis(150);
/// Skill `--duration-normal` (250ms).
public static final Duration NORMAL = Duration.millis(250);
/// Skill `--duration-slow` (400ms).
public static final Duration SLOW = Duration.millis(400);
/// The emphasizedAccelerate easing curve in the Material specification.
///
/// See also:
@@ -22,11 +22,11 @@ import org.jetbrains.annotations.Nullable;
@NotNullByDefault
public class TransitionPane extends StackPane {
/// The default easing curve for page transitions.
public static final Interpolator DEFAULT_INTERPOLATOR = Motion.EASE;
/// Skill ease: cubic-bezier(0.16, 1, 0.3, 1) — no Material overshoot.
public static final Interpolator DEFAULT_INTERPOLATOR = Motion.ANTHROPIC;
/// The default duration for page transitions.
public static final Duration DEFAULT_DURATION = Motion.SHORT4;
/// Skill `--duration-fast` (150ms).
public static final Duration DEFAULT_DURATION = Motion.FAST;
/// Half the default duration for transitions with two phases.
public static final Duration DEFAULT_DURATION_HALF = DEFAULT_DURATION.divide(2);
@@ -188,8 +188,9 @@ public final class TabController implements PageAware {
/// Mirrors the original slide-up and fade-in transition vertically.
TAB_DOWN(-1.0);
private static final Duration DURATION = Motion.MEDIUM4;
private static final Duration DURATION = Motion.NORMAL;
private static final Duration HALF_DURATION = DURATION.divide(2);
private static final Interpolator EASING = Motion.ANTHROPIC;
private final double direction;
@@ -200,22 +201,23 @@ public final class TabController implements PageAware {
@Override
public Animation animate(Pane container, Node previousNode, Node nextNode) {
double extent = container.getHeight();
double offset = (extent > 0 ? extent * 0.2 : 50) * direction;
// Small vertical drift + fade — avoids Material emphasized/overshoot motion.
double offset = (extent > 0 ? extent * 0.06 : 16) * direction;
var previousTranslation = previousNode.translateYProperty();
var nextTranslation = nextNode.translateYProperty();
return new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(nextNode.cacheProperty(), isCacheAllowed(nextNode), Interpolator.DISCRETE),
new KeyValue(previousTranslation, 0, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED),
new KeyValue(previousNode.opacityProperty(), 1, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED),
new KeyValue(nextNode.opacityProperty(), 0, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED),
new KeyValue(nextTranslation, offset, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED)),
new KeyValue(previousTranslation, 0, EASING),
new KeyValue(previousNode.opacityProperty(), 1, EASING),
new KeyValue(nextNode.opacityProperty(), 0, EASING),
new KeyValue(nextTranslation, offset, EASING)),
new KeyFrame(HALF_DURATION,
new KeyValue(previousNode.opacityProperty(), 0, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED)),
new KeyValue(previousNode.opacityProperty(), 0, EASING)),
new KeyFrame(DURATION,
new KeyValue(nextNode.cacheProperty(), nextNode.isCache(), Interpolator.DISCRETE),
new KeyValue(nextNode.opacityProperty(), 1, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED),
new KeyValue(nextTranslation, 0, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED)));
new KeyValue(nextNode.opacityProperty(), 1, EASING),
new KeyValue(nextTranslation, 0, EASING)));
}
@Override
@@ -83,8 +83,8 @@ public final class Decorator {
/// The minimum width, in pixels, of the visible window content.
private static final double MIN_CONTENT_WIDTH = 800.0;
/// The minimum height, in pixels, including the 450-pixel background and 40-pixel title bar.
private static final double MIN_CONTENT_HEIGHT = 450.0 + 40.0;
/// The minimum height, in pixels, including the 450-pixel background and 44-pixel title bar.
private static final double MIN_CONTENT_HEIGHT = 450.0 + 44.0;
/// The minimum visible overlap, in pixels, used to accept a persisted window position.
private static final double MIN_VISIBLE_WINDOW_AREA = 20.0;
@@ -975,13 +975,13 @@ public final class Decorator {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(root.opacityProperty(), 0, Motion.EASE),
new KeyValue(root.scaleXProperty(), 0.8, Motion.EASE),
new KeyValue(root.scaleYProperty(), 0.8, Motion.EASE)),
new KeyFrame(Duration.millis(600),
new KeyValue(root.opacityProperty(), 1, Motion.EASE),
new KeyValue(root.scaleXProperty(), 1, Motion.EASE),
new KeyValue(root.scaleYProperty(), 1, Motion.EASE)));
new KeyValue(root.opacityProperty(), 0, Motion.ANTHROPIC),
new KeyValue(root.scaleXProperty(), 0.985, Motion.ANTHROPIC),
new KeyValue(root.scaleYProperty(), 0.985, Motion.ANTHROPIC)),
new KeyFrame(Motion.NORMAL,
new KeyValue(root.opacityProperty(), 1, Motion.ANTHROPIC),
new KeyValue(root.scaleXProperty(), 1, Motion.ANTHROPIC),
new KeyValue(root.scaleYProperty(), 1, Motion.ANTHROPIC)));
playWindowAnimation(timeline, () -> {
resetRootTransform();
@@ -1003,15 +1003,11 @@ public final class Decorator {
if (!nativeDecorationEnabled && AnimationUtils.playWindowAnimation() && OperatingSystem.CURRENT_OS != OperatingSystem.MACOS) {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(root.opacityProperty(), 1, Motion.EASE),
new KeyValue(root.translateYProperty(), 0, Motion.EASE),
new KeyValue(root.scaleXProperty(), 1, Motion.EASE),
new KeyValue(root.scaleYProperty(), 1, Motion.EASE)),
new KeyFrame(Motion.SHORT4,
new KeyValue(root.opacityProperty(), 0, Motion.EASE),
new KeyValue(root.translateYProperty(), 200, Motion.EASE),
new KeyValue(root.scaleXProperty(), 0.4, Motion.EASE),
new KeyValue(root.scaleYProperty(), 0.4, Motion.EASE)));
new KeyValue(root.opacityProperty(), 1, Motion.ANTHROPIC),
new KeyValue(root.translateYProperty(), 0, Motion.ANTHROPIC)),
new KeyFrame(Motion.FAST,
new KeyValue(root.opacityProperty(), 0, Motion.ANTHROPIC),
new KeyValue(root.translateYProperty(), 24, Motion.ANTHROPIC)));
playWindowAnimation(timeline, () -> {
if (getStage() == currentStage) {
currentStage.setIconified(true);
@@ -1032,13 +1028,9 @@ public final class Decorator {
if (!nativeDecorationEnabled && AnimationUtils.playWindowAnimation()) {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(root.opacityProperty(), 1, Motion.EASE),
new KeyValue(root.scaleXProperty(), 1, Motion.EASE),
new KeyValue(root.scaleYProperty(), 1, Motion.EASE)),
new KeyFrame(Duration.millis(200),
new KeyValue(root.opacityProperty(), 0, Motion.EASE),
new KeyValue(root.scaleXProperty(), 0.8, Motion.EASE),
new KeyValue(root.scaleYProperty(), 0.8, Motion.EASE)));
new KeyValue(root.opacityProperty(), 1, Motion.ANTHROPIC)),
new KeyFrame(Motion.FAST,
new KeyValue(root.opacityProperty(), 0, Motion.ANTHROPIC)));
playWindowAnimation(timeline, Launcher::stopApplication);
} else {
stopWindowAnimation();
@@ -1172,15 +1164,11 @@ public final class Decorator {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(root.opacityProperty(), 0, Motion.EASE),
new KeyValue(root.translateYProperty(), 200, Motion.EASE),
new KeyValue(root.scaleXProperty(), 0.4, Motion.EASE),
new KeyValue(root.scaleYProperty(), 0.4, Motion.EASE)),
new KeyFrame(Motion.SHORT4,
new KeyValue(root.opacityProperty(), 1, Motion.EASE),
new KeyValue(root.translateYProperty(), 0, Motion.EASE),
new KeyValue(root.scaleXProperty(), 1, Motion.EASE),
new KeyValue(root.scaleYProperty(), 1, Motion.EASE)));
new KeyValue(root.opacityProperty(), 0, Motion.ANTHROPIC),
new KeyValue(root.translateYProperty(), 24, Motion.ANTHROPIC)),
new KeyFrame(Motion.FAST,
new KeyValue(root.opacityProperty(), 1, Motion.ANTHROPIC),
new KeyValue(root.translateYProperty(), 0, Motion.ANTHROPIC)));
playWindowAnimation(timeline, this::resetRootTransform);
}
@@ -2,16 +2,19 @@
// SPDX-License-Identifier: GPL-3.0-or-later WITH AdditionRef-HMCL
package org.jackhuang.hmcl.ui.decorator;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.LayoutMetrics;
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
import org.jetbrains.annotations.NotNullByDefault;
import org.jetbrains.annotations.Nullable;
/// Lays out a fixed-width sidebar and a growing content area as independent animation targets.
/// Lays out a φ-proportioned sidebar and a growing content area as independent animation targets.
///
/// Sidebar:content ≈ 1:φ (see [LayoutMetrics]), clamped for tool-mode density.
@NotNullByDefault
public class DecoratorAnimatedPage extends BorderPane {
@@ -25,7 +28,12 @@ public class DecoratorAnimatedPage extends BorderPane {
/// Creates both animation targets without requiring CSS or skin initialization.
public DecoratorAnimatedPage() {
getStyleClass().add("gray-background");
FXUtils.setLimitWidth(left, 200);
var sidebarWidth = Bindings.createDoubleBinding(
() -> LayoutMetrics.sidebarWidth(getWidth()),
widthProperty());
left.minWidthProperty().bind(sidebarWidth);
left.prefWidthProperty().bind(sidebarWidth);
left.maxWidthProperty().bind(sidebarWidth);
sidebar.setMinSize(0, 0);
left.setCenter(sidebar);
setLeft(left);
@@ -42,11 +50,11 @@ public class DecoratorAnimatedPage extends BorderPane {
return left.getBottom();
}
/// Replaces the bottom content, reserving its preferred height and a 12-pixel bottom gap.
/// Replaces the bottom content, reserving its preferred height and a φ-md bottom gap.
/// Passing null removes it without replacing the sidebar animation target.
public void setSidebarBottom(@Nullable Node node) {
if (node != null) {
BorderPane.setMargin(node, new Insets(0, 0, 12, 0));
BorderPane.setMargin(node, new Insets(0, 0, LayoutMetrics.SPACE_MD, 0));
}
left.setBottom(node);
}
@@ -24,8 +24,8 @@ import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
import org.glavo.monetfx.ColorRole;
import org.glavo.monetfx.ColorScheme;
import org.jackhuang.hmcl.theme.SkillPalette;
import org.jackhuang.hmcl.theme.Themes;
import org.jackhuang.hmcl.ui.*;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
@@ -81,7 +81,7 @@ final class MainWindowPane extends StackPane {
() -> Themes.windowTransparentProperty().get()
? null
: new Background(new BackgroundFill(
Themes.getColorScheme().getColor(ColorRole.SURFACE_CONTAINER),
SkillPalette.surfaceContainer(),
CornerRadii.EMPTY,
Insets.EMPTY)),
Themes.windowTransparentProperty(),
@@ -173,7 +173,7 @@ final class MainWindowPane extends StackPane {
HBox buttons = new HBox();
buttons.setAlignment(Pos.TOP_RIGHT);
decorator.capableDraggingWindow(buttons);
buttons.setMaxSize(Region.USE_PREF_SIZE, 40);
buttons.setMaxSize(Region.USE_PREF_SIZE, 44);
JFXButton minimizeButton = new JFXButton();
minimizeButton.setFocusTraversable(false);
@@ -194,19 +194,22 @@ final class MainWindowPane extends StackPane {
}
/// Recomputes the title-bar background for the current theme and transparency preference.
///
/// Uses surface / surface-container tones so the chrome stays calm and brand-forward
/// instead of a saturated primary strip.
private void updateTitleBarBackground() {
@Nullable ColorScheme colorScheme = Themes.getColorScheme();
if (colorScheme == null) {
titleBar.setBackground(null);
} else if (Themes.titleBarTransparentProperty().get()) {
Color surface = colorScheme.getSurface();
Color surface = SkillPalette.surface(SkillPalette.isDark(colorScheme));
titleBar.setBackground(new Background(new BackgroundFill(
new Color(surface.getRed(), surface.getGreen(), surface.getBlue(), 0.5),
CornerRadii.EMPTY,
Insets.EMPTY)));
} else {
titleBar.setBackground(new Background(new BackgroundFill(
colorScheme.getPrimaryContainer(),
SkillPalette.surfaceContainer(SkillPalette.isDark(colorScheme)),
CornerRadii.EMPTY,
Insets.EMPTY)));
}
@@ -326,7 +329,7 @@ final class MainWindowPane extends StackPane {
/// {@inheritDoc}
@Override
public Timeline animate(Pane container, Node previousNode, Node nextNode) {
return createTimeline(previousNode, nextNode, 50, -50);
return createTimeline(previousNode, nextNode, 18, -18);
}
/// {@inheritDoc}
@@ -350,7 +353,7 @@ final class MainWindowPane extends StackPane {
/// {@inheritDoc}
@Override
public Timeline animate(Pane container, Node previousNode, Node nextNode) {
return createTimeline(previousNode, nextNode, -50, 50);
return createTimeline(previousNode, nextNode, -18, 18);
}
/// {@inheritDoc}
@@ -15,6 +15,7 @@ import javafx.scene.layout.*;
import org.jackhuang.hmcl.setting.ProxyType;
import org.jackhuang.hmcl.task.FetchTask;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.LayoutMetrics;
import org.jackhuang.hmcl.ui.WeakListenerHolder;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.util.Holder;
@@ -30,8 +31,8 @@ public class DownloadSettingsPage extends StackPane {
private final WeakListenerHolder holder = new WeakListenerHolder();
public DownloadSettingsPage() {
VBox content = new VBox(10);
content.setPadding(new Insets(10));
VBox content = new VBox(LayoutMetrics.SPACE_MD);
content.setPadding(new Insets(LayoutMetrics.SPACE_SM, LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_MD));
content.setFillWidth(true);
ScrollPane scrollPane = new ScrollPane(content);
FXUtils.smoothScrolling(scrollPane);
@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.setting.GameDirectoryManager;
import org.jackhuang.hmcl.theme.Themes;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.LayoutMetrics;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
@@ -79,7 +80,8 @@ public final class MainPage extends StackPane implements DecoratorPage {
state.setValue(new State(null, titleNode, false, false, true));
setPadding(new Insets(20));
// Tool-density φ ladder (skill): md/lg insets — not landing-page whitespace.
setPadding(new Insets(LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_LG, LayoutMetrics.SPACE_MD));
@@ -1017,7 +1017,7 @@ public class PersonalizationPage extends StackPane {
}
}
vbox.getChildren().add(new Label("Hello Minecraft! Launcher"));
vbox.getChildren().add(new Label("TDLM"));
fontPane.getContent().add(vbox);
}
@@ -14,6 +14,7 @@ import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.LayoutMetrics;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.util.Lang;
@@ -47,8 +48,9 @@ public final class SettingsPage extends ScrollPane {
public SettingsPage() {
this.setFitToWidth(true);
VBox rootPane = new VBox(10);
rootPane.setPadding(new Insets(10));
// Title:body φ ladder — tight tool density (sm gap, md/lg insets).
VBox rootPane = new VBox(LayoutMetrics.SPACE_SM);
rootPane.setPadding(new Insets(LayoutMetrics.SPACE_SM, LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_MD));
this.setContent(rootPane);
FXUtils.smoothScrolling(this);
@@ -1,35 +1,48 @@
/*
* Anthropic UI — dark brightness tokens (skill charcoal #1A1916 family).
* Loaded after the generated Monet theme sheet; keep AA contrast on surfaces/text.
*/
* {
-anthropic-shadow: rgba(0, 0, 0, 0.50);
-anthropic-shadow-strong: rgba(0, 0, 0, 0.65);
-anthropic-scrollbar-track: #222119;
-anthropic-scrollbar-thumb: #5C5A54;
-anthropic-hairline: -monet-outline-variant;
-anthropic-focus-ring: #D97757;
}
.hint {
-fx-hyperlink-fill: -monet-inverse-primary;
-fx-hyperlink-fill: -monet-primary;
}
.tag {
-fixed-warning-tag-background: #2c0b0e;
-fixed-warning-tag-background: #4A1A1F;
}
.log-window {
-fixed-log-background: #282828;
-fixed-log-toggle-selected: #DEE2E6;
-fixed-log-toggle-unselected: #6C757D;
-fixed-log-text-fill: #e3e3e3;
-fixed-log-trace-background: #495057;
-fixed-log-debug-background: #343A40;
-fixed-log-info-background: #212529;
-fixed-log-warn-background: #413c26;
-fixed-log-warn-text-fill: #fdf3aa;
-fixed-log-error-background: #4e3534;
-fixed-log-error-text-fill: #f9dedc;
-fixed-log-background: -monet-surface;
-fixed-log-toggle-selected: -monet-on-surface;
-fixed-log-toggle-unselected: -monet-on-surface-variant;
-fixed-log-text-fill: -monet-on-surface;
-fixed-log-trace-background: -monet-surface-container-high;
-fixed-log-debug-background: -monet-surface-container;
-fixed-log-info-background: -monet-surface;
-fixed-log-warn-background: #3F3A26;
-fixed-log-warn-text-fill: #F7E8A0;
-fixed-log-error-background: #4E3534;
-fixed-log-error-text-fill: #F9DEDC;
-fixed-log-fatal-background: #842029;
-fixed-log-selected: #3e3e3e;
-fixed-log-selected: -monet-surface-container-highest;
}
.addon-channel-icon.alpha .svg {
-fx-fill: #ff496e;
-fx-fill: #E08A8A;
}
.addon-channel-icon.beta .svg {
-fx-fill: #ffa347;
-fx-fill: #E08A6C;
}
.addon-channel-icon.release .svg {
-fx-fill: #1bd96a;
-fx-fill: #A3B58A;
}
@@ -1,3 +1,16 @@
/*
* Anthropic UI — light brightness tokens (skill-aligned warm neutrals).
* Loaded after the generated Monet theme sheet; keep AA contrast on surfaces/text.
*/
* {
-anthropic-shadow: rgba(20, 20, 19, 0.10);
-anthropic-shadow-strong: rgba(20, 20, 19, 0.16);
-anthropic-scrollbar-track: #E8E6DC;
-anthropic-scrollbar-thumb: #9B9890;
-anthropic-hairline: -monet-outline-variant;
-anthropic-focus-ring: #D97757;
}
.hint {
-fx-hyperlink-fill: -monet-primary;
}
@@ -7,29 +20,29 @@
}
.log-window {
-fixed-log-background: #ffffff;
-fixed-log-toggle-selected: #000000;
-fixed-log-toggle-unselected: #6C757D;
-fixed-log-text-fill: #1f1f1f;
-fixed-log-trace-background: #EEE9E0;
-fixed-log-debug-background: #EEE9E0;
-fixed-log-info-background: #FFFFFF;
-fixed-log-warn-background: #fef6d5;
-fixed-log-warn-text-fill: #3e2f00;
-fixed-log-error-background: #fcebeb;
-fixed-log-error-text-fill: #410e0b;
-fixed-log-background: -monet-surface;
-fixed-log-toggle-selected: -monet-on-surface;
-fixed-log-toggle-unselected: -monet-on-surface-variant;
-fixed-log-text-fill: -monet-on-surface;
-fixed-log-trace-background: -monet-surface-container;
-fixed-log-debug-background: -monet-surface-container-low;
-fixed-log-info-background: -monet-surface;
-fixed-log-warn-background: #F7F0D8;
-fixed-log-warn-text-fill: #3E2F00;
-fixed-log-error-background: #FCEBEB;
-fixed-log-error-text-fill: #410E0B;
-fixed-log-fatal-background: #F7A699;
-fixed-log-selected: #f2f2f2;
-fixed-log-selected: -monet-surface-container-highest;
}
.addon-channel-icon.alpha .svg {
-fx-fill: #cb2245;
-fx-fill: #C0453A;
}
.addon-channel-icon.beta .svg {
-fx-fill: #e08325;
-fx-fill: #C96442;
}
.addon-channel-icon.release .svg {
-fx-fill: #00af5c;
-fx-fill: #5F7A4A;
}
+50 -5
View File
@@ -1,8 +1,53 @@
/*
* SPDX-FileCopyrightText: Copyright (C) 2025 The HMCL Authors
* SPDX-License-Identifier: GPL-3.0-or-later WITH AdditionRef-HMCL
* Anthropic skill type roles (CN+Latin):
* - Sans = UI / nav / buttons / chrome titles (Source Han Sans)
* - Serif = body / narrative copy (Source Han Serif)
*/
.root {
/* https://github.com/HMCL-dev/HMCL/pull/3423 */
-fx-font-family: -fx-base-font-family;
}
-fx-font-family: "Source Han Serif SC", "Source Han Serif", "思源宋体", serif;
}
/* UI chrome + interactive — sans */
.jfx-decorator-title,
.jfx-tool-bar-second .label,
.jfx-tool-bar Label,
.title-label,
.class-title,
.class-title Text,
.jfx-layout-heading,
.launch-pane > .launch-button .label,
.announcement .title,
.method-title,
.radio-button-title-label,
.two-line-list-item > .title,
.advanced-list-item > .rippler-container > .container > .title,
.iconed-item .iconed-item-container,
.iconed-menu-item .iconed-item-container,
.options-list-item .label,
.jfx-button,
.jfx-button *,
.jfx-button-raised,
.jfx-button-raised *,
.jfx-button-border,
.jfx-button-border *,
.tab-label,
.dialog-accept,
.dialog-cancel,
.line-component .title-label,
.menu-container .title-label {
-fx-font-family: "Source Han Sans SC", "Source Han Sans", "思源黑体", sans-serif;
-fx-font-weight: bold;
}
/* Body / secondary copy — serif */
.subtitle-label,
.two-line-list-item > .subtitle,
.advanced-list-item > .rippler-container > .container > .subtitle,
.announcement text,
.method-desc,
.options-list-item .label.subtitle,
.hint,
.html {
-fx-font-family: "Source Han Serif SC", "Source Han Serif", "思源宋体", serif;
-fx-font-weight: normal;
}
@@ -0,0 +1,113 @@
/*
* Anthropic skill — hard press feedback (loaded LAST, beats Monet/root).
* Forced :pressed / :armed change background AND border AND opacity.
* Solid hex only (no fragile lookups) so Modena cyan cannot win on specificity.
*/
.jfx-button:pressed,
.jfx-button:armed,
.toggle-icon4:pressed,
.toggle-icon4:armed,
.toggle-icon-tiny:pressed,
.toggle-icon-tiny:armed,
.announcement-close-button:pressed,
.announcement-close-button:armed,
.jfx-decorator-button:pressed,
.jfx-decorator-button:armed,
.jfx-tool-bar-button:pressed,
.jfx-tool-bar-button:armed,
.jfx-tool-bar .jfx-decorator-button:pressed,
.jfx-tool-bar .jfx-decorator-button:armed {
-fx-background-color: rgba(217, 119, 87, 0.42);
-fx-border-color: #C96442;
-fx-border-width: 2px;
-fx-border-radius: 6px;
-fx-background-radius: 6px;
-fx-opacity: 0.84;
-fx-scale-x: 0.94;
-fx-scale-y: 0.94;
}
.jfx-button:hover,
.toggle-icon4:hover,
.toggle-icon-tiny:hover,
.announcement-close-button:hover,
.jfx-decorator-button:hover,
.jfx-tool-bar-button:hover {
-fx-background-color: rgba(217, 119, 87, 0.16);
-fx-border-color: rgba(201, 100, 66, 0.55);
-fx-border-width: 1px;
-fx-opacity: 1;
}
.jfx-button-raised:pressed,
.jfx-button-raised:armed,
.jfx-button-raised-round:pressed,
.jfx-button-raised-round:armed,
.launch-pane > .jfx-button:pressed,
.launch-pane > .jfx-button:armed {
-fx-background-color: #9A4A32;
-fx-border-color: #F5F3EC;
-fx-border-width: 2px;
-fx-opacity: 0.90;
-fx-scale-x: 0.96;
-fx-scale-y: 0.96;
}
.jfx-button-border:pressed,
.jfx-button-border:armed {
-fx-background-color: rgba(217, 119, 87, 0.28);
-fx-border-color: #C96442;
-fx-border-width: 2px;
-fx-opacity: 0.88;
-fx-scale-x: 0.96;
-fx-scale-y: 0.96;
}
.toggle-icon4:pressed,
.toggle-icon4:armed,
.toggle-icon-tiny:pressed,
.toggle-icon-tiny:armed,
.announcement-close-button:pressed,
.announcement-close-button:armed {
-fx-background-radius: 50px;
-fx-border-radius: 50px;
}
.jfx-button:disabled:pressed,
.jfx-button:disabled:armed,
.toggle-icon4:disabled:pressed,
.toggle-icon4:disabled:armed,
.toggle-icon-tiny:disabled:pressed,
.toggle-icon-tiny:disabled:armed,
.jfx-decorator-button:disabled:pressed,
.jfx-decorator-button:disabled:armed,
.jfx-tool-bar-button:disabled:pressed,
.jfx-tool-bar-button:disabled:armed {
-fx-opacity: 0.45;
-fx-background-color: transparent;
-fx-border-width: 0;
-fx-scale-x: 1;
-fx-scale-y: 1;
}
/* Skin-driven armed class — mirrors :pressed when Modena/CSS lose the race. */
.jfx-button.anthropic-armed,
.toggle-icon4.anthropic-armed,
.toggle-icon-tiny.anthropic-armed,
.jfx-decorator-button.anthropic-armed,
.jfx-tool-bar-button.anthropic-armed,
.announcement-close-button.anthropic-armed {
-fx-background-color: rgba(217, 119, 87, 0.42);
-fx-border-color: #C96442;
-fx-border-width: 2px;
-fx-opacity: 0.84;
}
.jfx-button-raised.anthropic-armed,
.launch-pane > .jfx-button.anthropic-armed {
-fx-background-color: #9A4A32;
-fx-border-color: #F5F3EC;
-fx-border-width: 2px;
-fx-opacity: 0.90;
}
+353 -99
View File
@@ -120,7 +120,7 @@
.radio-button-title-label {
-fx-font-size: 16px;
-fx-padding: 14.0 0.0 -20.0 0.0;
-fx-text-fill: rgba(0.0, 0.0, 0.0, 0.87);
-fx-text-fill: -monet-on-surface;
}
.announcement {
@@ -144,7 +144,8 @@
}
.rippler-container {
-jfx-feedback-fill: -monet-on-surface;
/* Anthropic chrome: no Material ripple / press ink */
-jfx-feedback-fill: transparent;
}
.rippler-container:press-feedback-enabled {
@@ -185,6 +186,18 @@
.advanced-list-item {
-jfx-selected-background-fill: -monet-secondary-container;
-fx-background-radius: 6px;
}
.advanced-list-item:hover > .rippler-container {
-fx-background-color: -anthropic-hover-wash;
-fx-background-radius: 6px;
}
.advanced-list-item:pressed > .rippler-container,
.advanced-list-item:armed > .rippler-container {
-fx-background-color: -anthropic-press-wash;
-fx-background-radius: 6px;
}
.advanced-list-item:selected > .rippler-container > .container > .title {
@@ -229,8 +242,9 @@
}
.class-title {
-fx-font-size: 12px;
-fx-padding: 8 16 8 16;
/* Title block of title:body ≈ 1:φ — md/sm padding under tool density */
-fx-font-size: 11px;
-fx-padding: 13 13 8 13;
}
.class-title Text {
@@ -245,7 +259,7 @@
}
.advanced-list-box-content {
-fx-padding: 12 0 0 0;
-fx-padding: 13 0 0 0;
-fx-spacing: 0;
}
@@ -258,8 +272,9 @@
}
.iconed-item .iconed-item-container {
-fx-padding: 10 16 10 16;
-fx-spacing: 10;
/* Body row under φ title:body — sm/md denser than landing */
-fx-padding: 8 13 8 13;
-fx-spacing: 13;
-fx-font-size: 14;
-fx-alignment: CENTER_LEFT;
}
@@ -274,14 +289,14 @@
}
.iconed-menu-item .iconed-item-container {
-fx-padding: 8 16 8 16;
-fx-spacing: 10;
-fx-padding: 8 13 8 13;
-fx-spacing: 13;
-fx-font-size: 12;
-fx-alignment: CENTER_LEFT;
}
.menu-container {
-fx-padding: 8 16 8 16;
-fx-padding: 8 13 8 13;
}
.popup-menu .scroll-bar .thumb {
@@ -392,7 +407,7 @@
}
.installer-item-wrapper:card {
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 10, 0.12, -1, 2);
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 8, 0.08, 0, 1);
}
.installer-item-wrapper .installer-item:list-item {
@@ -421,57 +436,82 @@
******************************************************************************/
.launch-pane {
-fx-max-height: 57px;
-fx-min-height: 57px;
-fx-max-width: 230px;
-fx-min-width: 230px;
/* φ/Fibonacci CTA: 55×233 pairs with title bar 44 and sidebar rail */
-fx-max-height: 55px;
-fx-min-height: 55px;
-fx-max-width: 233px;
-fx-min-width: 233px;
}
.launch-pane > .jfx-button {
-fx-translate-y: 1px;
-fx-max-height: 55px;
-fx-min-height: 55px;
-fx-background-color: -monet-primary-container;
-fx-max-height: 53px;
-fx-min-height: 53px;
-fx-background-color: -monet-primary;
-fx-opacity: 1; /* Override the opacity of disabled button */
-fx-cursor: hand;
-fx-scale-x: 1;
-fx-scale-y: 1;
}
.launch-pane > .jfx-button:hover {
-fx-background-color: -anthropic-primary-hover;
-fx-scale-x: 1.02;
-fx-scale-y: 1.02;
}
.launch-pane > .jfx-button:pressed,
.launch-pane > .jfx-button:armed {
-fx-background-color: #9A4A32;
-fx-border-color: #F5F3EC;
-fx-border-width: 2px;
-fx-opacity: 0.90;
-fx-scale-x: 0.96;
-fx-scale-y: 0.96;
}
.launch-pane > .jfx-button:focused {
-fx-border-color: -anthropic-focus-ring;
-fx-border-width: 1.5px;
}
.launch-pane > .jfx-button.launch-button {
-fx-max-width: 200px;
-fx-min-width: 200px;
-fx-border-width: 0 3px 0 0;
-fx-max-width: 201px;
-fx-min-width: 201px;
-fx-border-width: 0 1px 0 0;
-fx-border-color: linear-gradient(
to bottom,
transparent 0%,
transparent 20%,
-monet-on-surface-variant 20%,
-monet-on-surface-variant 80%,
transparent 80%,
to bottom,
transparent 0%,
transparent 22%,
-monet-on-primary 22%,
-monet-on-primary 78%,
transparent 78%,
transparent 100%
);
-fx-background-radius: 4px 0 0 4px;
-fx-background-radius: 8px 0 0 8px;
}
.launch-pane > .jfx-button.menu-button {
-fx-max-width: 27px;
-fx-min-width: 27px;
-fx-max-width: 32px;
-fx-min-width: 32px;
-fx-font-size: 15px;
-fx-background-radius: 0 4px 4px 0;
-fx-background-radius: 0 8px 8px 0;
}
.launch-pane > .jfx-button > StackPane > .jfx-rippler {
-jfx-rippler-fill: -monet-on-primary-container;
-jfx-rippler-disabled: true;
-jfx-rippler-fill: transparent;
-jfx-mask-type: CIRCLE;
-fx-padding: 0;
}
.launch-pane > .launch-button .label {
-fx-text-fill: -monet-on-primary-container;
-fx-font-size: 14px;
-fx-text-fill: -monet-on-primary;
-fx-font-size: 15px;
}
.launch-pane .svg {
-fx-fill: -monet-on-primary-container;
-fx-fill: -monet-on-primary;
}
/*******************************************************************************
@@ -498,7 +538,8 @@
}
.jfx-tab-pane .jfx-rippler {
-jfx-rippler-fill: white;
-jfx-rippler-disabled: true;
-jfx-rippler-fill: transparent;
}
.tab-label {
@@ -520,8 +561,8 @@
******************************************************************************/
.dialog-trigger {
-fx-background-color: WHITE;
-jfx-button-type: RAISED;
-fx-background-color: -monet-surface-container-highest;
-jfx-button-type: FLAT;
-fx-font-size: 14.0px;
}
@@ -657,7 +698,7 @@
******************************************************************************/
.icon {
-fx-fill: #FE774D;
-fx-fill: -monet-primary;
-fx-padding: 10.0;
-fx-cursor: hand;
}
@@ -671,17 +712,18 @@
.jfx-tool-bar {
-fx-font-size: 13.0;
-fx-pref-width: 100.0%;
-fx-min-height: 40px;
-fx-max-height: 40px;
-fx-pref-height: 40px;
-fx-min-height: 44px;
-fx-max-height: 44px;
-fx-pref-height: 44px;
}
.jfx-tool-bar .jfx-decorator-title {
-fx-font-weight: BOLD;
-fx-font-size: 16px;
}
.jfx-tool-bar.background {
-fx-background-color: -monet-primary-container;
-fx-background-color: -monet-surface-container;
}
/*.jfx-tool-bar HBox {*/
@@ -690,21 +732,21 @@
/*}*/
.jfx-tool-bar .jfx-decorator-button {
-fx-min-width: 40px;
-fx-max-width: 40px;
-fx-pref-width: 40px;
-fx-min-height: 40px;
-fx-max-height: 40px;
-fx-pref-height: 40px;
-fx-min-width: 44px;
-fx-max-width: 44px;
-fx-pref-width: 44px;
-fx-min-height: 44px;
-fx-max-height: 44px;
-fx-pref-height: 44px;
-fx-cursor: hand;
}
.jfx-tool-bar .jfx-decorator-button .svg {
-fx-fill: -monet-on-primary-container;
-fx-fill: -monet-on-surface;
}
.jfx-decorator-button .jfx-spinner .arc {
-fx-stroke: -monet-on-primary-container;
-fx-stroke: -monet-on-surface;
}
.jfx-tool-bar Label {
@@ -712,7 +754,7 @@
}
.jfx-tool-bar.gray-background Label {
-fx-text-fill: BLACK;
-fx-text-fill: -monet-on-surface;
}
.jfx-tool-bar Text {
@@ -732,15 +774,15 @@
}
.jfx-tool-bar-second {
-fx-pref-height: 42;
-fx-padding: 2 2 2 2;
-fx-background-color: -monet-primary-container;
-fx-pref-height: 48;
-fx-padding: 4 8 4 8;
-fx-background-color: -monet-surface-container;
-fx-alignment: CENTER-LEFT;
-fx-spacing: 8;
}
.jfx-tool-bar-second .label {
-fx-text-fill: -monet-on-primary-container;
-fx-text-fill: -monet-on-surface;
-fx-font-size: 16;
-fx-font-weight: bold;
}
@@ -758,12 +800,31 @@
-fx-min-height: -fx-toggle-icon4-size;
-fx-background-radius: 5px;
-fx-background-color: transparent;
-fx-opacity: 1;
-fx-scale-x: 1;
-fx-scale-y: 1;
-jfx-toggle-color: -monet-on-surface;
-jfx-untoggle-color: transparent;
}
.jfx-tool-bar-button:hover {
-fx-background-color: -anthropic-icon-hover-wash;
-fx-scale-x: 1.04;
-fx-scale-y: 1.04;
}
.jfx-tool-bar-button:pressed,
.jfx-tool-bar-button:armed {
-fx-background-color: rgba(217, 119, 87, 0.42);
-fx-border-color: #C96442;
-fx-border-width: 2px;
-fx-opacity: 0.84;
-fx-scale-x: 0.94;
-fx-scale-y: 0.94;
}
.jfx-tool-bar-button .icon {
-fx-fill: rgb(204.0, 204.0, 51.0);
-fx-fill: -monet-on-surface;
-fx-padding: 10.0;
}
@@ -774,13 +835,35 @@
.jfx-decorator-button {
-fx-max-width: 40px;
-fx-background-radius: 5px;
-fx-background-radius: 8px;
-fx-max-height: 40px;
-fx-background-color: transparent;
}
.jfx-decorator-button:hover {
-fx-background-color: -anthropic-hover-wash;
-fx-scale-x: 1.04;
-fx-scale-y: 1.04;
}
.jfx-decorator-button:pressed,
.jfx-decorator-button:armed {
-fx-background-color: rgba(217, 119, 87, 0.42);
-fx-border-color: #C96442;
-fx-border-width: 2px;
-fx-opacity: 0.84;
-fx-scale-x: 0.94;
-fx-scale-y: 0.94;
}
.jfx-decorator-button:focused {
-fx-border-color: -anthropic-focus-ring;
-fx-border-width: 1.5px;
-fx-border-radius: 8px;
}
.jfx-decorator-button .icon {
-fx-fill: rgb(204.0, 204.0, 51.0);
-fx-fill: -monet-on-surface;
-fx-padding: 0.0;
}
@@ -848,6 +931,11 @@
* *
*******************************************************************************/
.jfx-rippler {
-jfx-rippler-disabled: true;
-jfx-rippler-fill: transparent;
}
.jfx-rippler:hover {
-fx-cursor: hand;
}
@@ -861,20 +949,80 @@
.jfx-button {
-jfx-disable-visual-focus: true;
-fx-cursor: hand;
-fx-background-radius: 6px;
-fx-background-color: transparent;
-fx-scale-x: 1;
-fx-scale-y: 1;
}
.jfx-button .jfx-rippler {
-jfx-rippler-fill: -monet-on-surface;
-jfx-rippler-disabled: true;
-jfx-rippler-fill: transparent;
-jfx-mask-type: CIRCLE;
-fx-padding: 0.0;
}
/* Calm tactile feedback: tone + border + opacity (no Material ripple). */
.jfx-button:hover {
-fx-background-color: -anthropic-hover-wash;
-fx-border-color: rgba(201, 100, 66, 0.45);
-fx-border-width: 1px;
-fx-scale-x: 1.02;
-fx-scale-y: 1.02;
}
.jfx-button:pressed,
.jfx-button:armed {
-fx-background-color: rgba(217, 119, 87, 0.42);
-fx-border-color: #C96442;
-fx-border-width: 2px;
-fx-opacity: 0.84;
-fx-scale-x: 0.94;
-fx-scale-y: 0.94;
}
.jfx-button:focused {
-fx-border-color: -anthropic-focus-ring;
-fx-border-width: 2px;
-fx-border-radius: 6px;
-fx-border-insets: 0;
-fx-effect: dropshadow(gaussian, -anthropic-focus-glow, 0, 0, 0, 0);
}
.jfx-button:disabled,
.jfx-button:disabled:hover,
.jfx-button:disabled:pressed,
.jfx-button:disabled:armed {
-fx-scale-x: 1;
-fx-scale-y: 1;
-fx-opacity: 0.45;
-fx-background-color: transparent;
}
.jfx-button-raised {
-fx-background-color: -monet-primary;
-fx-background-radius: 6px;
}
.jfx-button-raised:hover {
-fx-background-color: -anthropic-primary-hover;
-fx-scale-x: 1.02;
-fx-scale-y: 1.02;
}
.jfx-button-raised:pressed,
.jfx-button-raised:armed {
-fx-background-color: #9A4A32;
-fx-border-color: #F5F3EC;
-fx-border-width: 2px;
-fx-opacity: 0.90;
-fx-scale-x: 0.96;
-fx-scale-y: 0.96;
}
.jfx-button-raised .jfx-rippler {
-jfx-rippler-fill: -monet-on-primary;
-jfx-rippler-disabled: true;
-jfx-rippler-fill: transparent;
-jfx-mask-type: CIRCLE;
-fx-padding: 0.0;
}
@@ -887,11 +1035,33 @@
.jfx-button-border {
-fx-background-color: transparent;
-fx-border-color: -monet-outline;
-fx-border-radius: 5px;
-fx-border-width: 0.2px;
-fx-border-radius: 6px;
-fx-border-width: 1px;
-fx-padding: 8px;
}
.jfx-button-border:hover {
-fx-background-color: -anthropic-hover-wash;
-fx-border-color: -monet-primary;
-fx-scale-x: 1.02;
-fx-scale-y: 1.02;
}
.jfx-button-border:pressed,
.jfx-button-border:armed {
-fx-background-color: rgba(217, 119, 87, 0.28);
-fx-border-color: #C96442;
-fx-border-width: 2px;
-fx-opacity: 0.88;
-fx-scale-x: 0.96;
-fx-scale-y: 0.96;
}
.jfx-button-border:focused {
-fx-border-color: -anthropic-focus-ring;
-fx-border-width: 1.5px;
}
.jfx-button-border, .jfx-button-border * {
-fx-text-fill: -monet-primary;
}
@@ -901,6 +1071,22 @@
-fx-background-radius: 50px;
}
.jfx-button-raised-round:hover {
-fx-background-color: -anthropic-primary-container-hover;
-fx-scale-x: 1.03;
-fx-scale-y: 1.03;
}
.jfx-button-raised-round:pressed,
.jfx-button-raised-round:armed {
-fx-background-color: #9A4A32;
-fx-border-color: #F5F3EC;
-fx-border-width: 2px;
-fx-opacity: 0.90;
-fx-scale-x: 0.96;
-fx-scale-y: 0.96;
}
.menu-up-down-button .label {
-fx-text-fill: -monet-on-surface;
}
@@ -993,7 +1179,7 @@
.list-cell:selected, .jfx-list-cell:selected,
.list-cell:hover, .jfx-list-cell:hover {
-fx-text-fill: black;
-fx-text-fill: -monet-on-surface;
}
.jfx-list-cell .jfx-rippler {
@@ -1040,31 +1226,31 @@
.options-list {
-fx-background-color: transparent;
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 5, 0.06, -0.5, 1);
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 4, 0.04, 0, 1);
}
.depth-0 {
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0), 0, 0, 0, 0);
-fx-effect: dropshadow(gaussian, transparent, 0, 0, 0, 0);
}
.depth-1 {
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 10, 0.12, -1, 2);
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 8, 0.08, 0, 1);
}
.depth-2 {
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 15, 0.16, 0, 4);
-fx-effect: dropshadow(gaussian, -anthropic-shadow-strong, 12, 0.10, 0, 2);
}
.depth-3 {
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 20, 0.19, 0, 6);
-fx-effect: dropshadow(gaussian, -anthropic-shadow-strong, 16, 0.12, 0, 3);
}
.depth-4 {
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 25, 0.25, 0, 8);
-fx-effect: dropshadow(gaussian, -anthropic-shadow-strong, 20, 0.14, 0, 4);
}
.depth-5 {
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 30, 0.3, 0, 10);
-fx-effect: dropshadow(gaussian, -anthropic-shadow-strong, 24, 0.16, 0, 5);
}
.padding-8 {
@@ -1073,30 +1259,30 @@
.card {
-fx-background-color: -monet-surface-container-low-transparent-80;
-fx-background-radius: 4;
-fx-padding: 8px;
-fx-background-radius: 8;
-fx-padding: 13px;
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 10, 0.12, -1, 2);
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 6, 0.06, 0, 1);
}
.card-no-padding {
-fx-background-color: -monet-surface-container-low-transparent-80;
-fx-background-radius: 4;
-fx-background-radius: 8;
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 10, 0.12, -1, 2);
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 6, 0.06, 0, 1);
}
.card-non-transparent {
-fx-background-color: -monet-surface-container-low;
-fx-background-radius: 4;
-fx-padding: 8px;
-fx-background-radius: 8;
-fx-padding: 13px;
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 5, 0.06, -0.5, 1);
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 4, 0.04, 0, 1);
}
.card-list {
-fx-padding: 10;
-fx-spacing: 10;
-fx-padding: 13;
-fx-spacing: 13;
}
.md-list-cell {
@@ -1132,7 +1318,8 @@
-fx-background-color: -monet-surface;
-fx-border-color: -monet-outline-variant;
-fx-border-width: 1 0 0 0;
-fx-padding: 10 16 10 16;
/* Body of title:body — sm vertical / md horizontal */
-fx-padding: 8 13 8 13;
-fx-font-size: 12;
}
@@ -1324,9 +1511,11 @@
.jfx-decorator-button {
-fx-pref-width: 50px;
-fx-background-radius: 50px;
-fx-background-radius: 8px;
-fx-pref-height: 50px;
-fx-background-color: transparent;
-fx-border-width: 0;
-fx-opacity: 1;
}
@@ -1340,17 +1529,38 @@
-fx-min-height: -fx-toggle-icon4-size;
-fx-background-radius: 50px;
-fx-background-color: transparent;
-fx-opacity: 1;
-fx-scale-x: 1;
-fx-scale-y: 1;
-jfx-toggle-color: -monet-primary;
-jfx-untoggle-color: transparent;
}
.toggle-icon4:hover {
-fx-background-color: -anthropic-icon-hover-wash;
-fx-scale-x: 1.06;
-fx-scale-y: 1.06;
}
.toggle-icon4:pressed,
.toggle-icon4:armed {
-fx-background-color: rgba(217, 119, 87, 0.42);
-fx-border-color: #C96442;
-fx-border-width: 2px;
-fx-border-radius: 50px;
-fx-opacity: 0.84;
-fx-scale-x: 0.90;
-fx-scale-y: 0.90;
}
.toggle-icon4 .icon {
-fx-fill: rgb(204.0, 204.0, 51.0);
-fx-fill: -monet-on-surface;
-fx-padding: 10.0;
}
.toggle-icon4 .jfx-rippler {
-jfx-rippler-fill: -monet-primary;
-jfx-rippler-disabled: true;
-jfx-rippler-fill: transparent;
-jfx-mask-type: CIRCLE;
}
@@ -1365,12 +1575,33 @@
-fx-padding: 0;
-fx-background-radius: 25px;
-fx-background-color: transparent;
-fx-opacity: 1;
-fx-scale-x: 1;
-fx-scale-y: 1;
-jfx-toggle-color: -monet-primary;
-jfx-untoggle-color: transparent;
}
.toggle-icon-tiny:hover {
-fx-background-color: -anthropic-icon-hover-wash;
-fx-opacity: 0.92;
-fx-scale-x: 1.08;
-fx-scale-y: 1.08;
}
.toggle-icon-tiny:pressed,
.toggle-icon-tiny:armed {
-fx-background-color: rgba(217, 119, 87, 0.42);
-fx-border-color: #C96442;
-fx-border-width: 2px;
-fx-border-radius: 50px;
-fx-opacity: 0.80;
-fx-scale-x: 0.86;
-fx-scale-y: 0.86;
}
.toggle-icon-tiny .icon {
-fx-fill: rgb(204.0, 204.0, 51.0);
-fx-fill: -monet-on-surface;
-fx-padding: 0;
}
@@ -1379,7 +1610,8 @@
}
.toggle-icon-tiny .jfx-rippler {
-jfx-rippler-fill: -monet-primary;
-jfx-rippler-disabled: true;
-jfx-rippler-fill: transparent;
-jfx-mask-type: CIRCLE;
}
@@ -1393,10 +1625,31 @@
-fx-min-height: -fx-announcement-close-button-size;
-fx-background-radius: 50px;
-fx-background-color: transparent;
-fx-opacity: 1;
-fx-scale-x: 1;
-fx-scale-y: 1;
}
.announcement-close-button:hover {
-fx-background-color: -anthropic-icon-hover-wash;
-fx-scale-x: 1.06;
-fx-scale-y: 1.06;
}
.announcement-close-button:pressed,
.announcement-close-button:armed {
-fx-background-color: rgba(217, 119, 87, 0.42);
-fx-border-color: #C96442;
-fx-border-width: 2px;
-fx-border-radius: 50px;
-fx-opacity: 0.84;
-fx-scale-x: 0.90;
-fx-scale-y: 0.90;
}
.announcement-close-button .jfx-rippler {
-jfx-rippler-fill: -monet-primary;
-jfx-rippler-disabled: true;
-jfx-rippler-fill: transparent;
-jfx-mask-type: CIRCLE;
}
@@ -1598,12 +1851,12 @@
}
.jfx-combo-box-warning {
-jfx-focus-color: #D34336;
-jfx-unfocus-color: #D34336;
-jfx-focus-color: -monet-error;
-jfx-unfocus-color: -monet-error;
}
.jfx-combo-box-warning .text {
-fx-fill: #D34336;
-fx-fill: -monet-error;
}
/*.combo-box-popup .list-view .jfx-list-cell .jfx-rippler {*/
@@ -1781,15 +2034,16 @@
*******************************************************************************/
.jfx-decorator {
-fx-decorator-color: -monet-primary-container;
-fx-decorator-color: -monet-surface-container;
}
.jfx-decorator-drawer {
-fx-background-color: rgba(244, 244, 244, 0.5);
-fx-background-color: -monet-surface-transparent-80;
}
.jfx-decorator-title {
-fx-font-size: 14;
-fx-font-size: 16;
-fx-font-weight: bold;
}
.debug-border {
@@ -1798,11 +2052,11 @@
}
.content-background {
-fx-background-color: -monet-surface-transparent-50;
-fx-background-color: -monet-surface-transparent-80;
}
.gray-background {
-fx-background-color: -monet-surface-transparent-50;
-fx-background-color: -monet-surface-container-low-transparent-80;
}
/*******************************************************************************
@@ -1812,12 +2066,12 @@
******************************************************************************/
.scroll-bar:vertical > .track-background, .scroll-bar:horizontal > .track-background {
-fx-background-color: #F1F1F1;
-fx-background-color: -anthropic-scrollbar-track;
-fx-background-insets: 0.0;
}
.scroll-bar:vertical .thumb, .scroll-bar:horizontal .thumb {
-fx-background-color: #BCBCBC;
-fx-background-color: -anthropic-scrollbar-thumb;
-fx-background-insets: 0.0;
-fx-background-radius: 1.0;
}
@@ -1831,7 +2085,7 @@
}
.scroll-bar > .increment-button > .increment-arrow, .scroll-bar > .decrement-button > .decrement-arrow {
-fx-background-color: rgb(150.0, 150.0, 150.0);
-fx-background-color: -monet-on-surface-variant;
}
.scroll-bar > .increment-button > .increment-arrow {
@@ -0,0 +1,96 @@
Copyright 2014-2025 Adobe (http://www.adobe.com/), with Reserved Font
Name 'Source'. Source is a trademark of Adobe in the United States
and/or other countries.
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -936,9 +936,9 @@ launcher.cache_directory.disabled=Disabled
launcher.cache_directory.invalid=Failed to create a cache directory, falling back to default.
launcher.crash=HMCL has encountered a fatal error! Please copy the following log and ask for help on GitHub or other Minecraft forums.
launcher.crash.java_internal_error=HMCL has encountered a fatal error because your Java is corrupted. Please uninstall your Java and download a suitable Java <a href="https://bell-sw.com/pages/downloads/#downloads">here</a>.
launcher.title=Hello Minecraft! Launcher v%s
launcher.title.modified=Hello Minecraft! Launcher v%s (Modified)
launcher.title.unofficial=Hello Minecraft! Launcher v%s (Unofficial Build)
launcher.title=TDLM v%s
launcher.title.modified=TDLM v%s (Modified)
launcher.title.unofficial=TDLM v%s (Unofficial Build)
launcher.update_java=Please update your Java version.
libraries.download=Downloading Libraries
@@ -756,9 +756,9 @@ launcher.cache_directory.disabled=禁 (恒用戲案夾之徑)
launcher.cache_directory.invalid=自訂之快取夾不能立。既復初。
launcher.crash=HMCL 有謬而弗能正。宜鈔下文而報謬于右下之鈕。
launcher.crash.java_internal_error=HMCL 不能行,以爪哇壞也。宜去是爪哇,而擊<a href="https://bell-sw.com/pages/downloads/#downloads">此</a>以置爪哇之適者。
launcher.title=Hello Minecraft! Launcher v%s
launcher.title.modified=Hello Minecraft! Launcher %s (已改)
launcher.title.unofficial=Hello Minecraft! Launcher %s (非官版)
launcher.title=TDLM v%s
launcher.title.modified=TDLM %s (已改)
launcher.title.unofficial=TDLM %s (非官版)
launcher.update_java=宜迭更爪哇。\n君可求助於 https://docs.hmcl.net/help.html。
libraries.download=引之所依
@@ -759,9 +759,9 @@ launcher.cache_directory.disabled=禁用 (总是使用游戏文件夹路径)
launcher.cache_directory.invalid=无法创建自定义的缓存文件夹。已经恢复到默认设置。
launcher.crash=HMCL 遇到了无法处理的错误。请复制下列内容并点击右下角的按钮反馈问题。
launcher.crash.java_internal_error=HMCL 由于当前 Java 损坏而无法继续运行。请卸载当前 Java,点击 <a href="https://bell-sw.com/pages/downloads/#downloads">此处</a> 安装合适的 Java 版本。
launcher.title=Hello Minecraft! Launcher %s
launcher.title.modified=Hello Minecraft! Launcher %s (已修改)
launcher.title.unofficial=Hello Minecraft! Launcher %s (非官方构建)
launcher.title=TDLM %s
launcher.title.modified=TDLM %s (已修改)
launcher.title.unofficial=TDLM %s (非官方构建)
launcher.update_java=请更新你的 Java。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
libraries.download=下载依赖库
@@ -753,9 +753,9 @@ launcher.cache_directory.disabled=停用
launcher.cache_directory.invalid=無法建立自訂的快取目錄。已還原至預設設定。
launcher.crash=HMCL 遇到了無法處理的錯誤。請複製下列內容並透過 GitHub 或其他 Minecraft 論壇回報問題。
launcher.crash.java_internal_error=HMCL 由於目前 Java 損壞而無法繼續執行。請移除目前 Java,點擊 <a href="https://bell-sw.com/pages/downloads/#downloads">此處</a> 安裝合適的 Java 版本。
launcher.title=Hello Minecraft! Launcher %s
launcher.title.modified=Hello Minecraft! Launcher %s (已修改)
launcher.title.unofficial=Hello Minecraft! Launcher %s (非官方建置)
launcher.title=TDLM %s
launcher.title.modified=TDLM %s (已修改)
launcher.title.unofficial=TDLM %s (非官方建置)
launcher.update_java=請更新你的 Java
libraries.download=下載依賴庫
@@ -15,6 +15,8 @@ public final class ThemeColorTest {
@Test
public void testOf() {
assertEquals(new ThemeColor("#AABBCC", Color.web("#AABBCC")), ThemeColor.of("#AABBCC"));
assertEquals(new ThemeColor("anthropic_orange", Color.web("#D97757")), ThemeColor.of("anthropic_orange"));
assertEquals(new ThemeColor("stone", Color.web("#3D453C")), ThemeColor.of("stone"));
assertEquals(new ThemeColor("blue", Color.web("#5C6BC0")), ThemeColor.of("blue"));
assertEquals(new ThemeColor("darker_blue", Color.web("#283593")), ThemeColor.of("darker_blue"));
assertEquals(new ThemeColor("green", Color.web("#43A047")), ThemeColor.of("green"));