From c54c158a6b2c918fd788d7a46568b7502da12200 Mon Sep 17 00:00:00 2001 From: Wechat-ggGitHub <3164937577@qq.com> Date: Wed, 25 Mar 2026 21:34:30 +0800 Subject: [PATCH] feat: add tab:newtablayout setting for custom new tab layouts Allow users to define a custom block layout for new tabs via the `tab:newtablayout` setting in settings.json. This enables use cases like automatically opening a file browser alongside a terminal when creating a new tab. When not set, the default single-terminal layout is preserved. Example configuration for files + terminal split: { "tab:newtablayout": [ { "indexarr": [0], "blockdef": {"meta": {"view": "preview", "file": "~"}}, "size": 20 }, { "indexarr": [1], "blockdef": {"meta": {"view": "term", "controller": "shell"}}, "focused": true } ] } Co-Authored-By: Claude Opus 4.6 --- pkg/wconfig/settingsconfig.go | 1 + pkg/wcore/workspace.go | 20 +++++++++++++++++++- schema/settings.json | 22 ++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/pkg/wconfig/settingsconfig.go b/pkg/wconfig/settingsconfig.go index b55cab8cbf..89b71eb6cb 100644 --- a/pkg/wconfig/settingsconfig.go +++ b/pkg/wconfig/settingsconfig.go @@ -135,6 +135,7 @@ type SettingsType struct { PreviewDefaultSort string `json:"preview:defaultsort,omitempty" jsonschema:"enum=name,enum=modtime"` TabPreset string `json:"tab:preset,omitempty"` + TabNewTabLayout any `json:"tab:newtablayout,omitempty"` TabConfirmClose bool `json:"tab:confirmclose,omitempty"` TabBackground string `json:"tab:background,omitempty"` diff --git a/pkg/wcore/workspace.go b/pkg/wcore/workspace.go index c01e509a13..8d0586dd3f 100644 --- a/pkg/wcore/workspace.go +++ b/pkg/wcore/workspace.go @@ -5,6 +5,7 @@ package wcore import ( "context" + "encoding/json" "fmt" "log" "regexp" @@ -195,6 +196,23 @@ func getTabBackground() string { return config.Settings.TabPreset } +// getNewTabLayoutFromConfig returns the user-configured layout for new tabs, +// falling back to the default single-terminal layout if not configured. +func getNewTabLayoutFromConfig() PortableLayout { + settings := wconfig.GetWatcher().GetFullConfig() + rawLayout := settings.Settings.TabNewTabLayout + if rawLayout != nil { + barr, err := json.Marshal(rawLayout) + if err == nil { + var layout PortableLayout + if json.Unmarshal(barr, &layout) == nil && len(layout) > 0 { + return layout + } + } + } + return GetNewTabLayout() +} + var tabNameRe = regexp.MustCompile(`^T(\d+)$`) // getNextTabName returns the next auto-generated tab name (e.g. "T3") given a @@ -250,7 +268,7 @@ func CreateTab(ctx context.Context, workspaceId string, tabName string, activate // No need to apply an initial layout for the initial launch, since the starter layout will get applied after onboarding modal dismissal if !isInitialLaunch { - err = ApplyPortableLayout(ctx, tab.OID, GetNewTabLayout(), true) + err = ApplyPortableLayout(ctx, tab.OID, getNewTabLayoutFromConfig(), true) if err != nil { return tab.OID, fmt.Errorf("error applying new tab layout: %w", err) } diff --git a/schema/settings.json b/schema/settings.json index 67d8f5b9d4..75bd49f7f6 100644 --- a/schema/settings.json +++ b/schema/settings.json @@ -229,6 +229,28 @@ "tab:preset": { "type": "string" }, + "tab:newtablayout": { + "type": "array", + "description": "Custom block layout for new tabs. Each element defines a block with its position in the tile tree. If not set, defaults to a single terminal block.", + "items": { + "type": "object", + "properties": { + "indexarr": { + "type": "array", + "items": {"type": "integer"}, + "description": "Path in the tile tree (e.g. [0] = first child, [1] = second child, [1,1] = second child of second child)" + }, + "size": {"type": "integer", "description": "Relative size/weight of the block"}, + "blockdef": { + "type": "object", + "properties": {"meta": {"type": "object"}}, + "description": "Block definition with meta (view, controller, file, url, etc.)" + }, + "focused": {"type": "boolean", "description": "Whether this block should receive focus"} + }, + "required": ["indexarr", "blockdef"] + } + }, "tab:confirmclose": { "type": "boolean" },