Skip to content

Claude/ide refactoring#14

Draft
julienmerconsulting wants to merge 35 commits intomasterfrom
claude/ide-refactoring
Draft

Claude/ide refactoring#14
julienmerconsulting wants to merge 35 commits intomasterfrom
claude/ide-refactoring

Conversation

@julienmerconsulting
Copy link
Copy Markdown
Collaborator

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:

  • Aging Look & Feel tied to platform defaults, no dark theme
  • Hand-rolled layout code fragile on HiDPI
  • mac_widgets dependency dragging dead weight we don't use
  • No first-run experience, no visual script browser
  • Several latent NPEs in session restore and save paths

What's in this branch

Phase 0 — Dependencies & theming (16754f3, 21b00ea)

  • Add FlatLaf (modern L&F with native dark theme)
  • Add DJ-Raven (extra components)
  • Add MigLayout (replaces brittle hand-rolled layouts)
  • Neutralize mac_widgets legacy dependency
  • Fix console message colors for dark theme

Phase 1 — Sidebar & navigation (a7eee344c082fd)

  • New OculixSidebar component (345 LOC) with grouped items and submenus
  • Accelerator migration away from the old scattered keybinding code
  • FlatLaf tabs integration
  • All sidebar items unified as submenus (no more mixed UI)
  • SidebarItem / SidebarSubmenu helper components

Phase 2 — Welcome Tab & status bar (82e2a212139355)

  • New WelcomeTab (129 LOC) shown on startup, hidden when a script opens
  • Redesigned SikuliIDEStatusBar (+72 LOC)
  • Live info panels in sidebar: project, status, last run
  • SikuliX-style orange logo, serif "OculiX + IDE" branding
  • Grey-out Run/Capture/Record when no script is open
  • Menu cleanup and modern fonts

Phase 3 — Eclipse-style layout & workspace (0a0276adc730fc)

  • Eclipse-style split: explorer left, console bottom, editor center
  • Full rewrite of ScriptExplorer (277 LOC) as a visual workspace with
    script cards instead of the old tree
  • New WorkspaceDialog (167 LOC): create / open / rename workspaces
  • Auto-refresh of workspace cards on save / create / rename
  • File menu rebuilt with visible section headers (flat layout)
  • File dialogs default to workspace / script directory
  • .sikuli bundle validation on workspace open
  • Default save dialog to folder.sikuli instead of plain file

Phase 4 — Stability & audit fixes (1bda337, 713e62a, 7161f02,

9825388, 77c7516, 1757815, 9293fa9, a3c1016, 27abdd2, and others)

  • IDE startup crash: protect restoreSession against empty / missing scripts
  • Quit crash: saveSession NPE when no scripts open
  • WelcomeTab lifecycle: border syntax, null context safety, image ratio
  • setFile error handling: use local var, return false instead of fatal
  • Save As: refresh workspace cards + honor workspace directory
  • Save As: ensure .py extension inside the .sikuli bundle
  • Workspace open: NPE in setFile + flexible .py detection
  • Final audit: 5 critical bugs fixed in one pass

Files 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, MigLayout
  • IDE_en_US.properties (+3), IDE_fr.properties (+6)
  • sxideabout.txt, sxidestartup.txt (branding)

What I'd like feedback on

  1. SikulixIDE.java blast radius — it grew a lot. Worth splitting out
    the workspace logic into its own class, or keep it centralized?
  2. mac_widgets neutralization — I kept the dependency declared but
    unused for now. OK to remove fully in a follow-up?
  3. Eclipse-style layout — does this fit the direction you want for
    OculiX, or do you prefer the original single-pane approach?
  4. Dark theme as default — currently opt-in. Should it be default for
    new installs?

Tested on

  • Windows 11 / JDK 21 ✅
  • Linux (Ubuntu 24.04) — smoke-tested ✅
  • macOS ARM — not tested, no hardware. Help appreciated.

Not in this PR

  • No changes to the Script API or recognizer logic
  • No changes to the OpenCV / PaddleOCR / ADB stacks
  • Recorder untouched — no changes to the recording pipeline
  • Launcher untouched — no changes to the startup launcher
  • i18n limited to EN + FR strings I added; no full re-translation pass

Commit history

Will be squashed / cleaned before merge. Current 35 commits kept for
transparency during review.

claude added 30 commits April 5, 2026 15:56
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
claude added 5 commits April 5, 2026 21:12
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants