From 5807b3c5e1a208f07350dc3964ab2e1f025ba6a9 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 14 May 2026 06:13:39 +0300 Subject: [PATCH 1/9] initializr: default to Java 17, drop Experimental label, bundle Claude Code skill Switch new initializr projects to Java 17 by default and rename the JavaVersion enum from JAVA_17_EXPERIMENTAL to JAVA_17 (label "Java 17" instead of "Java 17 (Experimental)"). Java 8 remains selectable as "Java 8 (Legacy)". Every generated project now ships a Codename One authoring skill under .claude/skills/codename-one/ so Claude Code (and any other agent that respects the skills convention) inherits CN1 conventions for CSS, UI, testing, screenshots, mobile adaptability, Swing comparison, and an HTML/CSS cheat sheet. The skill markdown lives in source form under common/src/main/resources/skill/** and is repackaged into skill.zip at build time because CN1's classloader rejects nested resource directories. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/initializr/common/pom.xml | 38 +++ .../com/codename1/initializr/Initializr.java | 2 +- .../initializr/model/GeneratorModel.java | 34 +- .../initializr/model/ProjectOptions.java | 8 +- .../common/src/main/resources/skill/SKILL.md | 263 ++++++++++++++++ .../skill/references/build-and-run.md | 95 ++++++ .../main/resources/skill/references/css.md | 185 +++++++++++ .../skill/references/html-css-cheatsheet.md | 294 ++++++++++++++++++ .../skill/references/mobile-adaptability.md | 227 ++++++++++++++ .../skill/references/swing-comparison.md | 172 ++++++++++ .../references/testing-and-screenshots.md | 195 ++++++++++++ .../skill/references/ui-components.md | 219 +++++++++++++ .../GeneratorModelIntegrationBuildTest.java | 2 +- .../model/GeneratorModelMatrixTest.java | 106 +++++-- 14 files changed, 1807 insertions(+), 33 deletions(-) create mode 100644 scripts/initializr/common/src/main/resources/skill/SKILL.md create mode 100644 scripts/initializr/common/src/main/resources/skill/references/build-and-run.md create mode 100644 scripts/initializr/common/src/main/resources/skill/references/css.md create mode 100644 scripts/initializr/common/src/main/resources/skill/references/html-css-cheatsheet.md create mode 100644 scripts/initializr/common/src/main/resources/skill/references/mobile-adaptability.md create mode 100644 scripts/initializr/common/src/main/resources/skill/references/swing-comparison.md create mode 100644 scripts/initializr/common/src/main/resources/skill/references/testing-and-screenshots.md create mode 100644 scripts/initializr/common/src/main/resources/skill/references/ui-components.md diff --git a/scripts/initializr/common/pom.xml b/scripts/initializr/common/pom.xml index 34167c9ded..829b8b4834 100644 --- a/scripts/initializr/common/pom.xml +++ b/scripts/initializr/common/pom.xml @@ -306,6 +306,22 @@ + + + + src/main/resources + + skill/** + + + org.apache.maven.plugins @@ -402,6 +418,28 @@ + + + package-claude-skill + process-resources + + run + + + + + + + + + diff --git a/scripts/initializr/common/src/main/java/com/codename1/initializr/Initializr.java b/scripts/initializr/common/src/main/java/com/codename1/initializr/Initializr.java index e017e450ca..42bd266fe0 100644 --- a/scripts/initializr/common/src/main/java/com/codename1/initializr/Initializr.java +++ b/scripts/initializr/common/src/main/java/com/codename1/initializr/Initializr.java @@ -53,7 +53,7 @@ public void runApp() { final boolean[] roundedButtons = new boolean[]{true}; final boolean[] includeLocalizationBundles = new boolean[]{false}; final ProjectOptions.PreviewLanguage[] previewLanguage = new ProjectOptions.PreviewLanguage[]{ProjectOptions.PreviewLanguage.ENGLISH}; - final ProjectOptions.JavaVersion[] javaVersion = new ProjectOptions.JavaVersion[]{ProjectOptions.JavaVersion.JAVA_8}; + final ProjectOptions.JavaVersion[] javaVersion = new ProjectOptions.JavaVersion[]{ProjectOptions.JavaVersion.JAVA_17}; final String[] customThemeCss = new String[]{""}; final String[] lastValidCustomThemeCss = new String[]{""}; final TextArea[] customCssEditorRef = new TextArea[1]; diff --git a/scripts/initializr/common/src/main/java/com/codename1/initializr/model/GeneratorModel.java b/scripts/initializr/common/src/main/java/com/codename1/initializr/model/GeneratorModel.java index 1e927881a1..c4976c59ac 100644 --- a/scripts/initializr/common/src/main/java/com/codename1/initializr/model/GeneratorModel.java +++ b/scripts/initializr/common/src/main/java/com/codename1/initializr/model/GeneratorModel.java @@ -42,6 +42,8 @@ public class GeneratorModel { ".DS_Store\n" + "Thumbs.db\n"; + private static final String CLAUDE_SKILL_TARGET_PREFIX = ".claude/skills/codename-one/"; + private final IDE ide; private final Template template; private final String appName; @@ -83,6 +85,7 @@ void writeProjectZip(OutputStream outputStream) throws IOException { copyZipEntriesToMap("/common.zip", mergedEntries, ZipEntryType.COMMON); copySingleTextEntryToMap(".gitignore", GENERATED_GITIGNORE, mergedEntries, ZipEntryType.COMMON); copySingleTextEntryToMap("README.md", buildReadmeMarkdown(), mergedEntries, ZipEntryType.COMMON); + addClaudeSkillEntries(mergedEntries); copySingleTextEntryToMap("common/pom.xml", readResourceToString(template.POM_XML), mergedEntries, ZipEntryType.TEMPLATE_POM); if (template.CN1LIB_ZIP != null) { copyZipEntriesToMap(template.CN1LIB_ZIP, mergedEntries, ZipEntryType.TEMPLATE_CN1LIB); @@ -102,6 +105,31 @@ void writeProjectZip(OutputStream outputStream) throws IOException { } + private void addClaudeSkillEntries(Map mergedEntries) throws IOException { + // Ship the Codename One authoring skill inside every generated project so that + // Claude Code (or any agent that respects .claude/skills) gets the framework's + // CSS/UI/testing conventions for free. The skill markdown lives under + // src/main/resources/skill/** in source form and is repackaged into skill.zip + // at build time (CN1 classloader rejects nested directories under resources). + // ASCII-only Markdown so no encoding surprises end up in the project tree. + try (ZipInputStream zis = new ZipInputStream(getResourceAsStream("/skill.zip"))) { + ZipEntry entry = zis.getNextEntry(); + while (entry != null) { + if (!entry.isDirectory()) { + byte[] sourceData = readToBytesNoClose(zis); + String relative = entry.getName(); + // Defensive normalization: ant may produce backslashes on Windows. + relative = StringUtil.replaceAll(relative, "\\", "/"); + String targetPath = CLAUDE_SKILL_TARGET_PREFIX + relative; + byte[] targetData = applyDataReplacements(targetPath, sourceData); + mergedEntries.put(targetPath, targetData); + } + zis.closeEntry(); + entry = zis.getNextEntry(); + } + } + } + private void addLocalizationEntries(Map mergedEntries) throws IOException { if (!isBareTemplate() || !options.includeLocalizationBundles) { return; @@ -233,14 +261,14 @@ private byte[] applyDataReplacements(String targetPath, byte[] sourceData) throw private String applyJavaVersionSettings(String content) { - if (options.javaVersion == ProjectOptions.JavaVersion.JAVA_17_EXPERIMENTAL) { + if (options.javaVersion == ProjectOptions.JavaVersion.JAVA_17) { content = replaceProperty(content, "codename1.arg.java.version", "17"); } return content; } private String applyJavaVersionToPom(String content) { - if (options.javaVersion != ProjectOptions.JavaVersion.JAVA_17_EXPERIMENTAL) { + if (options.javaVersion != ProjectOptions.JavaVersion.JAVA_17) { return content; } content = StringUtil.replaceAll(content, "1.8", "17"); @@ -249,7 +277,7 @@ private String applyJavaVersionToPom(String content) { } private String normalizeIntellijMiscXml(String content) { - String languageLevel = options.javaVersion == ProjectOptions.JavaVersion.JAVA_17_EXPERIMENTAL ? "JDK_17" : "JDK_1_8"; + String languageLevel = options.javaVersion == ProjectOptions.JavaVersion.JAVA_17 ? "JDK_17" : "JDK_1_8"; content = removeXmlAttribute(content, "project-jdk-name"); content = removeXmlAttribute(content, "project-jdk-type"); content = setXmlAttribute(content, "languageLevel", languageLevel); diff --git a/scripts/initializr/common/src/main/java/com/codename1/initializr/model/ProjectOptions.java b/scripts/initializr/common/src/main/java/com/codename1/initializr/model/ProjectOptions.java index fa5546003f..f139c2846d 100644 --- a/scripts/initializr/common/src/main/java/com/codename1/initializr/model/ProjectOptions.java +++ b/scripts/initializr/common/src/main/java/com/codename1/initializr/model/ProjectOptions.java @@ -44,8 +44,8 @@ public enum Accent { } public enum JavaVersion { - JAVA_8("Java 8"), - JAVA_17_EXPERIMENTAL("Java 17 (Experimental)"); + JAVA_17("Java 17"), + JAVA_8("Java 8 (Legacy)"); public final String label; @@ -81,11 +81,11 @@ public ProjectOptions(ThemeMode themeMode, Accent accent, boolean roundedButtons this.roundedButtons = roundedButtons; this.includeLocalizationBundles = includeLocalizationBundles; this.previewLanguage = previewLanguage == null ? PreviewLanguage.ENGLISH : previewLanguage; - this.javaVersion = javaVersion == null ? JavaVersion.JAVA_8 : javaVersion; + this.javaVersion = javaVersion == null ? JavaVersion.JAVA_17 : javaVersion; this.customThemeCss = customThemeCss; } public static ProjectOptions defaults() { - return new ProjectOptions(ThemeMode.LIGHT, Accent.DEFAULT, true, false, PreviewLanguage.ENGLISH, JavaVersion.JAVA_8); + return new ProjectOptions(ThemeMode.LIGHT, Accent.DEFAULT, true, false, PreviewLanguage.ENGLISH, JavaVersion.JAVA_17); } } diff --git a/scripts/initializr/common/src/main/resources/skill/SKILL.md b/scripts/initializr/common/src/main/resources/skill/SKILL.md new file mode 100644 index 0000000000..b2d072af9a --- /dev/null +++ b/scripts/initializr/common/src/main/resources/skill/SKILL.md @@ -0,0 +1,263 @@ +--- +name: codename-one +description: Build and modify Codename One cross-platform mobile apps (Java 17, Maven, ParparVM/Android/iOS/JavaScript). Use when the project contains a `common/codenameone_settings.properties`, depends on `com.codenameone:codenameone-core`, edits CSS files under `common/src/main/css/`, calls `cn1:run`, `cn1:test`, `cn1:build`, references `com.codename1.ui.*` / `com.codename1.testing.*`, or when the user asks to build a UI, write screen tests, generate screenshots, or compare to Swing/HTML. +metadata: + type: skill +--- + +# Codename One — App and UI Authoring Skill + +This skill teaches you how to write code for a Codename One (CN1) cross-platform mobile project. Codename One compiles Java/Kotlin bytecode to native iOS, Android, desktop and web. It looks like Java AWT/Swing, behaves like a mobile UI toolkit, and styles with a subset of CSS. + +**Use this skill when**: + +- A file you are editing imports `com.codename1.ui.*`, `com.codename1.io.*`, `com.codename1.testing.*`, or extends `com.codename1.system.Lifecycle`. +- You are editing a file in `common/src/main/css/` (CN1 CSS). +- You are running `cn1:run`, `cn1:debug`, `cn1:test`, or `cn1:build` Maven goals. +- The user asks for a UI screen, a screenshot test, a responsive layout, or wants to convert a Swing/HTML snippet to CN1. + +## How this skill is organized + +`SKILL.md` (this file) is the top-level cheat sheet. Deeper reference material lives under `references/` — pull the relevant file in **only when you need it**: + +- `references/build-and-run.md` — Maven goals, JDK matrix, `codenameone_settings.properties`, running the simulator, building for iOS/Android/Web. +- `references/ui-components.md` — Form, Toolbar, Container layouts (Border/Box/Flow/Grid/Layered), common components, navigation, dialogs. +- `references/css.md` — CSS capabilities and (important) **limitations**. Selectors, supported properties, 9-patch borders, theme constants. +- `references/swing-comparison.md` — Mapping Swing concepts and code to Codename One. Read this when porting Swing code. +- `references/html-css-cheatsheet.md` — Converting common HTML/CSS snippets to CN1 components + CSS. +- `references/testing-and-screenshots.md` — `AbstractTest`, `TestUtils`, `screenshotTest`, the `cn1:test` Maven goal, the screenshot tolerance algorithm. +- `references/mobile-adaptability.md` — Density-independent units (mm), `convertToPixels`, `LayeredLayout` for responsive design, `Display.isTablet()`, font scaling. + +When the user's task hits any one of those topics, **read the matching reference before generating code**. Do not paste large snippets without checking. + +## Project layout (multi-module Maven) + +A CN1 project generated by the initializr has these modules: + +``` +my-app/ +├── pom.xml # Aggregator. cn1.plugin.version + cn1.version pinned here. +├── common/ # Cross-platform Java/Kotlin source. THIS IS WHERE THE APP LIVES. +│ ├── pom.xml # 17 17 by default +│ ├── codenameone_settings.properties +│ └── src/main/ +│ ├── java//.java +│ ├── css/theme.css # CN1 CSS (NOT regular web CSS - see references/css.md) +│ ├── l10n/ # i18n bundles (NOT src/main/resources!) +│ └── guibuilder/ # Optional GUI builder XML +├── javase/ # Desktop simulator port +├── android/ # Android wrapper (built via build server or local Gradle) +├── ios/ # iOS wrapper (ParparVM) +├── javascript/ # TeaVM-based web port +└── win/ # UWP wrapper (optional) +``` + +**Only edit `common/`**. The platform modules are thin wrappers — touching them is almost always wrong unless you are intentionally writing a native interface. + +## Java version and language features + +Generated projects target **Java 17** by default (`17` / `17` in `common/pom.xml`, plus `codename1.arg.java.version=17` in `codenameone_settings.properties`). This lets you use: + +- `var` for local variable type inference +- Text blocks (`"""..."""`) +- Records +- Pattern matching for `instanceof` +- `switch` expressions +- Lambda expressions, method references, `Stream`s (Java 8 baseline always available) + +**Caveat — the build server still cross-compiles to bytecode that ParparVM/TeaVM can consume.** Avoid APIs not in the CN1 Java API subset: + +- No `java.nio.file.*` — use `com.codename1.io.FileSystemStorage` and `Storage`. +- No `java.util.concurrent.locks.*` beyond simple `synchronized` — use `Display.getInstance().callSerially(...)` to marshal work to the EDT. +- No `java.awt.*` / `javax.swing.*` — CN1 has its own UI stack (Form, Container, Component) that mirrors Swing concepts. See `references/swing-comparison.md`. +- No `java.lang.reflect.*` on production builds — works in the simulator only. +- No threads spawned with `new Thread(...).start()` for UI work — always go through `Display.callSerially` or `Display.startThread(...)`. + +If the user picks **Java 8 (Legacy)** in the initializr, the pom uses `1.8` and you must stick to Java 8 syntax. + +## The Event Dispatch Thread (EDT) + +CN1 has a single EDT, exactly like Swing. All UI mutation **must** happen on it. + +- Inside event listeners and lifecycle callbacks (`start`, `stop`, `init`) you are already on the EDT. +- From a background thread, hop back with `Display.getInstance().callSerially(() -> { ... })` (or `callSeriallyAndWait` if you need to block). +- Use `Display.getInstance().startThread(runnable, "name").start()` instead of `new Thread(...)` so cleanup happens correctly across platforms. + +`references/swing-comparison.md` contains a Swing→CN1 EDT idiom table. + +## The Lifecycle main class + +Every CN1 app extends `com.codename1.system.Lifecycle` (or `com.codename1.ui.util.Lifecycle` in older code). The four methods you may override: + +```java +public class MyAppName extends Lifecycle { + @Override + public void init(Object context) { + // Called once. Load global resources / theme. Already on EDT. + Resources global = Resources.openLayered("/theme"); + UIManager.getInstance().setThemeProps(global.getTheme("Theme")); + } + + @Override + public void runApp() { + // Build and show the first form. + Form f = new Form("Hello", new BorderLayout()); + f.add(BorderLayout.CENTER, new Label("Welcome")); + f.show(); + } + + @Override + public void stop() { /* App backgrounded */ } + + @Override + public void destroy() { /* App killed */ } +} +``` + +## Minimal "first screen" pattern + +```java +import static com.codename1.ui.CN.*; // Convenience statics: callSerially, etc. +import com.codename1.ui.*; +import com.codename1.ui.layouts.*; + +Form f = new Form("Profile", BoxLayout.y()); +f.getToolbar().addCommandToRightBar("Save", null, e -> save()); +f.add(new Label("Name")) + .add(new TextField()) + .add(new Button("Submit")); +f.show(); +``` + +`BoxLayout.y()` (vertical) and `BoxLayout.x()` (horizontal) are the most common layouts. Wrap a `Form` content pane in `BorderLayout` when you want a header/footer/center split. See `references/ui-components.md` for the full layout matrix. + +## CSS in Codename One + +CN1 ships with a CSS compiler that bakes `common/src/main/css/theme.css` into the binary theme resource (`theme.res`). It supports a deliberate **subset** of web CSS: + +```css +Form { + background-color: #0f172a; /* hex, rgb(), or named colors */ + padding: 2mm; /* mm is the recommended unit */ +} + +Button { + background-color: #1d4ed8; + color: #ffffff; + border: 1px solid #1d4ed8; + border-radius: 3mm; + padding: 2mm 4mm; +} + +Button.pressed { /* state pseudo-class baked as UIID */ + background-color: #1e3a8a; +} + +#Constants { + useLargerTextScaleBool: true; /* theme constants, not standard CSS */ +} +``` + +**Key differences from web CSS** (read `references/css.md` before authoring more): + +- Selectors target **UIIDs** (Codename One component style names), not arbitrary HTML elements. `Button`, `Form`, `Label`, `Toolbar`, `Title` are the most common. +- No descendant combinator, no `:hover`, no media queries. State variants are baked: `.pressed`, `.disabled`, `.selected`. +- Units: prefer `mm` (millimeters) over `px`. CN1 converts `mm` to device pixels via `Display.convertToPixels`. `1mm` ≈ 6-9 px depending on density. +- `border-radius` works but is rasterized at compile time — animating it at runtime requires programmatic styling. +- No `transform`, no `flex`, no `grid`. Use CN1 Java layouts for arrangement; CSS is only for *styling*. +- Bundled named colors are limited: `pink`, `orange`, `purple`, `yellow`, `gray`/`grey` are translated to hex by the initializr, anything else you must specify as hex. + +`references/html-css-cheatsheet.md` shows how to map "I want a flexbox row" / "I want a hero section" / "I want a card" to CN1 idioms. + +## Adaptability and responsive design + +Mobile screens vary wildly. CN1 gives you: + +- **Density-independent units**: `1mm` always renders ~1mm tall regardless of pixel density. Always size in `mm`, not `px`. +- `Display.getInstance().convertToPixels(2.5f)` — convert millimeters to current device pixels programmatically. +- `Display.getInstance().isTablet()`, `Display.getInstance().isPortrait()`, `Display.getInstance().getDisplayWidth/Height()` — branch on form factor. +- `LayeredLayout` with `LayeredLayoutConstraint` for precise responsive positioning (percent-based insets). +- `Toolbar` automatically reshapes to platform conventions (Android side menu / iOS tab bar). + +See `references/mobile-adaptability.md` for patterns: phone-vs-tablet master-detail, orientation listeners, dynamic font scaling. + +## Testing + +CN1 has its own test runner (`cn1:test`), not surefire. Tests extend `com.codename1.testing.AbstractTest`: + +```java +public class LoginFormTest extends AbstractTest { + @Override + public boolean shouldExecuteOnEDT() { return true; } + + @Override + public boolean runTest() throws Exception { + new MyAppName().runApp(); + TestUtils.waitForFormTitle("Login"); + TestUtils.setText("usernameField", "alice"); + TestUtils.clickButtonByLabel("Sign In"); + TestUtils.waitForFormTitle("Home"); + return screenshotTest("home-screen-baseline"); + } +} +``` + +Run with `mvn -pl common cn1:test` or `mvn test`. + +`screenshotTest(name)` captures the current form, compares against a stored baseline under `Storage`, and returns `true` if within tolerance. First run records the baseline. See `references/testing-and-screenshots.md` for the tolerance algorithm and how to validate UI you just wrote. + +> Important: a "screenshot matches baseline" only proves consistency, **not** correctness. If you just generated the baseline yourself, you have not validated the screen — visually inspect at least once before treating that baseline as ground truth. + +## Build and run commands + +From the project root: + +```bash +# Run in the desktop simulator (requires JDK 11–25 at runtime; build still uses JDK 17 source level) +mvn -pl common cn1:run + +# Run with breakpoints +mvn -pl common cn1:debug + +# Execute the CN1 test runner +mvn -pl common cn1:test + +# Cloud build for Android/iOS/JS (requires CN1 build server creds) +mvn -pl android package -Dcodename1.platform=android -Dcodename1.buildTarget=android-device +mvn -pl ios package -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-device +mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=javascript +``` + +See `references/build-and-run.md` for the complete goal list, the `codename1.arg.*` settings you can put in `codenameone_settings.properties`, and how to point the project at a local Codename One snapshot. + +## What NOT to do + +- Don't use `java.awt.Color` / `java.awt.Font` / `javax.swing.*` — CN1 has its own `Color` constants (just `int` ARGB), `Font.createTrueTypeFont`, and `Component` hierarchy. +- Don't add CSS that references web-only properties (`display`, `flex`, `position`, `transform`, `@media`) — the CN1 CSS compiler will silently ignore them or fail. +- Don't put localization bundles under `common/src/main/resources/`. The CN1 plugin scans `common/src/main/l10n/` (or `common/src/main/i18n/`); bundles placed anywhere else are NOT baked into `theme.res` and `Resources.getL10N("messages", lang)` returns `null` at runtime. +- Don't spin up `new Thread(...)` for UI work — use `Display.getInstance().callSerially(...)` or `Display.startThread(...)`. +- Don't mutate UI off the EDT. Symptoms: random repaint glitches, native crashes on iOS. +- Don't write screenshot tests where the baseline was just generated by the same code you are validating — that proves nothing. + +## Sanity-check loop before reporting "done" + +For any UI-altering change: + +1. Run `mvn -pl common cn1:run` in the simulator and click through the changed flow. +2. Inspect at least one screenshot (capture via the simulator menu → Save Screenshot, or generate via a test). +3. Resize the simulator window or toggle a different skin to confirm the layout doesn't break on a different form factor. +4. If you wrote a `screenshotTest`, delete the auto-generated baseline once if the screen has changed, then re-run twice — the second run should pass with `true`. + +If you cannot run the simulator (e.g. headless environment), **say so explicitly in the response** rather than claiming the UI works. + +## Reference quick-look index + +| If the user asks for... | Open this reference | +| --- | --- | +| "Add a screen with a list / form / dialog" | `references/ui-components.md` | +| "Make this look like X" / CSS tweaks | `references/css.md` | +| "Port this from Swing" / Swing idioms | `references/swing-comparison.md` | +| "I have HTML/CSS, convert it" | `references/html-css-cheatsheet.md` | +| "Write a test for this screen" / "Compare to a baseline" | `references/testing-and-screenshots.md` | +| "Make it look right on tablet/landscape" | `references/mobile-adaptability.md` | +| "How do I run/build/deploy" | `references/build-and-run.md` | diff --git a/scripts/initializr/common/src/main/resources/skill/references/build-and-run.md b/scripts/initializr/common/src/main/resources/skill/references/build-and-run.md new file mode 100644 index 0000000000..c581339391 --- /dev/null +++ b/scripts/initializr/common/src/main/resources/skill/references/build-and-run.md @@ -0,0 +1,95 @@ +# Build and Run Reference + +## Required JDKs + +| Task | JDK | +| --- | --- | +| Compiling the `common` module (Java 17 default) | JDK 17+ available as `JAVA_HOME` | +| Compiling the `common` module (legacy Java 8 setting) | JDK 8+, source/target=1.8 | +| Running `cn1:run` / `cn1:debug` (the simulator) | **JDK 11–25** (CN1 plugin aborts on older JDKs with a friendly error) | +| Cross-compiling for Android natively | JDK 17 set in `JAVA17_HOME` | +| Cross-compiling for iOS | macOS + Xcode + JDK 11–25 | + +The simulator forks a JVM that needs the `--add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED` argument on macOS / JDK 17+. The initializr already wires this into `.idea/workspace.xml` "Run in Simulator" run configurations. + +## Maven goal cheat sheet + +All goals come from the `codenameone-maven-plugin` and live under the `cn1:` prefix. Run them from the project root unless noted. + +```bash +# Desktop simulator (live, with hot CSS reload) +mvn -pl common cn1:run + +# Debug from your IDE +mvn -pl common cn1:debug + +# Run the CN1 test runner (NOT surefire) +mvn -pl common cn1:test + +# Run as a real desktop app (.jar wrapped with bundled JRE) +mvn -pl javase package -Pexecutable-jar + +# Cross-build for a target platform via the CN1 build server +mvn -pl android package -Dcodename1.platform=android -Dcodename1.buildTarget=android-device +mvn -pl ios package -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-device +mvn -pl ios package -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-source +mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=javascript + +# Compile CSS to theme.res without running the app +mvn -pl common compile # CSS is compiled in the process-resources phase +``` + +`-Dcodename1.buildTarget=ios-source` is special: it does not require build server credentials and instead generates an Xcode project locally that you can build and debug yourself in Xcode. + +## `codenameone_settings.properties` — the configuration source of truth + +This file lives at `common/codenameone_settings.properties`. The most useful keys: + +```properties +codename1.packageName=com.example.myapp +codename1.mainName=MyAppName +codename1.displayName=My App Name +codename1.arg.java.version=17 # 17 = use Java 17 build server; remove for Java 8 +codename1.arg.android.googlePlayVersion=true +codename1.arg.ios.includePush=false +codename1.kotlin=false +codename1.arg.android.xPermissions=... +codename1.arg.ios.deployment_target=11.0 +codename1.arg.ios.teamId=ABCDEF1234 # used by the locally-generated Xcode project +codename1.arg.build.compile=true # ahead-of-time compile (recommended for iOS) +``` + +Anything prefixed `codename1.arg..` is forwarded to the build server's build args. The full list is in the Codename One developer guide. + +## Layout invariants + +- `common/src/main/css/theme.css` — **the** CSS file. Compiled to `common/target/classes/theme.res`. +- `common/src/main/l10n/messages*.properties` — i18n bundles. **MUST** live here (not in `src/main/resources`), or the CSS compiler will not bake them in. +- `common/src/main/java//.java` — entry point. Class name must match `codename1.mainName`. +- `common/src/main/resources/` — arbitrary runtime resources. Loaded via `Display.getInstance().getResourceAsStream("/file.json")`. +- `common/src/main/guibuilder/` — optional GUI builder XML (auto-generates Java). + +## Pointing the project at a local Codename One snapshot + +When iterating on the CN1 framework itself, point the generated project at a local SNAPSHOT instead of Maven Central: + +```xml + + 8.0-SNAPSHOT + 8.0-SNAPSHOT + +``` + +Then build the framework from `/path/to/CodenameOne/maven` with `mvn install -Plocal-dev-javase`. The generated project will resolve the SNAPSHOT from `~/.m2/repository`. + +## Common build errors and fixes + +- **`Unrecognized option: --add-exports=...` when running the simulator** → the runtime JDK is < 11. Switch to JDK 11–25. +- **`Resources.getGlobalResources().getL10N(...)` returns null at runtime** → your bundles are under `common/src/main/resources/` instead of `common/src/main/l10n/`. Move them. +- **`Cannot find symbol class XxxView`** after using the GUI builder → run `mvn -pl common generate-sources` to regenerate. +- **`OutOfMemoryError` running `cn1:test`** → bump `-Xmx2g` in the surefire/cn1 plugin config in `common/pom.xml`. +- **Build server returns "build args invalid"** → check `codename1.arg.*` keys spelled exactly as documented in the Codename One Developer Guide. + +## The build client + +The CN1 Maven plugin ships a build client jar that is copied to `~/.codenameone/CodeNameOneBuildClient.jar` on first use. If you ever see "build client missing", copy it manually from `maven/CodeNameOneBuildClient.jar` of the framework checkout. diff --git a/scripts/initializr/common/src/main/resources/skill/references/css.md b/scripts/initializr/common/src/main/resources/skill/references/css.md new file mode 100644 index 0000000000..fa7a740dae --- /dev/null +++ b/scripts/initializr/common/src/main/resources/skill/references/css.md @@ -0,0 +1,185 @@ +# Codename One CSS Reference + +`common/src/main/css/theme.css` is the entry point. The CN1 plugin's CSS compiler (`compile-css` goal, runs in `process-resources`) parses it and bakes the result into `common/target/classes/theme.res`. The runtime then loads styles by UIID from that binary resource — no CSS exists at runtime. + +This is **not** web CSS. It is a deliberate subset designed for native rendering on mobile. Treat any unfamiliar property as "probably unsupported" until you check. + +## Selector model + +The only selector form is: + +``` +[.] [, [.] ...] { ... } +``` + +- `UIID` matches `component.getUIID()` — e.g. `Button`, `Form`, `Label`, `Title`, `Toolbar`, `MyCustomCard`. +- `.` matches one of the built-in style variants: `.pressed`, `.disabled`, `.selected`. +- Multiple UIIDs may be grouped with commas. +- **No descendant combinators**, **no attribute selectors**, **no `*`**, **no `:hover`** (mobile has no hover), **no `@media` queries**. + +```css +/* Valid */ +Button { ... } +Button, Label { ... } +Button.pressed { ... } +MyCard.selected { ... } + +/* INVALID — these silently fail or break the compile */ +Form Button { ... } /* no descendant combinator */ +Button:hover { ... } /* no :hover */ +@media (max-width: 600px) { ... } /* no media queries */ +.btn { ... } /* class selectors don't exist; use UIIDs */ +``` + +Need per-screen styling? Set a different UIID on the parent and on the children, then write one rule per UIID. + +## Supported properties + +| Category | Properties | +| --- | --- | +| Background | `background-color`, `background-image`, `background-repeat`, `background-position` | +| Border | `border`, `border-color`, `border-style`, `border-width`, `border-radius`, `border-image` (9-patch) | +| Spacing | `margin`, `margin-top/right/bottom/left`, `padding`, `padding-top/right/bottom/left` | +| Text | `color`, `font-family`, `font-size`, `font-weight`, `font-style`, `text-decoration`, `text-align` (with `align` fallback) | +| Layout (per-component) | `cn1-text-align`, `cn1-include-native-bool` | +| Theme constants | Any key inside `#Constants { ... }` (custom theme constants) | + +Anything not on this list — `display`, `position`, `float`, `flex`, `grid`, `transform`, `box-shadow`, `opacity` (use `cn1-opacity` selectively), `overflow`, `z-index` — is **not honored**. Use Java layouts for arrangement. + +## Units + +`mm`, `px`, `%`, named integers. **Prefer `mm`** because pixel density varies wildly across devices: + +```css +Button { + padding: 2mm 4mm; /* GOOD — same physical size on any screen */ + border-radius: 3mm; + margin: 1mm; +} +Button { + padding: 12px; /* BAD — tiny on 3x density, huge on 1x */ +} +``` + +CN1 internally converts `mm` to pixels via `Display.convertToPixels(float)`. + +## Colors + +```css +Button { + background-color: #1d4ed8; /* hex, the most reliable form */ + color: rgb(255, 255, 255); /* rgb() works */ + border: 1px solid #475569; +} +``` + +**Named colors**: only a few are recognized by the initializr's pre-processor (`pink`, `orange`, `purple`, `yellow`, `gray`, `grey`). Beyond that, **use hex**. CSS like `color: red;` may not compile. + +Alpha: use `rgba(r, g, b, a)` where `a` is 0–255 in some compiler versions and 0.0–1.0 in others — test it. The safe alternative is to leave the color opaque and animate transparency programmatically via `setAlpha(int 0..255)` on the component style. + +## Borders and 9-patch + +Standard CSS borders: + +```css +Card { + border: 1px solid #cbd5e1; + border-radius: 3mm; +} +``` + +For pixel-perfect non-rectangular borders (think iOS bubble), use 9-patch via `border-image`: + +```css +ChatBubble { + border-image: url('/bubble.9.png') 12 12 12 12; +} +``` + +The image must follow the Android 9-patch convention (1-pixel border indicating stretchable regions). Drop it in `common/src/main/css/images/` and reference it relative to the CSS root. + +## Theme constants (`#Constants`) + +A special "selector" used to set framework-wide booleans/numbers/strings that CN1 reads at runtime: + +```css +#Constants { + useLargerTextScaleBool: true; /* honor system "Large Text" accessibility */ + rtlBool: false; /* right-to-left layout */ + drawMapPointerBool: true; + pureTouchBool: true; + centeredPopupBool: true; + statusBarHidden: false; +} +``` + +The naming convention `xxxBool` / `xxxInt` / `xxxImage` is required — the CSS compiler reads the suffix as the value type. + +`useLargerTextScaleBool: true` is added by default for barebones templates because mobile users with accessibility scaling enabled otherwise get unreadably small text. + +## Font handling + +```css +Title { + font-family: "native:MainBold"; /* CN1 named font alias */ + font-size: 4mm; +} +Body { + font-family: "Roboto.ttf"; /* TTF in common/src/main/css/fonts/ */ + font-size: 3mm; +} +``` + +`native:Main` / `native:MainBold` / `native:Italic` etc. map to the platform default font. Bundle TTFs under `common/src/main/css/fonts/` to use custom faces — the compiler embeds them in `theme.res`. + +## Live preview while editing + +`mvn -pl common cn1:run` runs the simulator with a CSS file watcher: save `theme.css` and the simulator hot-reloads styles without restarting. This is the fastest iteration loop for visual work. + +## Compiling CSS programmatically (for tools/tests) + +```java +import com.codename1.ui.css.CSSThemeCompiler; +import com.codename1.ui.util.MutableResource; + +MutableResource res = new MutableResource(); +new CSSThemeCompiler().compile(cssString, res, "MyTheme"); +boolean ok = res.getTheme("MyTheme") != null; +``` + +Use this to validate generated CSS in tests. + +## Common pitfalls + +| Symptom | Likely cause | +| --- | --- | +| Style doesn't apply | UIID typo. Default UIIDs are `Button`, `Label`, etc. — case-sensitive. | +| Colors look wrong on Android only | Channel order: a few older APIs expect ARGB ints, not 0xRGB. Stick to hex strings in CSS. | +| Padding ignored | You're setting padding on the Form itself; set it on its ContentPane (UIID `Container`) or wrap content in your own UIID. | +| Border radius animates jaggy | Border radius is rasterized at compile. To animate, draw via `Graphics.fillRoundRect` programmatically. | +| `text-align` does nothing | Add the `align` fallback (the initializr appends one automatically for `text-align`). | +| New CSS only takes effect after restart | The build cache may be stale — `mvn -pl common clean compile`. | + +## Initializr's CSS overlays + +Projects generated with theme options selected (Light/Dark/Accent/Rounded) get an auto-appended block at the bottom of `theme.css` between markers: + +```css +/* Initializr Theme Overrides */ +Form { background-color: #0f172a; color: #e2e8f0; } +... + +/* Initializr Appended Custom CSS */ + +``` + +Edit the overrides freely; the markers are informational, not load-bearing. + +## Reaching beyond the compiler + +If you need a CSS feature that doesn't exist, you have two escapes: + +1. **Programmatic styling**: `comp.getAllStyles().setBgColor(0xff1d4ed8 & 0xffffff)` etc. Verbose but supports anything. +2. **Custom painters**: implement `Painter` and `comp.getAllStyles().setBgPainter(painter)`. Lets you draw arbitrary shapes/gradients. + +Programmatic styling is the right hammer for animations, dynamic theming, and anything per-instance. diff --git a/scripts/initializr/common/src/main/resources/skill/references/html-css-cheatsheet.md b/scripts/initializr/common/src/main/resources/skill/references/html-css-cheatsheet.md new file mode 100644 index 0000000000..04ec6dcb0f --- /dev/null +++ b/scripts/initializr/common/src/main/resources/skill/references/html-css-cheatsheet.md @@ -0,0 +1,294 @@ +# HTML/CSS → Codename One Cheat Sheet + +Designers and web developers think in HTML/CSS idioms — flexbox rows, hero sections, sticky headers, media queries. None of those translate literally, but every one of them has a CN1 idiom. Use this file as a fast lookup. + +## HTML elements → CN1 components + +| HTML | CN1 component | Notes | +| --- | --- | --- | +| `
` | `Container` | Wraps children; pick a layout. | +| `` | `Label` | Single-line. | +| `

` / multi-line text | `SpanLabel` | Wraps automatically. | +| `

`–`

` | `Label` with a UIID like `H1`, `H2`, styled in CSS | | +| `