Archived
Render a single responsive workspace panel
This commit is contained in:
@@ -151,7 +151,7 @@ export class PiWebApp extends LitElement {
|
|||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private selectWorkspaceTool(tool: QualifiedContributionId) {
|
private openWorkspaceTool(tool: QualifiedContributionId) {
|
||||||
if (tool === "core:workspace.terminal") this.terminalAutoStartWorkspaceId = this.state.selectedWorkspace?.id;
|
if (tool === "core:workspace.terminal") this.terminalAutoStartWorkspaceId = this.state.selectedWorkspace?.id;
|
||||||
this.setState({ workspaceTool: tool, mainView: tool });
|
this.setState({ workspaceTool: tool, mainView: tool });
|
||||||
this.updateUrl();
|
this.updateUrl();
|
||||||
@@ -160,15 +160,12 @@ export class PiWebApp extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private selectMainView(view: AppState["mainView"]) {
|
private selectMainView(view: AppState["mainView"]) {
|
||||||
if (view === "navigation") {
|
if (view !== "navigation" && view !== "chat") {
|
||||||
this.setState({ mainView: view });
|
this.openWorkspaceTool(view);
|
||||||
this.updateUrl();
|
|
||||||
this.git.updatePolling();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({ mainView: view, workspaceTool: view === "chat" ? this.state.workspaceTool : view });
|
this.setState({ mainView: view });
|
||||||
this.updateUrl();
|
this.updateUrl();
|
||||||
if (view !== "chat") this.refreshSelectedWorkspaceTool(view);
|
|
||||||
this.git.updatePolling();
|
this.git.updatePolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,9 +233,9 @@ export class PiWebApp extends LitElement {
|
|||||||
if (tool === "core:workspace.git") void this.git.refreshGit();
|
if (tool === "core:workspace.git") void this.git.refreshGit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderWorkspacePanel(hideToolTabs = false) {
|
private renderWorkspacePanel() {
|
||||||
const workspaceLabelItems = this.state.selectedWorkspace === undefined ? [] : this.plugins.getWorkspaceLabelItems(this.state, this.state.selectedWorkspace);
|
const workspaceLabelItems = this.state.selectedWorkspace === undefined ? [] : this.plugins.getWorkspaceLabelItems(this.state, this.state.selectedWorkspace);
|
||||||
return html`<workspace-panel .workspace=${this.state.selectedWorkspace} .tool=${this.state.workspaceTool} .panels=${this.visibleWorkspacePanels()} .workspaceLabelItems=${workspaceLabelItems} .hideToolTabs=${hideToolTabs} .fileTree=${this.state.fileTree} .expandedDirs=${this.state.expandedDirs} .selectedFilePath=${this.state.selectedFilePath} .selectedFileContent=${this.state.selectedFileContent} .fileTreeStale=${this.state.fileTreeStale} .gitStatus=${this.state.gitStatus} .selectedDiffPath=${this.state.selectedDiffPath} .selectedDiff=${this.state.selectedDiff} .selectedStagedDiff=${this.state.selectedStagedDiff} .gitStale=${this.state.gitStale} .activeTerminalCount=${this.state.activeTerminalCount} .terminalAutoStart=${this.terminalAutoStartWorkspaceId === this.state.selectedWorkspace?.id} .onSelectTool=${(tool: QualifiedContributionId) => { this.selectWorkspaceTool(tool); }} .onRefreshFiles=${() => this.files.refreshFiles()} .onExpandDir=${(path: string) => this.files.expandDir(path)} .onSelectFile=${(path: string) => this.files.selectFile(path)} .onRefreshGit=${() => this.git.refreshGit()} .onSelectDiff=${(path: string) => this.git.selectDiff(path)}></workspace-panel>`;
|
return html`<workspace-panel .workspace=${this.state.selectedWorkspace} .tool=${this.state.workspaceTool} .panels=${this.visibleWorkspacePanels()} .workspaceLabelItems=${workspaceLabelItems} .fileTree=${this.state.fileTree} .expandedDirs=${this.state.expandedDirs} .selectedFilePath=${this.state.selectedFilePath} .selectedFileContent=${this.state.selectedFileContent} .fileTreeStale=${this.state.fileTreeStale} .gitStatus=${this.state.gitStatus} .selectedDiffPath=${this.state.selectedDiffPath} .selectedDiff=${this.state.selectedDiff} .selectedStagedDiff=${this.state.selectedStagedDiff} .gitStale=${this.state.gitStale} .activeTerminalCount=${this.state.activeTerminalCount} .terminalAutoStart=${this.terminalAutoStartWorkspaceId === this.state.selectedWorkspace?.id} .onSelectTool=${(tool: QualifiedContributionId) => { this.openWorkspaceTool(tool); }} .onRefreshFiles=${() => this.files.refreshFiles()} .onExpandDir=${(path: string) => this.files.expandDir(path)} .onSelectFile=${(path: string) => this.files.selectFile(path)} .onRefreshGit=${() => this.git.refreshGit()} .onSelectDiff=${(path: string) => this.git.selectDiff(path)}></workspace-panel>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderNavigationPanel(autoSwitchToChat: boolean) {
|
private renderNavigationPanel(autoSwitchToChat: boolean) {
|
||||||
@@ -314,7 +311,7 @@ export class PiWebApp extends LitElement {
|
|||||||
focusPrompt: () => { this.promptEditor?.focusInput(); },
|
focusPrompt: () => { this.promptEditor?.focusInput(); },
|
||||||
addProject: () => { this.setState({ projectDialogOpen: true }); },
|
addProject: () => { this.setState({ projectDialogOpen: true }); },
|
||||||
selectMainView: (view) => { this.selectMainView(view); },
|
selectMainView: (view) => { this.selectMainView(view); },
|
||||||
selectWorkspaceTool: (tool) => { this.selectWorkspaceTool(tool); },
|
selectWorkspaceTool: (tool) => { this.openWorkspaceTool(tool); },
|
||||||
refreshFiles: () => this.files.refreshFiles(),
|
refreshFiles: () => this.files.refreshFiles(),
|
||||||
refreshGit: () => this.git.refreshGit(),
|
refreshGit: () => this.git.refreshGit(),
|
||||||
startSession: () => this.withChatScrollTransition(() => this.sessions.startSession()),
|
startSession: () => this.withChatScrollTransition(() => this.sessions.startSession()),
|
||||||
@@ -373,14 +370,14 @@ export class PiWebApp extends LitElement {
|
|||||||
override render() {
|
override render() {
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
return html`
|
return html`
|
||||||
<div class="shell">
|
<div class=${`shell ${state.mainView === "navigation" ? "navigation-view" : state.mainView === "chat" ? "chat-view" : "workspace-view"}`}>
|
||||||
<aside>${this.renderNavigationPanel(false)}</aside>
|
<aside>${this.renderNavigationPanel(false)}</aside>
|
||||||
<main class=${state.mainView === "chat" ? "chat-view" : state.mainView === "navigation" ? "navigation-view" : "workspace-view"}>
|
<main class=${state.mainView === "chat" ? "chat-view" : state.mainView === "navigation" ? "navigation-view" : "workspace-view"}>
|
||||||
<div class="mobile-tabs">
|
<div class="mobile-tabs">
|
||||||
<button class=${state.mainView === "navigation" ? "mobile-navigation-tab selected" : "mobile-navigation-tab"} @click=${() => { this.selectMainView("navigation"); }}>Sessions</button>
|
<button class=${state.mainView === "navigation" ? "mobile-navigation-tab selected" : "mobile-navigation-tab"} @click=${() => { this.selectMainView("navigation"); }}>Sessions</button>
|
||||||
<button class=${state.mainView === "chat" ? "selected" : ""} @click=${() => { this.selectMainView("chat"); }}>Chat</button>
|
<button class=${state.mainView === "chat" ? "selected" : ""} @click=${() => { this.selectMainView("chat"); }}>Chat</button>
|
||||||
${this.visibleWorkspacePanels().map((panel) => html`
|
${this.visibleWorkspacePanels().map((panel) => html`
|
||||||
<button class=${state.mainView === panel.id ? "selected" : ""} @click=${() => { this.selectMainView(panel.id); }}>${this.renderMobilePanelTitle(panel)}</button>
|
<button class=${state.mainView === panel.id ? "selected" : ""} @click=${() => { this.openWorkspaceTool(panel.id); }}>${this.renderMobilePanelTitle(panel)}</button>
|
||||||
`)}
|
`)}
|
||||||
</div>
|
</div>
|
||||||
${state.error ? html`<div class="error">${state.error}</div>` : null}
|
${state.error ? html`<div class="error">${state.error}</div>` : null}
|
||||||
@@ -393,7 +390,6 @@ export class PiWebApp extends LitElement {
|
|||||||
${state.modelDialog !== undefined ? html`<command-picker title=${state.modelDialog.title} .searchable=${true} .options=${state.modelDialog.options} .selectedValue=${state.modelDialog.selectedValue} .onPick=${(value: string) => { void this.pickModel(value); }} .onCancel=${() => { this.setState({ modelDialog: undefined }); }}></command-picker>` : null}
|
${state.modelDialog !== undefined ? html`<command-picker title=${state.modelDialog.title} .searchable=${true} .options=${state.modelDialog.options} .selectedValue=${state.modelDialog.selectedValue} .onPick=${(value: string) => { void this.pickModel(value); }} .onCancel=${() => { this.setState({ modelDialog: undefined }); }}></command-picker>` : null}
|
||||||
${state.thinkingDialog !== undefined ? html`<command-picker title=${state.thinkingDialog.title} .options=${state.thinkingDialog.options} .selectedValue=${state.thinkingDialog.selectedValue} .onPick=${(value: string) => { void this.pickThinking(value); }} .onCancel=${() => { this.setState({ thinkingDialog: undefined }); }}></command-picker>` : null}
|
${state.thinkingDialog !== undefined ? html`<command-picker title=${state.thinkingDialog.title} .options=${state.thinkingDialog.options} .selectedValue=${state.thinkingDialog.selectedValue} .onPick=${(value: string) => { void this.pickThinking(value); }} .onCancel=${() => { this.setState({ thinkingDialog: undefined }); }}></command-picker>` : null}
|
||||||
` : html`<div class="empty">Select or start a session.</div>`}
|
` : html`<div class="empty">Select or start a session.</div>`}
|
||||||
<div class="mobile-panel">${this.renderWorkspacePanel(true)}</div>
|
|
||||||
</main>
|
</main>
|
||||||
${this.renderWorkspacePanel()}
|
${this.renderWorkspacePanel()}
|
||||||
${state.actionPaletteOpen ? html`<action-palette .actions=${this.getActions()} .onRun=${(actionId: string) => { this.setState({ actionPaletteOpen: false }); this.runAction(actionId); }} .onCancel=${() => { this.setState({ actionPaletteOpen: false }); }}></action-palette>` : null}
|
${state.actionPaletteOpen ? html`<action-palette .actions=${this.getActions()} .onRun=${(actionId: string) => { this.setState({ actionPaletteOpen: false }); this.runAction(actionId); }} .onCancel=${() => { this.setState({ actionPaletteOpen: false }); }}></action-palette>` : null}
|
||||||
|
|||||||
@@ -37,23 +37,26 @@ export const appStyles = css`
|
|||||||
session-list { flex: 1 1 auto; min-height: 0; overflow: auto; }
|
session-list { flex: 1 1 auto; min-height: 0; overflow: auto; }
|
||||||
main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
|
main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
|
||||||
.mobile-tabs { display: none; flex: 0 0 auto; gap: 6px; padding: 8px; border-bottom: 1px solid #30363d; overflow-x: auto; }
|
.mobile-tabs { display: none; flex: 0 0 auto; gap: 6px; padding: 8px; border-bottom: 1px solid #30363d; overflow-x: auto; }
|
||||||
.mobile-navigation-tab, .mobile-navigation-panel, .mobile-panel { display: none; }
|
.mobile-navigation-tab, .mobile-navigation-panel { display: none; }
|
||||||
.mobile-tabs button.selected { border-color: #58a6ff; background: #0d2847; }
|
.mobile-tabs button.selected { border-color: #58a6ff; background: #0d2847; }
|
||||||
.tab-badge { display: inline-block; min-width: 14px; margin-left: 4px; border: 1px solid #238636; border-radius: 999px; background: #0f2a16; color: #3fb950; padding: 0 5px; font-size: 11px; line-height: 16px; text-align: center; }
|
.tab-badge { display: inline-block; min-width: 14px; margin-left: 4px; border: 1px solid #238636; border-radius: 999px; background: #0f2a16; color: #3fb950; padding: 0 5px; font-size: 11px; line-height: 16px; text-align: center; }
|
||||||
workspace-panel { min-width: 0; min-height: 0; border-left: 1px solid #30363d; overflow: hidden; }
|
workspace-panel { min-width: 0; min-height: 0; border-left: 1px solid #30363d; overflow: hidden; }
|
||||||
@media (max-width: 1180px) {
|
@media (max-width: 1180px) {
|
||||||
.shell { grid-template-columns: 340px minmax(0, 1fr); }
|
.shell { grid-template-columns: 340px minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
|
||||||
.shell > workspace-panel { display: none; }
|
aside { grid-row: 1 / 3; }
|
||||||
|
main { grid-column: 2; grid-row: 1 / 3; }
|
||||||
.mobile-tabs { display: flex; }
|
.mobile-tabs { display: flex; }
|
||||||
|
.shell.workspace-view main { grid-row: 1; min-height: auto; }
|
||||||
|
.shell.workspace-view > workspace-panel { grid-column: 2; grid-row: 2; display: flex; border-left: 0; }
|
||||||
|
.shell:not(.workspace-view) > workspace-panel { display: none; }
|
||||||
main.workspace-view chat-view, main.workspace-view prompt-editor, main.workspace-view status-bar,
|
main.workspace-view chat-view, main.workspace-view prompt-editor, main.workspace-view status-bar,
|
||||||
main.workspace-view .empty { display: none; }
|
main.workspace-view .empty { display: none; }
|
||||||
main.chat-view .mobile-panel, main.navigation-view .mobile-panel { display: none; }
|
main.workspace-view { overflow: hidden; }
|
||||||
.mobile-panel { flex: 1 1 auto; min-height: 0; display: flex; }
|
|
||||||
.mobile-panel workspace-panel { flex: 1 1 auto; border-left: 0; }
|
|
||||||
}
|
}
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
.shell { grid-template-columns: minmax(0, 1fr); }
|
.shell { grid-template-columns: minmax(0, 1fr); }
|
||||||
aside { display: none; }
|
aside { display: none; }
|
||||||
|
main, .shell.workspace-view > workspace-panel { grid-column: 1; }
|
||||||
.mobile-navigation-tab { display: block; }
|
.mobile-navigation-tab { display: block; }
|
||||||
main.navigation-view chat-view, main.navigation-view prompt-editor, main.navigation-view status-bar,
|
main.navigation-view chat-view, main.navigation-view prompt-editor, main.navigation-view status-bar,
|
||||||
main.navigation-view .empty { display: none; }
|
main.navigation-view .empty { display: none; }
|
||||||
@@ -77,6 +80,7 @@ export const workspacePanelStyles = css`
|
|||||||
.panel-content { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
|
.panel-content { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
|
||||||
small, .muted { color: #8b949e; }
|
small, .muted { color: #8b949e; }
|
||||||
header small { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
header small { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
@media (max-width: 1180px) { .tabs { display: none; } }
|
||||||
.workspace-label { min-width: 0; display: inline-flex; align-items: baseline; gap: 5px; max-width: 100%; overflow: hidden; white-space: nowrap; }
|
.workspace-label { min-width: 0; display: inline-flex; align-items: baseline; gap: 5px; max-width: 100%; overflow: hidden; white-space: nowrap; }
|
||||||
.workspace-label-base, .workspace-label-item, .workspace-label-render { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
.workspace-label-base, .workspace-label-item, .workspace-label-render { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
||||||
.workspace-label-item, .workspace-label-render, .workspace-label-separator { color: #8b949e; }
|
.workspace-label-item, .workspace-label-render, .workspace-label-separator { color: #8b949e; }
|
||||||
|
|||||||
Reference in New Issue
Block a user