Draft
Conversation
IDE UI refactoring foundation: - Add FlatLaf 3.7.1, DJ-Raven modal-dialog 2.6.1, swing-pack 1.0.1, MigLayout 11.4.3 to IDE/pom.xml - Initialize FlatDarkLaf before any Swing component creation (Sikulix.java) - Remove UnifiedToolbarButtonUI from ButtonOnToolbar (use FlatLaf native) - Remove EmphasizedLabelUI from SikuliIDEStatusBar (use FlatLaf native) - Remove apple.laf.useScreenMenuBar (FlatLaf handles macOS natively) - mac_widgets dependency kept in pom for transition, removal in Phase 3 https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Console text colors were hardcoded for light background (black, dark olive, dark blue). Updated to light variants visible on FlatDarkLaf background: - normal: #BBBBBB (light gray) - debug: #C0A000 (amber) - info: #6CB6FF (light blue) - log: #3DDBA4 (mint green) - error: #FF6B6B (light red) https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Phase 1a — Accelerator migration:
- migrateAcceleratorsToRootPane() recursively extracts all KeyStrokes
from JMenuItems and registers them on rootPane InputMap/ActionMap
- All keyboard shortcuts (Ctrl+S, Ctrl+R, Ctrl+F, etc.) survive
the JMenuBar being hidden
Phase 1b — Sidebar replaces JMenuBar + JToolBar:
- New package org.sikuli.ide.ui with OculixSidebar, SidebarItem,
SidebarSubmenu
- OculixSidebar: vertical MigLayout panel with action buttons
(Run, Run Slow, Capture, Record) and navigation popups
(File, Edit, Tools, Help)
- Submenus auto-built from existing JMenu items (buildSubmenuFrom)
- JMenuBar hidden via setVisible(false), JToolBar hidden
- Double-click sidebar toggles collapsed/expanded mode
- Theme toggle (Dark/Light) in sidebar footer
- i18n keys added for en_US and fr
Phase 1c — FlatLaf native closeable tabs:
- putClientProperty("JTabbedPane.tabClosable", true) on tabs
- FlatLaf tabCloseCallback wired to existing close logic
- CloseableTabbedPane kept for backward compatibility
https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
- Sidebar width 180px → 200px to prevent text clipping - Use short universal labels: Run, Run Slow, Capture, Record instead of long i18n strings that overflow - Tooltips still show full localized text on hover https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
…anup Sidebar restructured with logical groups: - Run section: Run, Run Slow (top, most accessible) - SCRIPTS section: File ▸, Edit ▸ (popup submenus) - TOOLS section: Capture, Record, Tools ▸ - HELP section: Help ▸ - Footer: theme toggle + version Dead menu items removed: - Export as jar / Export as runnable jar (obsolete) - Pack Jar with Jython (was duplicated, obsolete) - Android support (fully commented out, never worked) - Open Special Files (redundant with OS file manager) - Recent files: enabled (was behind experimental flag) Bug fixes: - Find: str == "!" → "!".equals(str) (case-insensitive search was broken) - Check Update: rewritten to use GitHub releases API (was using broken #https:// URL to raiman.github.io) Welcome Tab: - Displayed when no scripts are open - Shows OculiX branding, New/Open quick actions, system status - Disappears when a script is opened or created - Non-closeable tab StatusBar modernized with MigLayout: - Shows: OculiX version │ Java version │ PaddleOCR status │ R: C: - PaddleOCR indicator: green (connected), red (disconnected), gray (unavailable) - Uses reflection to check PaddleOCR without hard dependency About/Splash rebranded: - "OculiX IDE — Powered by SikuliX" - Updated copyright to 2026, links to github.com/oculix-org https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
- Welcome tab now shows when last tab is closed (not just on startup) - Fix tab close: remove duplicate close logic (FlatLaf callback only, removed CloseableTabbedPane listener to prevent double-remove) - Welcome tab protected from close button - Fix buildSubmenuFrom: skip duplicate separators, skip empty nested menus (like empty Recent), avoid leading/trailing separators - Add modern fonts: Segoe UI for UI, Cascadia Code for monospace (FlatLaf falls back to system defaults if not available) https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Sidebar now has uniform submenu-only navigation: - Fichier ▸ (File operations) - Edition ▸ (Edit, Find, Indent) - Exécuter ▸ (Run, Run Slow, Run Selection with icons) - Outils ▸ (Capture, Record, Extensions with icons) - Aide ▸ (Help links, Check Update) Removed direct action buttons (Run, Capture, Record) from sidebar. All actions are now in submenus for consistent UX. Run submenu built manually with proper icons and shortcuts. Tools submenu includes Capture + Record + existing tool items. Also fixes: - Welcome tab shows when last tab is closed - Tab close conflict resolved (FlatLaf callback only) - Submenu separator cleanup (no duplicates/leading/trailing) https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
EditorImageButton.MAXHEIGHT was hardcoded to 20px, making captured images appear minuscule in the editor. Now uses the same preference value as EditorPatternButton (default 50px from PreferencesUser). https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
- Wrap restoreSession() in try/catch to prevent IDE freeze on error - Show welcome tab as fallback if restore fails or no tabs are open - Fix EditorImageButton: PreferencesUser.get() can return null during early class loading, causing NPE. Added safe fallback to 50px. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
- Fix FlatLaf border style: @Separator → $Separator.foreground - Fix FATAL ERROR on switchContext: return silently when contexts is empty (Welcome Tab selected, no script contexts) - Fix showAfterStart: guard getActiveContext() when no contexts - Logo 80px → 128px (was too small) https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Script-dependent menu items (Run, Run Slow, Run Selection, Capture, Record) are now disabled when no script context exists (e.g. Welcome Tab is showing). They re-enable automatically when a script is opened or created. Prevents crashes from operating on non-existent contexts. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
- Sidebar: set preferredSize and minimumSize to 180px to prevent compression by BorderLayout - WelcomeTab: replace FlatLaf style borders with standard Swing LineBorder + EmptyBorder (eliminates @Separator / $Separator variable errors across all FlatLaf versions) https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Sidebar now displays contextual information like a dashboard: - PROJECT panel: script name, path (truncated), image count from .sikuli bundle directory. Updates on tab switch. - STATUS panel: PaddleOCR (green/red), Tesseract (always green), Java version. Uses reflection for PaddleOCR check. - LAST RUN panel: pass/fail with exit code, duration, timestamp. Updates automatically after each script execution. - Scrollable main panel for small screens. - Project info clears when Welcome Tab is shown. - Sidebar width increased to 200px for info panels. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
- hideWelcomeTab() called in createEmptyScriptContext(), createEmptyTextContext(), and createFileContext() — Welcome Tab now properly disappears when any script is opened from any source (menu, recent, drag-drop, etc.) - Fix SikuliX logo aspect ratio: 959x161 original was forced to 128x128 (squished). Now scales proportionally to 280px width. - getActiveContext() returns null instead of crashing when Welcome Tab is selected (no script context exists) - getCurrentCodePane() null-safe https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Open Script action was hiding Welcome Tab before the file chooser dialog. If user cancels, the tab was already gone. Now the tab only hides inside createFileContext() after a file is confirmed. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
saveSession() created a dummy PaneContext via toArray(new PaneContext[]{new PaneContext()})
when contexts was empty. The uninitialized PaneContext had null fields, causing NPE on
isTemp(). Now returns early if contexts is empty, and uses toArray(new PaneContext[0])
which doesn't insert dummy elements.
https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
restoreSession() called getContextAt(0).closeSilent() to remove the initial empty script. But with Welcome Tab, no empty script is created at startup, so contexts is empty → IndexOutOfBoundsException. Now only closes context(0) if there are more contexts than loaded files. Also null-guard showAgain() for getActiveContext(). https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Major layout change (Eclipse/IntelliJ-style):
- Console (Message area) moved from right to bottom via VERTICAL_SPLIT
with 75% weight to editor. One-touch expandable divider.
- Script file explorer added to left of editor via HORIZONTAL_SPLIT
Shows .sikuli bundle contents: .py files first, then Images folder
with count. Double-click .png opens in system viewer.
- Empty state: "Open or create a script to browse files"
- Explorer updates on tab switch, clears on Welcome Tab
Layout structure:
Sidebar | Explorer | Editor tabs
| | ...code...
|--------- Console ------
WelcomeTab fixes:
- Background now uses Panel.background (respects dark theme)
- Removed redundant Status panel (already in sidebar)
- SikuliX logo preserved at correct aspect ratio
https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Logo rendered as large Serif Bold Italic "OculiX" (28pt) with spaced "I D E" subtitle in a bordered rectangle that fills the sidebar width. Uses Label.foreground for automatic dark/light theme support. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
- MigLayout align center on both labels - OculiX in Serif Bold Italic 30pt, orange #E06030 (SikuliX brand) - IDE subtitle in SansSerif Bold 13pt, centered - Color works on both dark and light themes https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
- updateScriptDependentItems() now called in showContext() so Run/Capture/Record are re-enabled whenever a script tab is shown (not just when Welcome Tab hides) - ScriptExplorer: fix potential component stacking by always removing both emptyLabel and scrollPane before re-adding https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Explicitly update explorer, sidebar and script-dependent items after createEmptyScriptContext(). The ChangeListener on tabs may not fire reliably during tab creation, so we force the update after context.create() completes. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Complete rewrite of ScriptExplorer from file tree to card-based workspace view: - Each open script is a visual card with name, image count, and save status (✓ saved, * unsaved) - Active card highlighted with selection color - Click a card → activates the corresponding editor tab - Cards update on tab switch, script creation, save, and close - Empty state: "No scripts open" with hint to create one - Hover effect on cards for better interactivity New refreshWorkspace() method in SikulixIDE rebuilds cards from the contexts list. Called on: showContext, createEmptyScriptContext, showWelcomeTab, hideWelcomeTab. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Workspace system:
- File > New > Script / Workspace (submenu)
- File > Open > Script / Workspace (submenu)
- New Workspace dialog: name, description, author fields
→ file chooser → creates workspace.json in selected directory
- Open Workspace: selects a directory, loads workspace.json,
auto-opens all .sikuli scripts found in the directory
- Workspace name displayed in explorer header
Script cards:
- Right-click context menu with "Rename" option
- Rename shows input dialog, updates tab title
- Cards auto-refresh after capture (image count updates)
- refreshWorkspace() called in showAgain() for post-capture sync
Welcome Tab updated:
- New Workspace / Open Workspace links added
- Separator between script and workspace actions
workspace.json format:
{
"name": "My QA Project",
"description": "...",
"author": "...",
"created": "2026-04-05",
"scripts": []
}
https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Replace confusing nested submenus (New > Script/Workspace) with flat menu items grouped by section headers: - ── Nouveau ── Script / Workspace - ── Ouvrir ── Script / Workspace - Enregistrer / Enregistrer sous / Exporter - Fermer l'onglet - Préférences / Quitter Section headers are disabled bold JMenuItems acting as visual separators. Clearer UX than nested submenus. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
File chooser (Open Script, Open Workspace) now opens on the workspace directory or the parent of the active script, instead of the Java default directory. Uses LAST_OPEN_DIR preference which SikulixFileChooser already reads. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
buildSubmenuFrom() was copying disabled section labels as normal menu items, losing the bold font and disabled state. Now detects disabled items and reproduces them as section headers in the popup. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
buildSubmenuFrom() was losing disabled/bold section labels. Replaced with buildFileSubmenu() that constructs the popup manually with proper section headers: ── Nouveau ── Script / Workspace ── Ouvrir ── Script / Workspace Enregistrer / Enregistrer sous / Exporter Fermer l'onglet / Préférences / Quitter Actions wired directly (no more reflection via FileAction). Save/SaveAs/Export/Close greyed when no script open. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
- After Save As, refreshWorkspace() and sidebar.updateProjectInfo() are called so the card name and save status update immediately - selectFileForSave() now sets LAST_OPEN_DIR to the workspace directory (or script parent), so the file chooser opens in the right location instead of the JAR directory https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
loadWorkspace() was passing .sikuli directories to createFileContext() without checking if they contain a .py script file. Empty bundles or bundles with mismatched filenames caused NPE in setFile(). Now checks for baseName.py existence before attempting to load. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Two fixes: 1. loadWorkspace: search for ANY .py file in .sikuli bundle instead of guessing baseName.py (handles renamed/mismatched scripts). Added refreshWorkspace() after loading all scripts. 2. PaneContext.setFile: when Runner.getScriptFile() returns null (no matching .py in bundle), the code tried _file.createNewFile() on null reference. Now creates a new .py file with the correct name (baseName + defaultExtension) instead of NPE. Also return false instead of fatal() to allow graceful skip. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Reordered SikulixFileChooser.saveAs() filter list to put "as folder.sikuli" first (default selection). Previously "as file" was first, requiring users to manually switch the filter to get the .sikuli extension auto-appended. Also added .sikuli filter option for non-bundle saves. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
When saving as folder.sikuli, the script file inside the bundle was created as "name" without any extension. Now uses the default runner extension (.py for Jython) to create "name.py" properly. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
In the else branch of setFile (new file creation), the error message referenced the class field 'file' instead of local '_file', which could be null on first script. Also replaced fatal() with log() + return false to allow graceful recovery instead of IDE termination. https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
Bug #1: Tab close callback used tab index as context index without welcome tab offset. Tab 1 with welcome = context 0, not 1. Bug #2: getContextAt() had no bounds check — now returns null for invalid indices instead of ArrayIndexOutOfBoundsException. Bug #3: getPaneAtIndex() called getContextAt().getPane() without null check — now null-safe. Bug #4: setActiveContext() had no bounds check — now returns null for invalid pos. Bug #5: restoreSession() called getContextAt(0).closeSilent() without null check — now guarded. Also fixed switchContext() to account for welcome tab offset. All tab-to-context index translations now use: contextIndex = welcomeShowing ? tabIndex - 1 : tabIndex https://claude.ai/code/session_01NfFAjaXPNsBirBfQdMAaHi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Refactoring of the IDE layer: modern look & feel, Eclipse-style layout,
workspace-aware script management, and stabilization of the IDE startup/save
lifecycle. 35 commits, 18 files, +1744 / -165.
Opened as draft — not ready to merge. Looking for feedback before I
clean up the history and squash.
Motivation
The legacy IDE had several pain points I wanted to address in one coherent pass:
mac_widgetsdependency dragging dead weight we don't useWhat's in this branch
Phase 0 — Dependencies & theming (16754f3, 21b00ea)
mac_widgetslegacy dependencyPhase 1 — Sidebar & navigation (a7eee34 → 4c082fd)
OculixSidebarcomponent (345 LOC) with grouped items and submenusSidebarItem/SidebarSubmenuhelper componentsPhase 2 — Welcome Tab & status bar (82e2a21 → 2139355)
WelcomeTab(129 LOC) shown on startup, hidden when a script opensSikuliIDEStatusBar(+72 LOC)Phase 3 — Eclipse-style layout & workspace (0a0276a → dc730fc)
ScriptExplorer(277 LOC) as a visual workspace withscript cards instead of the old tree
WorkspaceDialog(167 LOC): create / open / rename workspaces.sikulibundle validation on workspace openfolder.sikuliinstead of plain filePhase 4 — Stability & audit fixes (1bda337, 713e62a, 7161f02,
9825388, 77c7516, 1757815, 9293fa9, a3c1016, 27abdd2, and others)
restoreSessionagainst empty / missing scriptssaveSessionNPE when no scripts openWelcomeTablifecycle: border syntax, null context safety, image ratiosetFileerror handling: use local var, return false instead of fatal.pyextension inside the.sikulibundlesetFile+ flexible.pydetectionFiles touched (18)
New files (6) — all under
IDE/src/main/java/org/sikuli/ide/ui/:OculixSidebar.java(+345)ScriptExplorer.java(+277)WorkspaceDialog.java(+167)WelcomeTab.java(+129)SidebarItem.java(+96)SidebarSubmenu.java(+39)Main refactor:
SikulixIDE.java(+680 / heavy)SikuliIDEStatusBar.java(+72)EditorConsolePane.java,EditorImageButton.java,ButtonOnToolbar.java,Sikulix.java(minor adjustments)Build & i18n:
IDE/pom.xml(+24): FlatLaf, DJ-Raven, MigLayoutIDE_en_US.properties(+3),IDE_fr.properties(+6)sxideabout.txt,sxidestartup.txt(branding)What I'd like feedback on
SikulixIDE.javablast radius — it grew a lot. Worth splitting outthe workspace logic into its own class, or keep it centralized?
mac_widgetsneutralization — I kept the dependency declared butunused for now. OK to remove fully in a follow-up?
OculiX, or do you prefer the original single-pane approach?
new installs?
Tested on
Not in this PR
Commit history
Will be squashed / cleaned before merge. Current 35 commits kept for
transparency during review.