Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
170e940
feat(web): give LastCode sidebar its own opt-in setting
lastobelus Oct 10, 2026
c29d978
fix(web): keep unsent drafts reachable in legacy sidebar
lastobelus Oct 10, 2026
94221c0
fix(web): preserve current legacy sidebar recovery behavior
lastobelus Oct 10, 2026
7f8950a
fix(web): preserve status marker size in LastCode sidebar
lastobelus Oct 10, 2026
1516ba6
docs(lastcode): register the renamed sidebar sources
lastobelus Oct 10, 2026
d444d7e
fix(web): preserve protected threads in legacy sidebar
lastobelus Oct 10, 2026
b925b68
fix(web): preserve legacy archive confirmation and recovery
lastobelus Oct 10, 2026
964ddc1
fix(web): preserve legacy deletion warnings and settings routes
lastobelus Oct 10, 2026
ee97787
fix(web): preserve keyboard actions in sidebar controls
lastobelus Oct 11, 2026
795d5eb
fix(web): clarify sidebar controls and protected rows
lastobelus Oct 11, 2026
5645fc2
fix(web): keep synthetic legacy groups consistent
lastobelus Oct 11, 2026
f1e2871
perf(web): load LastCode sidebar only when selected
lastobelus Oct 11, 2026
7009e63
refactor(web): separate LastCode sidebar menu policies
lastobelus Oct 11, 2026
21f0694
docs(web): name LastCode sidebar in action guidance
lastobelus Oct 11, 2026
9ad5cef
fix(web): skip overridden shortcut writes and clarify removal
lastobelus Oct 11, 2026
0f07f21
docs(web): explain optional LastCode sidebar and default
lastobelus Oct 11, 2026
dbe1c86
docs(web): remove obsolete legacy sidebar scaling plan
lastobelus Oct 11, 2026
96333f3
refactor(web): separate sidebar family graph phases
lastobelus Oct 11, 2026
67f1f67
refactor(web): simplify sidebar family aggregation and visibility
lastobelus Oct 11, 2026
5858f65
refactor(web): give sidebar notices and options their own state
lastobelus Oct 11, 2026
e8064e2
refactor(web): isolate sidebar project operations and dialog drafts
lastobelus Oct 11, 2026
75bc35d
refactor(web): separate sidebar thread lifecycle operations
lastobelus Oct 11, 2026
1eedf9b
refactor(web): give sidebar annotations a project owner
lastobelus Oct 11, 2026
a012277
refactor(web): separate sidebar menu targets and action dispatch
lastobelus Oct 11, 2026
fe09dcf
refactor(web): localize sidebar rename and archive controls
lastobelus Oct 11, 2026
23b1c39
refactor(web): separate sidebar source and process controls
lastobelus Oct 11, 2026
500ad39
refactor(web): give sidebar families and titles focused controls
lastobelus Oct 11, 2026
ac84872
fix(web): limit sidebar settings subscriptions to selected keys
lastobelus Oct 11, 2026
4581990
refactor(web): give sidebar metadata a focused grid owner
lastobelus Oct 11, 2026
f68e7b3
refactor(web): remove obsolete sidebar settings forwarding
lastobelus Oct 11, 2026
6eb8fea
refactor(web): isolate sidebar row identity and details lifetime
lastobelus Oct 11, 2026
6b09192
refactor(web): consolidate sidebar row activation and menu routing
lastobelus Oct 11, 2026
36ba46b
refactor(web): compose sidebar thread details
lastobelus Oct 11, 2026
90803bd
refactor(web): own project thread projections locally
lastobelus Oct 11, 2026
db25005
refactor(web): isolate project new thread control
lastobelus Oct 11, 2026
26b0689
test(web): preserve new thread control during refresh
lastobelus Oct 11, 2026
2d9dee8
refactor(web): compose project header and gestures
lastobelus Oct 11, 2026
f4106ec
refactor(web): isolate sidebar thread navigation
lastobelus Oct 11, 2026
565e8eb
refactor(web): localize sidebar row interaction lifetimes
lastobelus Oct 11, 2026
b01dcdb
refactor(web): isolate sidebar row surface
lastobelus Oct 11, 2026
d17b5cc
refactor(web): compose sidebar metadata controls
lastobelus Oct 11, 2026
de49c7e
refactor(web): share sidebar presentation inputs
lastobelus Oct 11, 2026
8358a9c
refactor(web): stabilize sidebar interaction capabilities
lastobelus Oct 11, 2026
9b15cc8
refactor(web): compose sidebar row layout and leading controls
lastobelus Oct 11, 2026
e12d8c2
refactor(web): isolate sidebar visit status and hover composition
lastobelus Oct 11, 2026
ec4065b
refactor(web): bind sidebar selection order to project navigation
lastobelus Oct 11, 2026
b745c96
refactor(web): compose the LastCode thread row in its own module
lastobelus Oct 11, 2026
6864aa9
refactor(web): give the LastCode thread list focused composition inputs
lastobelus Oct 11, 2026
69b5556
refactor(web): compose LastCode projects from focused owners
lastobelus Oct 11, 2026
8f7238c
refactor(web): keep project ordering in the sidebar list
lastobelus Oct 11, 2026
ad0e4d8
refactor(web): derive sidebar project groups in one place
lastobelus Oct 11, 2026
11062d8
refactor(web): separate sidebar routes and keyboard navigation
lastobelus Oct 11, 2026
e78b7a4
refactor(web): isolate sidebar environment metadata
lastobelus Oct 11, 2026
025421a
refactor(web): compose sidebar around one expansion owner
lastobelus Oct 11, 2026
5825c40
test(web): flatten sidebar behavior matrices
lastobelus Oct 11, 2026
beac1a2
refactor(web): clarify sidebar identity and rename decisions
lastobelus Oct 11, 2026
e344310
refactor(web): separate sidebar control intent from rendering
lastobelus Oct 11, 2026
f9381b7
refactor(web): use one sidebar shortcut ordering
lastobelus Oct 11, 2026
f7e4d78
refactor(web): clarify sidebar menu destinations and safeguards
lastobelus Oct 11, 2026
c15706b
refactor(web): clarify sidebar family status and cycle invariants
lastobelus Oct 11, 2026
55cf600
fix(web): preserve sidebar child titles in narrow rows
lastobelus Oct 11, 2026
4aa64da
fix(web): retain distinct compact subagent outcomes
lastobelus Oct 11, 2026
44e0dc6
fix(web): keep deep sidebar outcomes visible
lastobelus Oct 11, 2026
dff898f
fix(web): reserve sidebar titles at minimum width
lastobelus Oct 11, 2026
c3346d5
refactor(web): simplify sidebar mode and activation guards
lastobelus Oct 11, 2026
702deca
fix(web): retain sidebar status distinctions in narrow rows
lastobelus Oct 12, 2026
f9b4b0f
fix(web): reserve readable sidebar rename width
lastobelus Oct 12, 2026
f8f5640
fix(web): keep sidebar row component module private
lastobelus Oct 12, 2026
db38695
fix(web): preserve legacy row control-click menus
lastobelus Oct 12, 2026
a604656
test(web): notify sidebar setting and grant subscribers
lastobelus Oct 12, 2026
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LastCode:

- Tracks T3 Code nightly.
- Takes over the update UI and installs a daemon that checkpoints T3 Code nightly, then rebases LastCode's changes on top.
- Favors the legacy sidebar. We do not QA LastCode changes against the inbox sidebar, although fix pull requests are welcome.
- Uses the upstream inbox sidebar by default and offers an optional LastCode sidebar under Settings → LastCode → Appearance. The upstream legacy choice remains independent. Automated checks cover sidebar selection and selected LastCode workflows; exhaustive cross-sidebar and desktop/manual acceptance remain incomplete.
- Adds experimental features that increase both the security-sensitive surface area and the risk that agents could delete or mangle data on your machine. These changes have not been exhaustively reviewed for those risks. They include letting tools wake up threads, planned support for threads talking to each other, URL schemes that make emitted links clickable, and integrations with personal tools used by the maintainer, such as Markover.

Use LastCode with that additional risk in mind. For the smaller upstream surface and its supported release path, use [T3 Code](https://github.com/pingdotgg/t3code).
Expand Down
3 changes: 2 additions & 1 deletion apps/desktop/src/settings/DesktopClientSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ const clientSettings: ClientSettings = {
sidebarProjectSortOrder: "manual",
sidebarThreadSortOrder: "created_at",
sidebarThreadPreviewCount: 6,
legacySidebarEnabled: false,
legacySidebarEnabled: true,
lastcodeSidebarEnabled: true,
sidebarWorkingShelfEnabled: false,
loadBalancingEnabled: false,
loadBalancingWeights: { "environment-1": 75, "environment-2": 0 },
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src/settings/LastCodeSettingsImport.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ describe("LastCodeSettingsImport", () => {
Reflect.deleteProperty(sourceClient, "compactLegacySidebarStatuses");
Reflect.deleteProperty(sourceClient, "showThreadWorktreeIndicators");
Reflect.deleteProperty(sourceClient, "legacySidebarScale");
Reflect.deleteProperty(sourceClient, "lastcodeSidebarEnabled");
sourceClient.legacySidebarEnabled = true;
Reflect.deleteProperty(sourceClient, "roundedProjectIcons");
Reflect.deleteProperty(sourceClient, "showThreadProviderBadge");
Reflect.deleteProperty(sourceClient, "threadProviderBadgeSize");
Expand All @@ -111,6 +113,7 @@ describe("LastCodeSettingsImport", () => {
compactLegacySidebarStatuses: true,
showThreadWorktreeIndicators: false,
legacySidebarScale: 75,
lastcodeSidebarEnabled: true,
roundedProjectIcons: true,
environmentIconColors: { primary: "#2563eb", remote: "#7c3aed" },
showLocalEnvironmentIcon: true,
Expand Down Expand Up @@ -230,6 +233,8 @@ describe("LastCodeSettingsImport", () => {
assert.equal(importedClient.compactLegacySidebarStatuses, true);
assert.equal(importedClient.showThreadWorktreeIndicators, false);
assert.equal(importedClient.legacySidebarScale, 75);
assert.equal(importedClient.lastcodeSidebarEnabled, true);
assert.equal(importedClient.legacySidebarEnabled, true);
assert.equal(importedClient.roundedProjectIcons, true);
assert.deepEqual(importedClient.environmentIconColors, {
primary: "#2563eb",
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/settings/LastCodeSettingsImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ function mergeClientSettings(sourceRaw: string, destinationRaw: string | null):
compactLegacySidebarStatuses: destination.compactLegacySidebarStatuses,
environmentIconColors: destination.environmentIconColors,
legacySidebarScale: destination.legacySidebarScale,
lastcodeSidebarEnabled: destination.lastcodeSidebarEnabled,
roundedProjectIcons: destination.roundedProjectIcons,
showLocalEnvironmentIcon: destination.showLocalEnvironmentIcon,
showThreadProviderBadge: destination.showThreadProviderBadge,
Expand Down
46 changes: 30 additions & 16 deletions apps/web/src/components/AppSidebarLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useAtomValue } from "@effect/atom-react";
import * as Schema from "effect/Schema";
import {
lazy,
Suspense,
useEffect,
useState,
useSyncExternalStore,
Expand Down Expand Up @@ -33,10 +35,11 @@ import {
import {
useClientSettingsHydrated,
useEnvironmentIdentificationMode,
useLegacySidebarEnabled,
useSidebarMode,
useUpdateClientSettings,
} from "../hooks/useSettings";
import LegacyThreadSidebar from "./LegacySidebar";
import { toggleUpstreamSidebar } from "../sidebarMode";
import { useThreadVisitedMigration } from "../hooks/useThreadVisitedMigration";
import ThreadSidebar from "./Sidebar";
import { SettingsSidebarNav } from "./settings/SettingsSidebarNav";
Expand Down Expand Up @@ -64,6 +67,16 @@ import {
import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";

const MACOS_TRAFFIC_LIGHTS_LEFT_INSET = "var(--desktop-window-controls-inset, 90px)";
const LastCodeSidebar = lazy(() => import("./LastCodeSidebar"));
const THREAD_SIDEBARS = {
lastcode: (
<Suspense fallback={<SidebarChromeHeader isElectron={isElectron} />}>
<LastCodeSidebar />
</Suspense>
),
legacy: <LegacyThreadSidebar />,
inbox: <ThreadSidebar />,
};

function subscribeToViewportWidth(onChange: () => void): () => void {
window.addEventListener("resize", onChange);
Expand Down Expand Up @@ -91,6 +104,7 @@ function SidebarControl() {
const keybindings = useAtomValue(primaryServerKeybindingsAtom);
const { toggleSidebar } = useSidebar();
const updateClientSettings = useUpdateClientSettings();
const sidebarMode = useSidebarMode();
const clientSettingsHydrated = useClientSettingsHydrated();
const routeTarget = useParams({
strict: false,
Expand Down Expand Up @@ -144,18 +158,19 @@ function SidebarControl() {
modelPickerOpen: isModelPickerOpen(),
},
});
if (command !== "sidebar.toggle" && command !== "sidebar.mode.toggle") return;
if (command === "sidebar.mode.toggle" && !clientSettingsHydrated) return;

event.preventDefault();
event.stopPropagation();
if (command === "sidebar.toggle") {
toggleSidebar();
return;
switch (command) {
case "sidebar.toggle":
event.preventDefault();
event.stopPropagation();
toggleSidebar();
return;
case "sidebar.mode.toggle":
if (!clientSettingsHydrated) return;
event.preventDefault();
event.stopPropagation();
if (sidebarMode !== "lastcode") updateClientSettings(toggleUpstreamSidebar);
return;
}
updateClientSettings((settings) => ({
legacySidebarEnabled: !settings.legacySidebarEnabled,
}));
};

// Capture before focused editors consume commands such as Mod+B for rich-text formatting.
Expand All @@ -173,6 +188,7 @@ function SidebarControl() {
keybindings,
previewOpen,
terminalOpen,
sidebarMode,
toggleSidebar,
updateClientSettings,
usagePageOpen,
Expand Down Expand Up @@ -272,7 +288,7 @@ function ProjectProjectionRetention() {

export function AppSidebarLayout({ children }: { children: ReactNode }) {
const navigate = useNavigate();
const legacySidebarEnabled = useLegacySidebarEnabled();
const sidebarMode = useSidebarMode();
const { active: panelAnimationsActive, durationMs: panelAnimationDurationMs } =
usePanelAnimationSettings();
// Settings routes show the settings nav in place of whichever thread
Expand Down Expand Up @@ -382,10 +398,8 @@ export function AppSidebarLayout({ children }: { children: ReactNode }) {
<SidebarChromeHeader isElectron={isElectron} />
<SettingsSidebarNav pathname={pathname} />
</>
) : legacySidebarEnabled ? (
<LegacyThreadSidebar />
) : (
<ThreadSidebar />
THREAD_SIDEBARS[sidebarMode]
)}
<SidebarRail onDoubleClick={resetSidebarWidth} />
</Sidebar>
Expand Down
15 changes: 15 additions & 0 deletions apps/web/src/components/LastCodeSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { LastCodeSidebarContent } from "./sidebar/LastCodeSidebarContent";
import { LastCodeThreadActionsProvider } from "./sidebar/LastCodeThreadActions";
import { SidebarChromeFooter, SidebarChromeHeader } from "./sidebar/SidebarChrome";
import { isElectron } from "../env";

/** The lazy LastCode entry owns chrome and the single thread-operation provider. */
export default function LastCodeSidebar() {
return (
<LastCodeThreadActionsProvider>
<SidebarChromeHeader isElectron={isElectron} />
<LastCodeSidebarContent />
<SidebarChromeFooter />
</LastCodeThreadActionsProvider>
);
}
Loading
Loading