Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [2.1.3] — 2026-08-31

### Added
- Child table rows (e.g. Sales Invoice Items) now open in a right-side slide-in panel instead of expanding inline in the table — full row height, header stays pinned with row actions (Duplicate, Move, Insert Above/Below, Delete) always visible, footer stays pinned with shortcut hints. Drops in over a dimming backdrop; closes via the backdrop, Escape, or Frappe's own header/footer controls. Frappe's native row behavior (row switching, Ctrl+Up/Down navigation, save-on-close) is untouched — the panel is CSS-only, animated off Frappe's own `display:block/none` toggle on `.form-in-grid` via `@starting-style`/`transition-behavior: allow-discrete`, since `GridRow`/`GridRowForm` aren't exposed globally for patching. New Theme Studio toggle (Layout → "Slide-in child table row editor", on by default) falls back to the standard inline expansion when off

### Fixed
- In dark mode, the active sidebar item was nearly invisible — only a 15%-opacity accent tint over the already brand-colored dark sidebar background, relying almost entirely on a thin 3px left border to show selection. Now a solid pill matching light mode's treatment, darkened via relative-color lightness scaling (hue/saturation preserved) rather than mixing toward black — the latter washes out saturation badly on light/pale custom accents (e.g. a pale yellow lost ~38 points of saturation, reading as muddy grey-brown instead of gold). Verified against WCAG AA (4.5:1, worst case 7.2:1) across five accent colors — default, near-black, pure-saturated, desaturated, and pale — using the same contrast formula Theme Studio's own accessibility audit uses
- Workspace Shortcut/Links cards (e.g. a workspace's "Shortcuts"/"Meta" sections) looked flat and washed out on every workspace — a near-invisible `rgba(0,0,0,0.06)` border and a subtle idle shadow left cards barely distinguishable from the page canvas, and count badges fell back to a flat neutral gray whenever a shortcut had no configured color. Cards now use the theme's real card-border token with a stronger idle shadow, and gain a brand-accent top stripe on hover (matching the App Grid's existing card language); default-color count badges pick up an accent tint instead of flat gray — scoped specifically to Shortcut/Links widgets so ordinary status pills elsewhere keep their normal semantic gray meaning
- Section labels typed as plain bold text in a workspace's content editor (e.g. Frappe CRM's "SHORTCUTS"/"META" headings) were being wrapped in their own stray white/dark card — a thin, oddly-shaped sliver rather than a real card. Root cause: Frappe's workspace editor tags every content block, including plain text paragraphs, with a bare `.widget` class meant only for spacing; the theme's card rules in `solvronix_desk.css`, `polish.css`, and the runtime `theme_engine.py` output were all painting card background/border/shadow onto that class unconditionally, and even after excluding EditorJS content blocks from all three, Frappe core's own un-important `.widget` rule was still shining through underneath with a plain background/radius. Added an explicit reset for EditorJS content blocks (`.widget.cdx-block`) so plain text renders as text, while real functional widgets (Shortcut, Links, Chart, Number Card, etc. — which don't carry that class) keep their normal card look. Workspaces that use Frappe's newer structured "Shortcuts"/"Links" group title (`.widget-group-title`) also get a proper shaded header bar instead of a bare label, matching the list-view column header treatment already used elsewhere in the theme

## [2.1.2] — 2026-08-11

### Fixed
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ You set one brand color. The system automatically generates your complete color
### Icon Rail Sidebar
A slim, always-visible app-icon column sits beside the classic workspace list — one compact tile per installed app (ERPNext, CRM, Frappe HR, and so on), each using that app's own icon in a theme-colored tile that follows your brand/accent color automatically. New apps and workspaces appear on the rail automatically. Collapses to icon-only via its own toggle, independent of the classic sidebar. Switch back to the classic single-column Tree layout at any time from Theme Studio → Navbar & Sidebar → Sidebar Layout.

### Slide-In Row Editor
Child table rows (Sales Invoice Items, and any other grid) open in a full-height panel that slides in from the right instead of expanding inline in the table — header stays pinned with row actions (Duplicate, Move, Insert Above/Below, Delete), footer stays pinned with keyboard shortcut hints. Closes via the dimming backdrop, `Esc`, or Frappe's own controls; row switching and `Ctrl+↑`/`Ctrl+↓` navigation work exactly as before. Toggle it off in Theme Studio → Layout to fall back to the classic inline row editor.

### Dark Mode — Light / Dark / Auto
One click cycles between light, dark, and auto. Auto follows your operating system theme and switches live when the OS does. Both modes respect your brand colors. Works on every page.

Expand Down
2 changes: 1 addition & 1 deletion solvronix_desk/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.1.2"
__version__ = "2.1.3"
8 changes: 5 additions & 3 deletions solvronix_desk/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
app_license = "MIT"
app_color = "#E8610A"
app_icon = "octicon octicon-paintcan"
app_version = "2.1.2"
app_version = "2.1.3"

required_apps = []

Expand All @@ -18,17 +18,18 @@
# ── AUTHENTICATED DESK ASSETS ──────────────────────────────────────────────────
# Query versions are bumped whenever an asset changes to invalidate browser cache.
app_include_css = [
"/assets/solvronix_desk/css/solvronix_desk.css?v=54",
"/assets/solvronix_desk/css/solvronix_desk.css?v=58",
"/assets/solvronix_desk/css/sidebar.css?v=31",
"/assets/solvronix_desk/css/command_palette.css?v=4",
"/assets/solvronix_desk/css/smart_home.css?v=7",
"/assets/solvronix_desk/css/progressive_forms.css?v=3",
"/assets/solvronix_desk/css/notification_center.css?v=3",
"/assets/solvronix_desk/css/polish.css?v=3",
"/assets/solvronix_desk/css/polish.css?v=4",
"/assets/solvronix_desk/css/dark_mode.css?v=15",
"/assets/solvronix_desk/css/module_cards.css?v=2",
"/assets/solvronix_desk/css/density.css?v=2",
"/assets/solvronix_desk/css/theme_studio.css?v=19",
"/assets/solvronix_desk/css/grid_row_slide_panel.css?v=1",
]
app_include_js = [
"/assets/solvronix_desk/js/dark_mode.js?v=12",
Expand All @@ -41,6 +42,7 @@
"/assets/solvronix_desk/js/module_cards.js?v=10",
"/assets/solvronix_desk/js/theme_runtime.js?v=8",
"/assets/solvronix_desk/js/chart_runtime.js?v=4",
"/assets/solvronix_desk/js/grid_row_slide_panel.js?v=1",
]

# ── BOOT / INSTALL / DOCUMENT LIFECYCLE HOOKS ──────────────────────────────────
Expand Down
3 changes: 2 additions & 1 deletion solvronix_desk/public/css/dark_mode.css
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ html[data-theme="dark"] .datatable .dt-cell {

html[data-theme="dark"] .datatable .dt-cell--header,
html[data-theme="dark"] .list-row-head,
html[data-theme="dark"] .dt-header {
html[data-theme="dark"] .dt-header,
html[data-theme="dark"] .widget-group-head {
background-color: var(--st-input-bg) !important;
border-color: rgba(255,255,255,0.08) !important;
}
Expand Down
95 changes: 95 additions & 0 deletions solvronix_desk/public/css/grid_row_slide_panel.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/* =============================================================================
Solvronix Desk — Slide-in child table row editor (Task 3)
Frappe 16 already opens a grid row's edit form inline in the table
(GridRowForm's .form-in-grid, toggled via plain display:block/none — there
is no modal to intercept, and GridRow/GridRowForm aren't exposed as
frappe.ui.form.GridRow anywhere, so they can't be monkey-patched from here).
Rather than relocate the DOM node or patch inaccessible classes, this lets
Frappe's own display toggle drive a native slide-in/out animation via
@starting-style + transition-behavior: allow-discrete — CSS handles both
open and close directions, so all of Frappe's own state (row toggling,
keyboard nav, switching rows) keeps working exactly as built.
Gated behind body.st-grid-slide-panel-enabled, set by
grid_row_slide_panel.js from the Theme Studio toggle — absent, this file
has no effect and Frappe's default inline behavior is untouched.
============================================================================= */

body.st-grid-slide-panel-enabled .form-in-grid {
position: fixed !important;
top: var(--st-header-height, 0) !important;
right: 0 !important;
left: auto !important;
bottom: 0 !important;
width: min(480px, 100vw) !important;
height: auto !important;
max-width: 100vw;
margin: 0 !important;
background: var(--st-card-bg) !important;
border: none !important;
border-left: 1px solid var(--st-border) !important;
box-shadow: -8px 0 24px rgba(0, 0, 0, 0.18) !important;
z-index: 1050;
overflow-y: auto;
transform: translateX(0);
transition:
transform var(--st-transition),
display var(--st-transition) allow-discrete,
overlay var(--st-transition) allow-discrete;
}

@starting-style {
body.st-grid-slide-panel-enabled .form-in-grid {
transform: translateX(100%);
}
}

/* Header/footer stay pinned, body scrolls — same structure Frappe already
builds (.grid-form-heading / .grid-form-body / .grid-footer-toolbar),
just re-laid-out as a proper panel instead of an inline block. */
body.st-grid-slide-panel-enabled .form-in-grid .grid-form-heading {
position: sticky;
top: 0;
z-index: 2;
background: var(--st-card-bg);
border-bottom: 1px solid var(--st-border);
padding: 12px 16px;
}
body.st-grid-slide-panel-enabled .form-in-grid .grid-form-body {
display: flex;
flex-direction: column;
min-height: calc(100% - 48px);
}
body.st-grid-slide-panel-enabled .form-in-grid .form-area {
flex: 1 1 auto;
padding: 4px 16px;
}
body.st-grid-slide-panel-enabled .form-in-grid .grid-footer-toolbar {
position: sticky;
bottom: 0;
background: var(--st-card-bg);
border-top: 1px solid var(--st-border);
padding: 10px 16px;
margin-top: auto;
}

/* Backdrop — a single shared element toggled by grid_row_slide_panel.js
whenever any .form-in-grid panel is open; click-to-close is wired there. */
.st-grid-slide-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.35);
z-index: 1040;
opacity: 0;
pointer-events: none;
transition: opacity var(--st-transition);
}
.st-grid-slide-backdrop.st-open {
opacity: 1;
pointer-events: auto;
}

@media (max-width: 640px) {
body.st-grid-slide-panel-enabled .form-in-grid {
width: 100vw !important;
}
}
5 changes: 4 additions & 1 deletion solvronix_desk/public/css/polish.css
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ a {
}

/* ── 3. CARDS — border + layered shadow + hover lift ───────────── */
.widget,
/* :not(.cdx-block) — Frappe's workspace editor tags every content block,
including plain text paragraphs, with a bare .widget class; without this
exclusion a one-line text label gets card border/shadow treatment too. */
.widget:not(.cdx-block),
.frappe-card {
border: 1px solid var(--st-card-border);
box-shadow: var(--st-shadow-sm);
Expand Down
94 changes: 89 additions & 5 deletions solvronix_desk/public/css/solvronix_desk.css
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,17 @@ textarea,
--st-text-muted: #94A3B8;
--st-bg: var(--st-page-bg); /* backwards-compat alias */
/* Sidebar vars restored to dark-on-brand for dark mode */
--st-sidebar-active: color-mix(in srgb, var(--st-accent) 15%, transparent);
/* Solid pill, matching light mode's treatment (var(--st-sidebar-active): var(--st-accent))
— a 15%-opacity tint over the already brand-colored dark sidebar was nearly invisible,
leaving only the 3px left border to show which item is active.
Darkened via relative-color lightness scaling (hue/saturation untouched) rather than
color-mix() toward black — mixing toward black in sRGB compresses saturation hardest on
colors that start light (e.g. a pale custom accent loses ~38 points of saturation), which
reads as muddy rather than a clean darker shade of the same color.
WCAG-verified with the runtime's auto-picked --st-sidebar-active-text: stays >=4.5:1 (worst
case 7.2:1) across default accent, near-black, pure-saturated, desaturated, and pale/light
custom accents (same ratio formula as theme_studio.js's _ratio()/_contrast()). */
--st-sidebar-active: hsl(from var(--st-accent) h s calc(l * 0.55));
--st-sidebar-hover: rgba(255, 255, 255, 0.10);
--st-sidebar-text: #FFFFFF;
--st-sidebar-text-muted: rgba(255, 255, 255, 0.55);
Expand Down Expand Up @@ -702,20 +712,42 @@ input[type="checkbox"]:checked {

/* ── WORKSPACE / HOME CARDS ─────────────────────── */

/* Cards — layered shadow, no top stripe */
/* Frappe's workspace editor tags every content block (plain text paragraphs,
headers, lists — not just real ShortcutWidget/ChartWidget instances) with a
bare .widget class for spacing. Frappe core's own .widget rule (no
!important) unconditionally paints background-color/border-radius onto
that class, so a one-line text label like a bold "SHORTCUTS" heading was
rendering as its own stray white/dark card. Excluding .cdx-block from the
theme's OWN card rules (below, and in polish.css/theme_engine.py) only
stops the theme from reinforcing that — core's un-important rule still
shows through underneath, so it needs an explicit reset here too. */
.widget.cdx-block {
background: transparent !important;
border-radius: 0 !important;
padding: 4px 0 !important;
}

/* Cards — stronger separation from the page canvas than a bare shadow-md gave;
border now uses the theme's real card-border token instead of a near-invisible
flat rgba, so cards read as distinct surfaces at rest, not just on hover. */
.desk-card,
.widget.onboarding-widget-box,
.widget.links-widget-box,
.widget.shortcut-widget-box {
position: relative;
border-top: none !important;
border-radius: var(--st-radius-lg) !important;
box-shadow: var(--st-shadow-md) !important;
border: 1px solid rgba(0,0,0,0.06) !important;
box-shadow: 0 2px 8px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.08) !important;
border: 1px solid var(--st-card-border) !important;
transition: all var(--st-transition) !important;
overflow: hidden;
}
.desk-card:hover,
.widget:hover {
/* :not(.cdx-block) — Frappe's workspace editor tags EVERY content block (plain
text paragraphs, not just real ShortcutWidget/ChartWidget instances) with a
bare .widget class for spacing; without this exclusion a one-line text
label like a bold "SHORTCUTS" heading gets the same hover lift as a card. */
.widget:hover:not(.cdx-block) {
box-shadow: var(--st-shadow-lg) !important;
transform: translateY(-3px);
border-color: var(--st-primary-light) !important;
Expand All @@ -724,6 +756,58 @@ input[type="checkbox"]:checked {
color: var(--st-accent) !important;
}

/* Accent top stripe on hover — scoped to Shortcut/Links widgets only (not the
broad .widget selector) so Number Card/Chart widgets are untouched; mirrors
the App Grid's .st-ws-card::before treatment for one consistent card language. */
.widget.shortcut-widget-box::before,
.widget.links-widget-box::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: var(--st-accent);
opacity: 0;
transition: opacity var(--st-transition);
}
.widget.shortcut-widget-box:hover::before,
.widget.links-widget-box:hover::before {
opacity: 1;
}

/* Section group labels (SHORTCUTS / META / PORTAL, etc.) — a real header bar
(background fill, not just a hairline border) using the same shaded-bg
convention already used for list-view/datatable column headers, so the
label reads as a distinct section header instead of a barely-visible line. */
.widget-group-head {
background: color-mix(in srgb, var(--st-page-bg) 92%, #000) !important;
border: 1px solid var(--st-border) !important;
border-radius: var(--st-radius-sm) !important;
padding: 9px 14px !important;
margin-bottom: 14px !important;
}
.widget-group-head .widget-group-title {
color: var(--st-text-muted) !important;
font-size: 12px !important;
font-weight: 700 !important;
letter-spacing: 0.08em !important;
text-transform: uppercase !important;
margin-bottom: 0 !important;
}

/* Shortcut/Links widget count badge — accent tint instead of the flat neutral
gray Frappe falls back to when a workspace shortcut has no configured color.
Scoped to these two widget types so ordinary status pills elsewhere (list
views, kanban, etc.) keep their normal semantic gray meaning untouched. */
.widget.shortcut-widget-box .indicator-pill.gray,
.widget.shortcut-widget-box .indicator-pill.grey,
.widget.links-widget-box .indicator-pill.gray,
.widget.links-widget-box .indicator-pill.grey {
background: color-mix(in srgb, var(--st-accent) 12%, transparent) !important;
color: var(--st-accent) !important;
}

/* ── PAGE HEAD ───────────────────────────────────────────────────────────────── */
/* Light mode: clean white card with accent left stripe */
html:not([data-theme="dark"]) .desk-page > .page-head,
Expand Down
Loading