feat(dashboard): right-dock inline tools, left-sidebar views, embedded-view polish
- Right dock: Files first/default; usage/activity-log/git-manager render inline; embedded Git Manager uses a container query (compact horizontal tab strip in the dock, full two-pane in the wide pop-out); Files inline viewer + pop-out. - Import Tasks layout fits its container (stacked when narrow, two-pane when wide); Import Tasks uses the GitHub mark. - Planning embeds full-area and works on mobile; planning shows the board WorkflowSwitcher. - Automations screen uses theme color tokens. - Workflow selector matches the project selector height/font. - Left sidebar: uniform spacing across the primary/secondary boundary. Adds a changeset for @runfusion/fusion (minor). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
.changeset/sidebar-panel-redesign.md
Normal file
10
.changeset/sidebar-panel-redesign.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Dashboard navigation and panel redesign (desktop/tablet; mobile unchanged):
|
||||
|
||||
- **Right sidebar**: a single show/hide toggle now lives in the top header (replacing the tablet overflow menu); the dock is hidden when closed and no longer keeps a persistent icon rail or in-dock collapse button. Its tools (Files — now the default/first tab, Activity, Activity Log, Git Manager) render inline inside the dock instead of opening popup modals. Files opens inline with a pop-out to the resizable file modal. The embedded Git Manager adapts to its width (compact horizontal tab strip in the dock, full two-pane in the wide pop-out). The dependency graph no longer appears in the dock.
|
||||
- **Left sidebar**: New Task button matches the item-highlight box; footer spacing between Collapse and Settings; divider before the secondary section removed with uniform row spacing. New main-content destinations — Workflows, Import Tasks (GitHub import, with the GitHub mark), and Automations (two-pane, Command Center styling) — render in the main panel instead of as modals.
|
||||
- **Embedded views**: Planning Mode embeds without modal chrome (no header/close/shadow), fills the full content area, and renders correctly on mobile; the board WorkflowSwitcher is available in Planning. Dev Server header matches Command Center. Insights header wraps so actions don't overlap. List view's left pane can be dragged much narrower with two-line title wrapping.
|
||||
- **Other**: the docked terminal no longer blurs or blocks the page behind it; the footer Terminal button renders as plain text like the running-state trigger; the workflow selector matches the project selector's styling, height, and font size; the Automations screen uses theme color tokens.
|
||||
@@ -962,4 +962,59 @@ The embedded root is a plain flow box that fills the host; the inner shell sheds
|
||||
resize: none;
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:RightDockEmbedding 2026-06-22-12:30:
|
||||
Embedded "Import Tasks" main-content view must fit on screen and react to its OWN width, not the viewport.
|
||||
The shared two-pane code sets the list pane width via an inline flex-basis (default 360px) whenever the VIEWPORT is wide (canResizePanes => innerWidth > 860). In the embedded main area the host can be far narrower than the viewport, so that inline 360px list pane plus the preview overflowed horizontally.
|
||||
Fix (embedded variant only — modal path untouched): turn the embedded root into a query container (container-type: inline-size) and drive the layout off @container width.
|
||||
- Narrow container (default): stack list ABOVE preview in a single column; cap the list height and override the inline desktop flex-basis so nothing forces horizontal overflow. Long titles/repo names already truncate via .issue-title ellipsis / .issue-main min-width:0, and labels/branch info wrap.
|
||||
- Wide container (>= 720px): restore the two-pane row, but bound the list pane to a sane share of the container (clamp) instead of trusting the viewport-derived inline width.
|
||||
*/
|
||||
.github-import-embedded.right-dock-embedded-view {
|
||||
container-type: inline-size;
|
||||
container-name: github-import-embedded;
|
||||
}
|
||||
|
||||
/* Default (narrow container): single stacked column. */
|
||||
.github-import-modal--embedded .github-import-workspace {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Override the inline viewport-derived flex-basis so the list never forces overflow when stacked. */
|
||||
.github-import-modal--embedded .github-import-list-pane {
|
||||
flex: 0 0 auto !important;
|
||||
width: 100%;
|
||||
max-height: 40cqh;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.github-import-modal--embedded .github-import-preview-pane {
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* Hide the col-resize handle when stacked; it only makes sense in the side-by-side layout. */
|
||||
.github-import-modal--embedded .github-import-workspace__resize-handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@container github-import-embedded (min-width: 720px) {
|
||||
.github-import-modal--embedded .github-import-workspace {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
/* Side-by-side again: bound the list pane to a share of the CONTAINER width, overriding the inline viewport width. */
|
||||
.github-import-modal--embedded .github-import-list-pane {
|
||||
flex: 0 1 clamp(240px, 38cqi, 420px) !important;
|
||||
width: auto;
|
||||
max-height: none;
|
||||
padding-right: var(--space-md);
|
||||
}
|
||||
|
||||
.github-import-modal--embedded .github-import-workspace__resize-handle {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -85,11 +85,11 @@ With the secondary divider removed the nav reads as one continuous list, so the
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:Navigation 2026-06-22-00:00:
|
||||
The secondary section keeps its top spacing but drops the divider line before the first secondary entry (Goals/Evals); the rule reads as one continuous nav list instead of two bordered groups.
|
||||
FNXC:Navigation 2026-06-22-00:30:
|
||||
The secondary section has no divider and no extra top padding, so the gap across the primary/secondary boundary (e.g. Compound -> Workflows) equals the --space-xs list/row rhythm and the nav reads as one continuous list.
|
||||
*/
|
||||
.left-sidebar-nav__section--secondary {
|
||||
padding-top: var(--space-sm);
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.left-sidebar-nav__item {
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
Clock,
|
||||
FileText,
|
||||
Gauge,
|
||||
GitPullRequestArrow,
|
||||
Lightbulb,
|
||||
LayoutGrid,
|
||||
List,
|
||||
@@ -35,6 +34,18 @@ import type { TaskView } from "../hooks/useViewState";
|
||||
import { buildPluginTaskViewId } from "../plugins/pluginViewRegistry";
|
||||
import { getPluginNavIcon } from "./pluginNavIcon";
|
||||
|
||||
/*
|
||||
FNXC:Navigation 2026-06-22-00:30:
|
||||
Import Tasks uses the GitHub brand mark. lucide-react in this repo does not export a `Github` icon, so render the octocat glyph as a LucideProps-compatible component (size defaults to 16, currentColor fill) usable wherever a sidebar entry icon is expected.
|
||||
*/
|
||||
function GithubIcon({ size = 16, ...props }: LucideProps) {
|
||||
return (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" {...props}>
|
||||
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.203 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.942.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export interface LeftSidebarExperimentalFeatures {
|
||||
insights?: boolean;
|
||||
memoryView?: boolean;
|
||||
@@ -367,7 +378,7 @@ export function LeftSidebarNav({
|
||||
label: t("nav.importTasks", "Import Tasks"),
|
||||
view: "import-tasks" as TaskView,
|
||||
isActive: view === "import-tasks",
|
||||
icon: GitPullRequestArrow,
|
||||
icon: GithubIcon,
|
||||
testId: "sidebar-nav-import-tasks",
|
||||
onSelect: () => onChangeView("import-tasks"),
|
||||
},
|
||||
|
||||
@@ -56,12 +56,19 @@ FN-6886 promotes Planning Mode into the main app content area. The embedded shel
|
||||
.planning-view {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
padding: var(--space-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:PlanningMode 2026-06-22-15:30:
|
||||
FN-6886 full-view fix: embedded planning must fill the entire main-content pane with no awkward gaps. The flex/height chain is planning-view (flex:1, height:100%) -> planning-modal--embedded (flex:1, height:100%) -> planning-modal-body (flex:1, min-height:0). flex:1 + min-width:0 + min-height:0 on the embedded panel lets it consume all remaining width/height inside the flex .planning-view wrapper instead of collapsing to the base .modal 480px width.
|
||||
*/
|
||||
.planning-modal--embedded {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
min-width: 0;
|
||||
@@ -383,17 +390,26 @@ The embedded planning title must read like other embedded-view titles (Command C
|
||||
box-shadow: var(--focus-ring-strong);
|
||||
}
|
||||
|
||||
/* Mobile: stack — only one pane visible at a time */
|
||||
@media (max-width: 768px) {
|
||||
/* Mobile: stack — only one pane visible at a time.
|
||||
FNXC:PlanningMode 2026-06-22-15:30 covers both the landscape phone case
|
||||
(max-height: 480px exceeds 768px wide) and portrait. */
|
||||
@media (max-width: 768px), (max-height: 480px) {
|
||||
/* Full-screen sheet — drop overlay padding so the modal fills the
|
||||
viewport instead of being pushed below it, and disable resize since
|
||||
touchscreen users can't drag the corner grip anyway. */
|
||||
touchscreen users can't drag the corner grip anyway.
|
||||
FNXC:PlanningMode 2026-06-22-15:30: scope the viewport-takeover rules to
|
||||
the NON-embedded (dialog) presentation only. The embedded panel
|
||||
(.planning-modal--embedded) must NOT grab 100vw/100dvh — it lives inside
|
||||
the main-content pane, so forcing full-viewport sizing made it overflow
|
||||
the content area on mobile. The :not(.planning-modal--embedded) guard
|
||||
keeps the modal full-screen sheet intact while letting the embedded view
|
||||
fill only its own pane (handled by the .planning-view rules below). */
|
||||
.modal-overlay:has(.planning-modal) {
|
||||
padding-top: 0;
|
||||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
}
|
||||
.modal.planning-modal {
|
||||
.modal.planning-modal:not(.planning-modal--embedded) {
|
||||
width: 100vw;
|
||||
min-width: 0;
|
||||
max-width: 100vw;
|
||||
@@ -405,12 +421,23 @@ The embedded planning title must read like other embedded-view titles (Command C
|
||||
border-radius: 0;
|
||||
resize: none;
|
||||
}
|
||||
.modal.planning-modal[style*="--keyboard-overlap"] {
|
||||
.modal.planning-modal:not(.planning-modal--embedded)[style*="--keyboard-overlap"] {
|
||||
height: var(--vv-height, 100dvh);
|
||||
max-height: var(--vv-height, 100dvh);
|
||||
transform: translateY(var(--vv-offset-top, 0px));
|
||||
will-change: transform;
|
||||
}
|
||||
/* FNXC:PlanningMode 2026-06-22-15:30: on mobile the embedded view should use
|
||||
the full width of the (already-narrow) content pane — drop the outer
|
||||
padding so the session list and detail panes are edge-to-edge, and keep
|
||||
the height/flex chain filling the pane. */
|
||||
.planning-view {
|
||||
padding: var(--space-sm);
|
||||
}
|
||||
.planning-view .planning-modal--embedded {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.planning-modal-body--split {
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -1374,7 +1401,11 @@ The embedded planning title must read like other embedded-view titles (Command C
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.planning-modal {
|
||||
/* FNXC:PlanningMode 2026-06-22-15:30: this legacy full-viewport sheet sizing
|
||||
is for the dialog presentation only. The :not(.planning-modal--embedded)
|
||||
guard prevents the embedded view from being yanked to 100vw/100dvh, which
|
||||
would overflow the main-content pane it lives in on mobile. */
|
||||
.planning-modal:not(.planning-modal--embedded) {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
|
||||
@@ -46,11 +46,20 @@ function relativeTime(iso: string): string {
|
||||
return `${Math.floor(diffMs / 86_400_000)}d ago`;
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:Automations 2026-06-22-12:00:
|
||||
Trigger-type badge colors must use the design system's THEME COLOR TOKENS so the Automations screen follows the
|
||||
active theme (including light theme) instead of fixed hex literals. The previous values referenced undefined tokens
|
||||
(--color-blue/-purple/-green/-gray) with hardcoded hex fallbacks that never resolved to a real token and never
|
||||
adapted to the theme. Mapped to the closest defined semantic tokens from styles.css: cron→--todo (blue status),
|
||||
webhook→--accent (brand purple), api→--color-success (green), manual→--text-muted (neutral). The badge applies this
|
||||
to both border and text via inline style on .routine-trigger-badge.
|
||||
*/
|
||||
const TRIGGER_TYPE_COLORS: Record<RoutineTriggerType, string> = {
|
||||
cron: "var(--color-blue, #3b82f6)",
|
||||
webhook: "var(--color-purple, #a855f7)",
|
||||
api: "var(--color-green, #22c55e)",
|
||||
manual: "var(--color-gray, #6b7280)",
|
||||
cron: "var(--todo)",
|
||||
webhook: "var(--accent)",
|
||||
api: "var(--color-success)",
|
||||
manual: "var(--text-muted)",
|
||||
};
|
||||
|
||||
const TRIGGER_TYPE_LABELS: Record<RoutineTriggerType, string> = {
|
||||
|
||||
@@ -1289,9 +1289,15 @@ when supported, falling back to a min-width media breakpoint, that collapses to
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:Automations 2026-06-22-12:00:
|
||||
Selected automation row uses the theme accent token. --accent-subtle is not a defined token; fall back to a
|
||||
theme-derived subtle accent tint (color-mix house style) so the active state follows the active theme (incl. light)
|
||||
instead of resolving to a flat --card with no accent emphasis.
|
||||
*/
|
||||
.automation-list-row.active {
|
||||
border-color: var(--accent);
|
||||
background: var(--accent-subtle, var(--card));
|
||||
background: var(--accent-subtle, color-mix(in srgb, var(--accent) 10%, transparent));
|
||||
}
|
||||
|
||||
.automation-list-row-name {
|
||||
@@ -1964,53 +1970,60 @@ The embedded host fills its right-dock container; the inner shell drops overlay-
|
||||
border-radius: 0;
|
||||
resize: none;
|
||||
position: static;
|
||||
/* Drive the dock-vs-expand layout off the embedded host's own width, not the viewport. */
|
||||
container-type: inline-size;
|
||||
container-name: gm-embedded;
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:GitManager 2026-06-22-00:10:
|
||||
The embedded Git Manager renders inside the narrow right dock, so it must use the mobile single-column layout (section tabs as a horizontal strip above a full-width content pane) regardless of viewport width, mirroring the max-width:768px rules.
|
||||
FNXC:GitManager 2026-06-22-00:25:
|
||||
The embedded Git Manager adapts to its container width, not the viewport, so the SAME embedded render works in both the narrow right dock and the wide pop-out (expand) modal:
|
||||
- Wide container (expand modal): inherits the default desktop layout — vertical section sidebar + content (two-pane, like before).
|
||||
- Narrow container (dock, < 560px): section tabs become a horizontal strip above a full-width content pane, and each tab is compact (min-width:0, tight padding, inline icon+label) so MORE tabs fit in the strip at once. The content collapses to a single column.
|
||||
*/
|
||||
.gm-modal--embedded .gm-layout {
|
||||
flex-direction: column;
|
||||
}
|
||||
@container gm-embedded (max-width: 560px) {
|
||||
.gm-modal--embedded .gm-layout {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.gm-modal--embedded .gm-sidebar {
|
||||
flex: 0 0 auto;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: calc(var(--space-2xl) + var(--space-md));
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
padding: var(--space-xs) var(--space-sm);
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
.gm-modal--embedded .gm-sidebar {
|
||||
flex: 0 0 auto;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
padding: var(--space-xs);
|
||||
gap: calc(var(--space-xs) / 2);
|
||||
}
|
||||
|
||||
.gm-modal--embedded .gm-nav-item {
|
||||
flex: 0 0 auto;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--space-xs) / 2);
|
||||
padding: var(--space-xs) var(--space-sm);
|
||||
border-left: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
min-width: calc(var(--space-2xl) + var(--space-xl));
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.gm-modal--embedded .gm-nav-item {
|
||||
flex: 0 0 auto;
|
||||
flex-direction: row;
|
||||
gap: var(--space-xs);
|
||||
padding: calc(var(--space-xs) / 2) var(--space-xs);
|
||||
border-left: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
min-width: 0;
|
||||
font-size: var(--font-size-xs);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.gm-modal--embedded .gm-nav-item.active {
|
||||
border-left-color: transparent;
|
||||
border-bottom-color: var(--todo);
|
||||
}
|
||||
.gm-modal--embedded .gm-nav-item.active {
|
||||
border-left-color: transparent;
|
||||
border-bottom-color: var(--todo);
|
||||
}
|
||||
|
||||
.gm-modal--embedded .gm-status-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.gm-modal--embedded .gm-status-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.gm-modal--embedded .gm-create-form {
|
||||
flex-wrap: wrap;
|
||||
.gm-modal--embedded .gm-create-form {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* Main layout: sidebar + content */
|
||||
|
||||
@@ -28,6 +28,12 @@ The workflow selector trigger must match the project selector trigger styling: t
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--text-muted);
|
||||
font: inherit;
|
||||
/*
|
||||
FNXC:WorkflowSwitcher 2026-06-22-00:10:
|
||||
Match the project selector trigger height and font size: the project label uses 13px / line-height 1, so set the same here (the parent .workflow-switcher font-size is smaller). With identical padding + font-size + line-height the two triggers render the same height.
|
||||
*/
|
||||
font-size: 13px;
|
||||
line-height: 1;
|
||||
text-align: left;
|
||||
transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,14 @@ FNXC:Navigation 2026-06-22-00:00:
|
||||
Right-dock tools render INLINE inside the dock container, not as popup modals: usage, activity-log, and git-manager use each modal's `presentation="embedded"` mode instead of launching an overlay. (github-import and automation remain launcher actions here only until their left-sidebar/main destinations land, then they leave the dock.)
|
||||
*/
|
||||
export const STATIC_OVERFLOW_VIEW_ENTRIES: readonly OverflowViewEntry[] = [
|
||||
/* FNXC:Navigation 2026-06-22-00:20: Files is the first/default right-dock tool. */
|
||||
{
|
||||
key: "files",
|
||||
label: "Files",
|
||||
icon: Folder,
|
||||
testId: "right-dock-tab-files",
|
||||
render: (props) => wrapOverflowView(<DockFilesView projectId={props.projectId} openFile={props.openFile} />),
|
||||
},
|
||||
{
|
||||
key: "usage",
|
||||
label: "Activity",
|
||||
@@ -140,13 +148,6 @@ export const STATIC_OVERFLOW_VIEW_ENTRIES: readonly OverflowViewEntry[] = [
|
||||
/>,
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "files",
|
||||
label: "Files",
|
||||
icon: Folder,
|
||||
testId: "right-dock-tab-files",
|
||||
render: (props) => wrapOverflowView(<DockFilesView projectId={props.projectId} openFile={props.openFile} />),
|
||||
},
|
||||
];
|
||||
|
||||
function buildPluginOverflowViewEntries(pluginDashboardViews: PluginDashboardViewEntry[] = []): OverflowViewEntry[] {
|
||||
|
||||
Reference in New Issue
Block a user