diff --git a/.github/workflows/deploy-pages-channel.yml b/.github/workflows/deploy-pages-channel.yml index 0ce004834..0fcc7fc8c 100644 --- a/.github/workflows/deploy-pages-channel.yml +++ b/.github/workflows/deploy-pages-channel.yml @@ -74,7 +74,7 @@ jobs: uses: extractions/setup-just@v3 - name: Install wasm-bindgen-cli - run: cargo install wasm-bindgen-cli --version 0.2.114 --locked + run: cargo install wasm-bindgen-cli --version 0.2.114 --locked --force - name: Install dioxus-cli run: cargo install dioxus-cli --version 0.7.9 --locked diff --git a/.github/workflows/deploy-studio-pages.yml b/.github/workflows/deploy-studio-pages.yml index 71041c62f..b4a6174f4 100644 --- a/.github/workflows/deploy-studio-pages.yml +++ b/.github/workflows/deploy-studio-pages.yml @@ -64,7 +64,7 @@ jobs: uses: extractions/setup-just@v3 - name: Install wasm-bindgen-cli - run: cargo install wasm-bindgen-cli --version 0.2.114 --locked + run: cargo install wasm-bindgen-cli --version 0.2.114 --locked --force - name: Install dioxus-cli run: cargo install dioxus-cli --version 0.7.9 --locked diff --git a/lp-app/lpa-studio-core/src/app/device/device_controller.rs b/lp-app/lpa-studio-core/src/app/device/device_controller.rs index 6b8c5445a..590620352 100644 --- a/lp-app/lpa-studio-core/src/app/device/device_controller.rs +++ b/lp-app/lpa-studio-core/src/app/device/device_controller.rs @@ -2,14 +2,13 @@ use crate::core::view::steps_view::{UiStepState, UiStepView}; use crate::{ ConnectedDeviceSummary, Controller, ControllerId, DeviceOp, DeviceSnapshot, EndpointChoice, LinkController, LinkOp, LinkState, ProjectOp, ProjectState, ProviderChoice, ServerController, - ServerFailureKind, ServerState, UiAction, UiLogEntry, UiMetric, UiPaneView, UiStatus, - UiStepsView, UiTerminalLine, UiViewContent, + ServerFailureKind, ServerState, UiAction, UiMetric, UiPaneView, UiStatus, UiStepsView, + UiViewContent, }; pub struct DeviceController { pub(crate) link: LinkController, pub(crate) server: ServerController, - terminal: Vec, } impl DeviceController { @@ -23,7 +22,6 @@ impl DeviceController { Self { link: LinkController::new(), server: ServerController::new(), - terminal: Vec::new(), } } @@ -49,30 +47,8 @@ impl DeviceController { ) } - pub fn has_meaningful_terminal(&self) -> bool { - !matches!(self.link.state(), LinkState::SelectingProvider { .. }) - } - - pub fn record_logs(&mut self, logs: &[UiLogEntry]) { - self.terminal.extend( - logs.iter() - .filter(|log| is_device_log_source(&log.source)) - .map(|log| UiTerminalLine::new(format!("[{}] {}", log.source, log.message))), - ); - if self.terminal.len() > 240 { - let remove_count = self.terminal.len() - 240; - self.terminal.drain(0..remove_count); - } - } - pub fn view(&self, project_state: &ProjectState, project_actions: Vec) -> UiPaneView { - let stack = UiStepsView::new(self.sections(project_state, project_actions)).with_terminal( - if self.has_meaningful_terminal() { - self.terminal.clone() - } else { - Vec::new() - }, - ); + let stack = UiStepsView::new(self.sections(project_state, project_actions)); UiPaneView::new( Self::NODE_ID, @@ -604,10 +580,3 @@ fn provider_action_priority(kind: lpa_link::LinkProviderKind) -> crate::ActionPr lpa_link::LinkProviderKind::Fake => crate::ActionPriority::Tertiary, } } - -fn is_device_log_source(source: &str) -> bool { - matches!( - source, - "lpa-link" | "browser-serial" | "fw-esp32" | "fw-browser" | "lp-server" - ) -} diff --git a/lp-app/lpa-studio-core/src/app/studio/studio_controller.rs b/lp-app/lpa-studio-core/src/app/studio/studio_controller.rs index 59c91b41e..c235a95a9 100644 --- a/lp-app/lpa-studio-core/src/app/studio/studio_controller.rs +++ b/lp-app/lpa-studio-core/src/app/studio/studio_controller.rs @@ -197,7 +197,6 @@ impl StudioController { connected: ConnectedLink, updates: UxUpdateSink, ) -> UiResult { - self.device.record_logs(&connected.logs); self.logs.extend(connected.logs); self.connect_server_connection(&connected.connection, updates) .await @@ -263,7 +262,6 @@ impl StudioController { Ok(auto_connect) => auto_connect, Err(error) => { let pending_logs = self.device.server.take_pending_logs(); - self.device.record_logs(&pending_logs); self.logs.extend(pending_logs); self.project.reset(); if matches!(error, UiError::NoFirmwareDetected(_)) { @@ -326,7 +324,6 @@ impl StudioController { }; match result { Ok(ProjectConnectResult::Connected { logs }) => { - self.device.record_logs(&logs); self.logs.extend(logs); let sync = self.sync_project_after_attach(updates).await?; Ok(UiNotices::new().with_notice(project_sync_notice( @@ -336,12 +333,10 @@ impl StudioController { ))) } Ok(ProjectConnectResult::SelectionRequired { logs }) => { - self.device.record_logs(&logs); self.logs.extend(logs); Ok(UiNotices::new().with_notice(UiNotice::info("Choose running project"))) } Ok(ProjectConnectResult::NotFound { logs }) => { - self.device.record_logs(&logs); self.logs.extend(logs); Ok(UiNotices::new().with_notice(UiNotice::info("No running project found"))) } @@ -376,7 +371,6 @@ impl StudioController { }; match result? { ProjectConnectResult::Connected { logs } => { - self.device.record_logs(&logs); self.logs.extend(logs); let sync = self.sync_project_after_attach(updates).await?; Ok(AutoProjectConnect::Connected { @@ -384,12 +378,10 @@ impl StudioController { }) } ProjectConnectResult::SelectionRequired { logs } => { - self.device.record_logs(&logs); self.logs.extend(logs); Ok(AutoProjectConnect::SelectionRequired) } ProjectConnectResult::NotFound { logs } => { - self.device.record_logs(&logs); self.logs.extend(logs); Ok(AutoProjectConnect::NotFound) } @@ -410,7 +402,6 @@ impl StudioController { }; match result { Ok(logs) => { - self.device.record_logs(&logs); self.logs.extend(logs); let sync = self.sync_project_after_attach(updates).await?; Ok(UiNotices::new().with_notice(project_sync_notice( @@ -445,7 +436,6 @@ impl StudioController { }; match result { Ok(logs) => { - self.device.record_logs(&logs); self.logs.extend(logs); let sync = self.sync_project_after_attach(updates).await?; Ok(UiNotices::new().with_notice(project_sync_notice( @@ -515,7 +505,6 @@ impl StudioController { } fn record_project_sync_run(&mut self, sync: &ProjectSyncRun) { - self.device.record_logs(&sync.logs); self.logs.extend(sync.logs.clone()); } @@ -560,7 +549,6 @@ impl StudioController { } }; self.record_logs(core::mem::take(&mut *captured_logs.borrow_mut())); - self.device.record_logs(&management.logs); self.logs.extend(management.logs); let mut outcome = UiNotices::new().with_notice(UiNotice::info("Device reset")); @@ -630,7 +618,6 @@ impl StudioController { return Err(error); } }; - self.device.record_logs(&management.logs); self.logs.extend(management.logs); let mut outcome = UiNotices::new().with_notice(provision_notice(&management.result)); emit_activity( @@ -699,7 +686,6 @@ impl StudioController { return Err(error); } }; - self.device.record_logs(&management.logs); self.logs.extend(management.logs); let mut outcome = UiNotices::new().with_notice(reset_notice(&management.result)); emit_activity( @@ -749,7 +735,6 @@ impl StudioController { if logs.is_empty() { return; } - self.device.record_logs(&logs); self.logs.extend(logs); } } diff --git a/lp-app/lpa-studio-web/assets/tailwind.css b/lp-app/lpa-studio-web/assets/tailwind.css index bd18742ad..7b3b5046e 100644 --- a/lp-app/lpa-studio-web/assets/tailwind.css +++ b/lp-app/lpa-studio-web/assets/tailwind.css @@ -16,6 +16,7 @@ --tw-text-xl: 1.25rem; --tw-text-xl--line-height: calc(1.75 / 1.25); --tw-font-weight-medium: 500; + --tw-font-weight-semibold: 600; --tw-font-weight-bold: 700; --tw-font-weight-extrabold: 800; --tw-leading-tight: 1.25; @@ -174,9 +175,15 @@ .tw\:h-2 { height: calc(var(--tw-spacing) * 2); } + .tw\:h-4 { + height: calc(var(--tw-spacing) * 4); + } .tw\:h-6 { height: calc(var(--tw-spacing) * 6); } + .tw\:h-8 { + height: calc(var(--tw-spacing) * 8); + } .tw\:h-14 { height: calc(var(--tw-spacing) * 14); } @@ -240,9 +247,15 @@ .tw\:w-2 { width: calc(var(--tw-spacing) * 2); } + .tw\:w-4 { + width: calc(var(--tw-spacing) * 4); + } .tw\:w-6 { width: calc(var(--tw-spacing) * 6); } + .tw\:w-8 { + width: calc(var(--tw-spacing) * 8); + } .tw\:w-14 { width: calc(var(--tw-spacing) * 14); } @@ -329,6 +342,9 @@ .tw\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } + .tw\:grid-cols-\[18px_minmax\(0\,1fr\)_auto\] { + grid-template-columns: 18px minmax(0,1fr) auto; + } .tw\:grid-cols-\[28px_minmax\(0\,1fr\)\] { grid-template-columns: 28px minmax(0,1fr); } @@ -353,17 +369,14 @@ .tw\:grid-cols-\[260px_minmax\(0\,1fr\)\] { grid-template-columns: 260px minmax(0,1fr); } - .tw\:grid-cols-\[minmax\(0\,1fr\)_auto_auto\] { - grid-template-columns: minmax(0,1fr) auto auto; - } .tw\:grid-cols-\[minmax\(0\,1fr\)_minmax\(300px\,380px\)\] { grid-template-columns: minmax(0,1fr) minmax(300px,380px); } .tw\:grid-cols-\[minmax\(80px\,0\.35fr\)_minmax\(0\,1fr\)\] { grid-template-columns: minmax(80px,0.35fr) minmax(0,1fr); } - .tw\:grid-cols-\[minmax\(120px\,0\.4fr\)_minmax\(0\,1fr\)_24px\] { - grid-template-columns: minmax(120px,0.4fr) minmax(0,1fr) 24px; + .tw\:grid-cols-\[minmax\(120px\,0\.4fr\)_minmax\(0\,1fr\)_32px\] { + grid-template-columns: minmax(120px,0.4fr) minmax(0,1fr) 32px; } .tw\:grid-cols-\[minmax\(120px\,0\.35fr\)_minmax\(0\,1fr\)\] { grid-template-columns: minmax(120px,0.35fr) minmax(0,1fr); @@ -913,6 +926,10 @@ --tw-font-weight: var(--tw-font-weight-medium); font-weight: var(--tw-font-weight-medium); } + .tw\:font-semibold { + --tw-font-weight: var(--tw-font-weight-semibold); + font-weight: var(--tw-font-weight-semibold); + } .tw\:break-words { overflow-wrap: break-word; } diff --git a/lp-app/lpa-studio-web/src/app/node/config_slot_row.rs b/lp-app/lpa-studio-web/src/app/node/config_slot_row.rs index c18e7a1f9..58331ff2a 100644 --- a/lp-app/lpa-studio-web/src/app/node/config_slot_row.rs +++ b/lp-app/lpa-studio-web/src/app/node/config_slot_row.rs @@ -56,10 +56,7 @@ pub fn ConfigSlotRow( span { class: "tw:h-6 tw:w-6 tw:flex-none" } } div { class: "tw:min-w-0", - strong { class: "tw:block tw:min-w-0 tw:text-sm tw:leading-tight tw:text-strong-foreground tw:break-words", "{slot.label}" } - if let Some(detail) = slot.detail.as_ref() { - small { class: "tw:block tw:text-xs tw:text-subtle-foreground tw:break-words", "{detail}" } - } + strong { class: "tw:block tw:min-w-0 tw:text-sm tw:font-semibold tw:leading-tight tw:text-strong-foreground tw:break-words", "{slot.label}" } } } div { class: "tw:flex tw:min-w-0 tw:items-center tw:justify-end tw:gap-2 tw:text-sm tw:leading-tight tw:text-muted-foreground", @@ -168,9 +165,9 @@ fn AssetSlotEditor(asset: lpa_studio_core::UiSlotAsset) -> Element { fn record_summary_class(expanded: bool) -> &'static str { if expanded { - "tw:text-xs tw:font-bold tw:uppercase tw:text-subtle-foreground" + "tw:text-xs tw:font-semibold tw:uppercase tw:text-subtle-foreground" } else { - "tw:text-xs tw:font-bold tw:uppercase tw:text-muted-foreground" + "tw:text-xs tw:font-semibold tw:uppercase tw:text-muted-foreground" } } diff --git a/lp-app/lpa-studio-web/src/app/node/node_header.rs b/lp-app/lpa-studio-web/src/app/node/node_header.rs index c5c44a947..eed78c5a9 100644 --- a/lp-app/lpa-studio-web/src/app/node/node_header.rs +++ b/lp-app/lpa-studio-web/src/app/node/node_header.rs @@ -28,7 +28,7 @@ fn NodeStatusMenu(header: UiNodeHeader) -> Element { class: node_status_button_class(header.status.kind).to_string(), open_class: node_status_button_open_class(header.status.kind).to_string(), icon, - icon_size: 13, + icon_size: 16, label, title: format!("{} status details", header.title), popup_class: node_status_popup_class(header.status.kind).to_string(), @@ -91,19 +91,19 @@ fn status_icon(kind: UiStatusKind) -> StudioIconName { fn node_status_button_class(kind: UiStatusKind) -> &'static str { match kind { UiStatusKind::Neutral => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-neutral-border tw:bg-status-neutral-bg tw:p-0 tw:text-status-neutral-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-neutral-border tw:bg-status-neutral-bg tw:p-0 tw:text-status-neutral-foreground" } UiStatusKind::Working => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-working-border tw:bg-status-working-bg tw:p-0 tw:text-status-working-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-working-border tw:bg-status-working-bg tw:p-0 tw:text-status-working-foreground" } UiStatusKind::Good => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-good-border tw:bg-status-good-bg tw:p-0 tw:text-status-good-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-good-border tw:bg-status-good-bg tw:p-0 tw:text-status-good-foreground" } UiStatusKind::Warning => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-warning-border tw:bg-status-warning-bg tw:p-0 tw:text-status-warning-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-warning-border tw:bg-status-warning-bg tw:p-0 tw:text-status-warning-foreground" } UiStatusKind::Error => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-error-border tw:bg-status-error-bg tw:p-0 tw:text-status-error-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-error-border tw:bg-status-error-bg tw:p-0 tw:text-status-error-foreground" } } } @@ -111,19 +111,19 @@ fn node_status_button_class(kind: UiStatusKind) -> &'static str { fn node_status_button_open_class(kind: UiStatusKind) -> &'static str { match kind { UiStatusKind::Neutral => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-neutral-border tw:bg-card-raised tw:p-0 tw:text-status-neutral-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-neutral-border tw:bg-card-raised tw:p-0 tw:text-status-neutral-foreground" } UiStatusKind::Working => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-working-border tw:bg-card-raised tw:p-0 tw:text-status-working-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-working-border tw:bg-card-raised tw:p-0 tw:text-status-working-foreground" } UiStatusKind::Good => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-good-border tw:bg-card-raised tw:p-0 tw:text-status-good-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-good-border tw:bg-card-raised tw:p-0 tw:text-status-good-foreground" } UiStatusKind::Warning => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-warning-border tw:bg-card-raised tw:p-0 tw:text-status-warning-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-warning-border tw:bg-card-raised tw:p-0 tw:text-status-warning-foreground" } UiStatusKind::Error => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-error-border tw:bg-card-raised tw:p-0 tw:text-status-error-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-full tw:border tw:border-status-error-border tw:bg-card-raised tw:p-0 tw:text-status-error-foreground" } } } diff --git a/lp-app/lpa-studio-web/src/app/node/produced_product_view.rs b/lp-app/lpa-studio-web/src/app/node/produced_product_view.rs index c50a3983a..3faa87b6c 100644 --- a/lp-app/lpa-studio-web/src/app/node/produced_product_view.rs +++ b/lp-app/lpa-studio-web/src/app/node/produced_product_view.rs @@ -341,15 +341,10 @@ fn product_label(kind: UiProductKind) -> &'static str { fn preview_detail(preview: &UiProductPreview, tracking: UiProductTrackingState) -> Option { match preview { - UiProductPreview::VisualSrgb8 { - width, - height, - revision, - .. - } => Some(format!("{width} x {height} rev {revision}")), + UiProductPreview::VisualSrgb8 { width, height, .. } => Some(format!("{width} x {height}")), UiProductPreview::ControlNative(preview) => Some(format!( - "{} x {} samples rev {}", - preview.extent.rows, preview.extent.samples_per_row, preview.revision + "{} x {} samples", + preview.extent.rows, preview.extent.samples_per_row )), UiProductPreview::Pending if tracking == UiProductTrackingState::Tracking => { Some("preview pending".to_string()) diff --git a/lp-app/lpa-studio-web/src/app/node/produced_value_view.rs b/lp-app/lpa-studio-web/src/app/node/produced_value_view.rs index 8cc5a34b3..406ba9c9a 100644 --- a/lp-app/lpa-studio-web/src/app/node/produced_value_view.rs +++ b/lp-app/lpa-studio-web/src/app/node/produced_value_view.rs @@ -31,7 +31,7 @@ pub fn ProducedValueView( } } } - dt { class: "tw:flex tw:min-w-0 tw:items-center tw:justify-between tw:gap-1.5 tw:text-xs tw:font-bold tw:leading-tight tw:text-subtle-foreground", + dt { class: "tw:flex tw:min-w-0 tw:items-center tw:justify-between tw:gap-1.5 tw:text-xs tw:font-semibold tw:leading-tight tw:text-subtle-foreground", span { class: "tw:min-w-0 tw:break-words", "{value.label}" } SlotDetailButton { label: value.label.clone(), diff --git a/lp-app/lpa-studio-web/src/app/node/slot_detail_button.rs b/lp-app/lpa-studio-web/src/app/node/slot_detail_button.rs index ebd5d933c..25ce72b84 100644 --- a/lp-app/lpa-studio-web/src/app/node/slot_detail_button.rs +++ b/lp-app/lpa-studio-web/src/app/node/slot_detail_button.rs @@ -21,10 +21,9 @@ pub fn SlotDetailButton( let menu_label = format!("{label} details"); rsx! { - span { class: "tw:inline-flex tw:w-6 tw:justify-end", + span { class: "tw:inline-flex tw:w-8 tw:justify-end", IconMenuButton { icon: style.icon, - icon_size: 13, label: menu_label.clone(), title: menu_label, tone: style.tone, @@ -43,22 +42,22 @@ pub fn SlotDetailButton( pub(crate) fn slot_row_class(affordance: UiSlotAffordance, index: usize) -> &'static str { match affordance { UiSlotAffordance::Error | UiSlotAffordance::Invalid => { - "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_24px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-status-error-bg)_0%,var(--studio-status-error-bg)_34%,transparent_100%)] tw:px-2 tw:py-1.5" + "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_32px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-status-error-bg)_0%,var(--studio-status-error-bg)_34%,transparent_100%)] tw:px-2 tw:py-1.5" } UiSlotAffordance::Edited => { - "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_24px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-status-warning-bg)_0%,var(--studio-status-warning-bg)_34%,transparent_100%)] tw:px-2 tw:py-1.5" + "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_32px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-status-warning-bg)_0%,var(--studio-status-warning-bg)_34%,transparent_100%)] tw:px-2 tw:py-1.5" } UiSlotAffordance::Saving => { - "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_24px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-status-working-bg)_0%,var(--studio-status-working-bg)_34%,transparent_100%)] tw:px-2 tw:py-1.5" + "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_32px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-status-working-bg)_0%,var(--studio-status-working-bg)_34%,transparent_100%)] tw:px-2 tw:py-1.5" } UiSlotAffordance::Bound => { - "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_24px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-status-good-bg)_0%,var(--studio-status-good-bg)_34%,transparent_100%)] tw:px-2 tw:py-1.5" + "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_32px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-status-good-bg)_0%,var(--studio-status-good-bg)_34%,transparent_100%)] tw:px-2 tw:py-1.5" } UiSlotAffordance::Info if index % 2 == 0 => { - "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_24px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-color-surface-muted)_0%,var(--studio-color-surface-muted)_34%,transparent_100%)] tw:px-2 tw:py-1.5" + "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_32px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-color-surface-muted)_0%,var(--studio-color-surface-muted)_34%,transparent_100%)] tw:px-2 tw:py-1.5" } UiSlotAffordance::Info => { - "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_24px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-color-surface-subtle)_0%,var(--studio-color-surface-subtle)_34%,transparent_100%)] tw:px-2 tw:py-1.5" + "tw:grid tw:min-w-0 tw:grid-cols-[minmax(120px,0.4fr)_minmax(0,1fr)_32px] tw:items-center tw:gap-2 tw:bg-[linear-gradient(270deg,var(--studio-color-surface-subtle)_0%,var(--studio-color-surface-subtle)_34%,transparent_100%)] tw:px-2 tw:py-1.5" } } } diff --git a/lp-app/lpa-studio-web/src/app/node/slot_unit_display.rs b/lp-app/lpa-studio-web/src/app/node/slot_unit_display.rs index deeafb3c4..3a12dba0a 100644 --- a/lp-app/lpa-studio-web/src/app/node/slot_unit_display.rs +++ b/lp-app/lpa-studio-web/src/app/node/slot_unit_display.rs @@ -45,13 +45,13 @@ pub(crate) fn SlotUnitSuffix( fn unit_suffix_class(visible: bool, reserve: bool) -> &'static str { match (visible, reserve) { (true, true) => { - "tw:inline-flex tw:min-w-[2ch] tw:justify-start tw:text-xs tw:font-bold tw:text-subtle-foreground" + "tw:inline-flex tw:min-w-[2ch] tw:justify-start tw:text-xs tw:font-semibold tw:text-subtle-foreground" } (true, false) => { - "tw:inline-flex tw:justify-start tw:text-xs tw:font-bold tw:text-subtle-foreground" + "tw:inline-flex tw:justify-start tw:text-xs tw:font-semibold tw:text-subtle-foreground" } (false, true) => { - "tw:invisible tw:inline-flex tw:min-w-[2ch] tw:justify-start tw:text-xs tw:font-bold" + "tw:invisible tw:inline-flex tw:min-w-[2ch] tw:justify-start tw:text-xs tw:font-semibold" } (false, false) => "tw:hidden", } diff --git a/lp-app/lpa-studio-web/src/app/project/project_workspace.rs b/lp-app/lpa-studio-web/src/app/project/project_workspace.rs index e1bde8ada..8e03b7724 100644 --- a/lp-app/lpa-studio-web/src/app/project/project_workspace.rs +++ b/lp-app/lpa-studio-web/src/app/project/project_workspace.rs @@ -2,6 +2,7 @@ use dioxus::prelude::*; use lpa_studio_core::{ProjectEditorView, ProjectNodeStatusTone, ProjectNodeTreeItem, UiAction}; use crate::app::node::NodePane; +use crate::base::{StudioIcon, StudioIconName}; use crate::core::MetricGrid; #[component] @@ -103,17 +104,20 @@ fn ProjectNodeTreeItemView( running: bool, on_action: EventHandler, ) -> Element { + let focused = item.focused; let action = item.action.clone(); let children = item.children; - let class = if item.focused { - "tw:grid tw:w-full tw:grid-cols-[minmax(0,1fr)_auto_auto] tw:items-center tw:gap-2 tw:rounded-sm tw:border tw:border-accent-border tw:bg-status-good-bg tw:px-2 tw:py-1.5 tw:text-left" + let class = if focused { + "tw:grid tw:w-full tw:grid-cols-[18px_minmax(0,1fr)_auto] tw:items-center tw:gap-2 tw:rounded-sm tw:border tw:border-accent-border tw:bg-status-good-bg tw:px-2 tw:py-1.5 tw:text-left" } else { - "tw:grid tw:w-full tw:grid-cols-[minmax(0,1fr)_auto_auto] tw:items-center tw:gap-2 tw:rounded-sm tw:border tw:border-transparent tw:bg-transparent tw:px-2 tw:py-1.5 tw:text-left tw:hover:bg-card-muted" + "tw:grid tw:w-full tw:grid-cols-[18px_minmax(0,1fr)_auto] tw:items-center tw:gap-2 tw:rounded-sm tw:border tw:border-transparent tw:bg-transparent tw:px-2 tw:py-1.5 tw:text-left tw:hover:bg-card-muted" }; let indent = depth * 14; let status_class = node_status_class(item.status.tone); let status_label = item.status.label; let detail = item.status.detail; + let label = item.label; + let kind_label = item.kind; rsx! { li { @@ -122,9 +126,15 @@ fn ProjectNodeTreeItemView( r#type: "button", disabled: running, style: "padding-left: {indent}px;", + title: "{kind_label}", onclick: move |_| on_action.call(action.clone()), - span { class: "tw:min-w-0 tw:overflow-hidden tw:text-ellipsis tw:whitespace-nowrap tw:text-sm tw:text-soft-foreground", "{item.label}" } - span { class: "tw:text-xs tw:text-subtle-foreground", "{item.kind}" } + span { class: "tw:inline-flex tw:h-4 tw:w-4 tw:items-center tw:justify-center tw:text-subtle-foreground", + StudioIcon { + name: StudioIconName::NodeTreeItem, + size: 14, + } + } + span { class: "tw:min-w-0 tw:overflow-hidden tw:text-ellipsis tw:whitespace-nowrap tw:text-sm tw:text-soft-foreground", "{label}" } span { class: "{status_class}", "{status_label}" } } if let Some(detail) = detail.as_ref() { diff --git a/lp-app/lpa-studio-web/src/base/icon.rs b/lp-app/lpa-studio-web/src/base/icon.rs index 2a97d7e8d..ca646f799 100644 --- a/lp-app/lpa-studio-web/src/base/icon.rs +++ b/lp-app/lpa-studio-web/src/base/icon.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; use dioxus_icons::lucide::{ - Asterisk, Check, ChevronDown, ChevronRight, CircleAlert, CircleDot, CircleMinus, FlaskConical, - Info, Link2, Link2Off, Pencil, Play, SquareArrowRight, TriangleAlert, Usb, + Asterisk, Boxes, Check, ChevronDown, ChevronRight, CircleAlert, CircleDot, CircleMinus, + FlaskConical, Info, Link2, Link2Off, Pencil, Play, SquareArrowRight, TriangleAlert, Usb, }; #[component] @@ -20,6 +20,7 @@ pub fn StudioIcon(name: StudioIconName, size: u32) -> Element { StudioIconName::AssignedValue => rsx! { CircleDot { size } }, StudioIconName::BoundValue => rsx! { Link2 { size } }, StudioIconName::ChildValue => rsx! { SquareArrowRight { size } }, + StudioIconName::NodeTreeItem => rsx! { Boxes { size } }, StudioIconName::Edited => rsx! { Pencil { size } }, StudioIconName::Info => rsx! { Info { size } }, StudioIconName::InfoBare => rsx! { @@ -58,6 +59,7 @@ pub enum StudioIconName { AssignedValue, BoundValue, ChildValue, + NodeTreeItem, Edited, Info, InfoBare, diff --git a/lp-app/lpa-studio-web/src/base/icon_menu.rs b/lp-app/lpa-studio-web/src/base/icon_menu.rs index 36ed181ae..02d69a9a6 100644 --- a/lp-app/lpa-studio-web/src/base/icon_menu.rs +++ b/lp-app/lpa-studio-web/src/base/icon_menu.rs @@ -8,7 +8,7 @@ pub fn IconMenuButton( icon: StudioIconName, label: String, #[props(default = label.clone())] title: String, - #[props(default = 14)] icon_size: u32, + #[props(default = 16)] icon_size: u32, #[props(default = IconMenuTone::Neutral)] tone: IconMenuTone, #[props(default = PopoverPlacement::BottomEnd)] placement: PopoverPlacement, #[props(default = false)] active: bool, @@ -86,34 +86,34 @@ fn icon_menu_visual_class( fn icon_menu_class(tone: IconMenuTone, active: bool) -> &'static str { match (tone, active) { (IconMenuTone::Quiet, false) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-subtle tw:bg-terminal tw:p-0 tw:text-muted-foreground tw:transition-colors tw:hover:border-border-strong tw:hover:text-strong-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-subtle tw:bg-terminal tw:p-0 tw:text-muted-foreground tw:transition-colors tw:hover:border-border-strong tw:hover:text-strong-foreground" } (IconMenuTone::Quiet, true) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-subtle tw:bg-terminal tw:p-0 tw:text-muted-foreground tw:transition-colors tw:hover:border-border-strong tw:hover:text-strong-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-subtle tw:bg-terminal tw:p-0 tw:text-muted-foreground tw:transition-colors tw:hover:border-border-strong tw:hover:text-strong-foreground" } (IconMenuTone::Neutral, false) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-subtle tw:bg-page tw:p-0 tw:text-subtle-foreground tw:hover:border-border-strong tw:hover:text-muted-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-subtle tw:bg-page tw:p-0 tw:text-subtle-foreground tw:hover:border-border-strong tw:hover:text-muted-foreground" } (IconMenuTone::Neutral, true) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-card-muted tw:p-0 tw:text-muted-foreground tw:transition-colors tw:hover:text-strong-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-card-muted tw:p-0 tw:text-muted-foreground tw:transition-colors tw:hover:text-strong-foreground" } (IconMenuTone::Accent, false) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-subtle tw:bg-transparent tw:p-0 tw:text-subtle-foreground tw:transition-colors tw:hover:border-accent-border tw:hover:text-accent" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-subtle tw:bg-transparent tw:p-0 tw:text-subtle-foreground tw:transition-colors tw:hover:border-accent-border tw:hover:text-accent" } (IconMenuTone::Accent, true) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-accent-border tw:bg-transparent tw:p-0 tw:text-accent tw:transition-colors tw:hover:border-status-good-foreground tw:hover:text-status-good-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-accent-border tw:bg-transparent tw:p-0 tw:text-accent tw:transition-colors tw:hover:border-status-good-foreground tw:hover:text-status-good-foreground" } (IconMenuTone::Good, _) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-good-border tw:bg-status-good-bg tw:p-0 tw:text-status-good-foreground tw:transition-colors tw:hover:border-status-good-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-good-border tw:bg-status-good-bg tw:p-0 tw:text-status-good-foreground tw:transition-colors tw:hover:border-status-good-foreground" } (IconMenuTone::Working, _) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-working-border tw:bg-status-working-bg tw:p-0 tw:text-status-working-foreground tw:transition-colors tw:hover:border-status-working-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-working-border tw:bg-status-working-bg tw:p-0 tw:text-status-working-foreground tw:transition-colors tw:hover:border-status-working-foreground" } (IconMenuTone::Warning, _) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-warning-border tw:bg-status-warning-bg tw:p-0 tw:text-status-warning-foreground tw:transition-colors tw:hover:border-status-warning-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-warning-border tw:bg-status-warning-bg tw:p-0 tw:text-status-warning-foreground tw:transition-colors tw:hover:border-status-warning-foreground" } (IconMenuTone::Error, _) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-error-border tw:bg-status-error-bg tw:p-0 tw:text-status-error-foreground tw:transition-colors tw:hover:border-status-error-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-error-border tw:bg-status-error-bg tw:p-0 tw:text-status-error-foreground tw:transition-colors tw:hover:border-status-error-foreground" } } } @@ -121,31 +121,31 @@ fn icon_menu_class(tone: IconMenuTone, active: bool) -> &'static str { fn icon_menu_hover_class(tone: IconMenuTone, active: bool) -> &'static str { match (tone, active) { (IconMenuTone::Quiet, _) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-terminal tw:p-0 tw:text-strong-foreground tw:transition-colors" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-terminal tw:p-0 tw:text-strong-foreground tw:transition-colors" } (IconMenuTone::Neutral, false) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-page tw:p-0 tw:text-muted-foreground tw:transition-colors" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-page tw:p-0 tw:text-muted-foreground tw:transition-colors" } (IconMenuTone::Neutral, true) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-card-muted tw:p-0 tw:text-strong-foreground tw:transition-colors" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-card-muted tw:p-0 tw:text-strong-foreground tw:transition-colors" } (IconMenuTone::Accent, false) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-accent-border tw:bg-transparent tw:p-0 tw:text-accent tw:transition-colors" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-accent-border tw:bg-transparent tw:p-0 tw:text-accent tw:transition-colors" } (IconMenuTone::Accent, true) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-good-foreground tw:bg-transparent tw:p-0 tw:text-status-good-foreground tw:transition-colors" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-good-foreground tw:bg-transparent tw:p-0 tw:text-status-good-foreground tw:transition-colors" } (IconMenuTone::Good, _) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-good-foreground tw:bg-status-good-bg tw:p-0 tw:text-status-good-foreground tw:transition-colors" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-good-foreground tw:bg-status-good-bg tw:p-0 tw:text-status-good-foreground tw:transition-colors" } (IconMenuTone::Working, _) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-working-foreground tw:bg-status-working-bg tw:p-0 tw:text-status-working-foreground tw:transition-colors" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-working-foreground tw:bg-status-working-bg tw:p-0 tw:text-status-working-foreground tw:transition-colors" } (IconMenuTone::Warning, _) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-warning-foreground tw:bg-status-warning-bg tw:p-0 tw:text-status-warning-foreground tw:transition-colors" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-warning-foreground tw:bg-status-warning-bg tw:p-0 tw:text-status-warning-foreground tw:transition-colors" } (IconMenuTone::Error, _) => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-error-foreground tw:bg-status-error-bg tw:p-0 tw:text-status-error-foreground tw:transition-colors" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-error-foreground tw:bg-status-error-bg tw:p-0 tw:text-status-error-foreground tw:transition-colors" } } } @@ -153,25 +153,25 @@ fn icon_menu_hover_class(tone: IconMenuTone, active: bool) -> &'static str { fn icon_menu_open_class(tone: IconMenuTone) -> &'static str { match tone { IconMenuTone::Quiet => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-terminal tw:p-0 tw:text-strong-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-terminal tw:p-0 tw:text-strong-foreground" } IconMenuTone::Neutral => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-card-subtle tw:p-0 tw:text-strong-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-border-strong tw:bg-card-subtle tw:p-0 tw:text-strong-foreground" } IconMenuTone::Accent => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-accent-border tw:bg-transparent tw:p-0 tw:text-accent" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-accent-border tw:bg-transparent tw:p-0 tw:text-accent" } IconMenuTone::Good => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-good-border tw:bg-status-good-bg tw:p-0 tw:text-status-good-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-good-border tw:bg-status-good-bg tw:p-0 tw:text-status-good-foreground" } IconMenuTone::Working => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-working-border tw:bg-status-working-bg tw:p-0 tw:text-status-working-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-working-border tw:bg-status-working-bg tw:p-0 tw:text-status-working-foreground" } IconMenuTone::Warning => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-warning-border tw:bg-status-warning-bg tw:p-0 tw:text-status-warning-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-warning-border tw:bg-status-warning-bg tw:p-0 tw:text-status-warning-foreground" } IconMenuTone::Error => { - "tw:inline-flex tw:h-6 tw:w-6 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-error-border tw:bg-status-error-bg tw:p-0 tw:text-status-error-foreground" + "tw:inline-flex tw:h-8 tw:w-8 tw:items-center tw:justify-center tw:rounded-xs tw:border tw:border-status-error-border tw:bg-status-error-bg tw:p-0 tw:text-status-error-foreground" } } } diff --git a/lp-app/lpa-studio-web/src/style.css b/lp-app/lpa-studio-web/src/style.css index e6c3b80cb..7b8dd3ef3 100644 --- a/lp-app/lpa-studio-web/src/style.css +++ b/lp-app/lpa-studio-web/src/style.css @@ -1992,7 +1992,7 @@ select { gap: 0.4rem; color: var(--studio-color-text-subtle); font-size: 0.68rem; - font-weight: 700; + font-weight: 600; line-height: 1; cursor: pointer; user-select: none;