Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions cmd/server/main-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ func updateTelemetryCounts(lastCounts telemetrydata.TEventProps) telemetrydata.T
props.CountSSHConn = conncontroller.GetNumSSHHasConnected()
props.CountWSLConn = wslconn.GetNumWSLHasConnected()
props.CountViews, _ = wstore.DBGetBlockViewCounts(ctx)

fullConfig := wconfig.GetWatcher().GetFullConfig()
customWidgets := fullConfig.CountCustomWidgets()
customAIPresets := fullConfig.CountCustomAIPresets()
customSettings := wconfig.CountCustomSettings()

props.UserSet = &telemetrydata.TEventUserProps{
SettingsCustomWidgets: customWidgets,
SettingsCustomAIPresets: customAIPresets,
SettingsCustomSettings: customSettings,
}

Comment on lines +149 to +159
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential nil-pointer if the config watcher failed to initialize

wconfig.GetWatcher() can return nil (e.g., fsnotify initialization failure), leading to a panic on .GetFullConfig(). Add a nil-guard and fall back to wconfig.ReadFullConfig().

Apply this diff:

-	fullConfig := wconfig.GetWatcher().GetFullConfig()
-	customWidgets := fullConfig.CountCustomWidgets()
-	customAIPresets := fullConfig.CountCustomAIPresets()
-	customSettings := wconfig.CountCustomSettings()
+	watcher := wconfig.GetWatcher()
+	var fullConfig wconfig.FullConfigType
+	if watcher != nil {
+		fullConfig = watcher.GetFullConfig()
+	} else {
+		// fallback without watcher (e.g., fsnotify unavailable)
+		fullConfig = wconfig.ReadFullConfig()
+	}
+	customWidgets := fullConfig.CountCustomWidgets()
+	customAIPresets := fullConfig.CountCustomAIPresets()
+	customSettings := wconfig.CountCustomSettings()
 
 	props.UserSet = &telemetrydata.TEventUserProps{
 		SettingsCustomWidgets:   customWidgets,
 		SettingsCustomAIPresets: customAIPresets,
 		SettingsCustomSettings:  customSettings,
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fullConfig := wconfig.GetWatcher().GetFullConfig()
customWidgets := fullConfig.CountCustomWidgets()
customAIPresets := fullConfig.CountCustomAIPresets()
customSettings := wconfig.CountCustomSettings()
props.UserSet = &telemetrydata.TEventUserProps{
SettingsCustomWidgets: customWidgets,
SettingsCustomAIPresets: customAIPresets,
SettingsCustomSettings: customSettings,
}
watcher := wconfig.GetWatcher()
var fullConfig wconfig.FullConfigType
if watcher != nil {
fullConfig = watcher.GetFullConfig()
} else {
// fallback without watcher (e.g., fsnotify unavailable)
fullConfig = wconfig.ReadFullConfig()
}
customWidgets := fullConfig.CountCustomWidgets()
customAIPresets := fullConfig.CountCustomAIPresets()
customSettings := wconfig.CountCustomSettings()
props.UserSet = &telemetrydata.TEventUserProps{
SettingsCustomWidgets: customWidgets,
SettingsCustomAIPresets: customAIPresets,
SettingsCustomSettings: customSettings,
}
🤖 Prompt for AI Agents
In cmd/server/main-server.go around lines 149-159, guard against a nil
wconfig.GetWatcher() before calling .GetFullConfig(): retrieve watcher :=
wconfig.GetWatcher(); if watcher == nil, call fullConfig :=
wconfig.ReadFullConfig(); otherwise call fullConfig := watcher.GetFullConfig();
then compute customWidgets, customAIPresets and customSettings from that
fullConfig (use fullConfig.CountCustomWidgets(),
fullConfig.CountCustomAIPresets(), fullConfig.CountCustomSettings() or the
appropriate fullConfig methods) and populate props.UserSet as before so the code
won’t panic when the watcher failed to initialize.

if utilfn.CompareAsMarshaledJson(props, lastCounts) {
return lastCounts
}
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/telemetry.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Below is a list of the event types collected in the new telemetry system. More e
| `app:shutdown` | Logged on every shutdown |
| `app:activity` | Logged once per hour of app activity |
| `app:display` | Logged on startup and contains information about size of displays |
| `app:counts` | Logged once per hour when app is active, contains basic counts like number of windows, tabs, workspaces, blocks, etc. |
| `app:counts` | Logged once per hour when app is active, contains basic counts like number of windows, tabs, workspaces, blocks, number of settings customizations, etc. |
| `action:magnify` | Logged each time a block is magnified |
| `action:settabtheme` | Logged each time a tab theme is changed |
| `action:runaicmd` | Logged each time an AI request is made (no prompt information or text is sent), only sends "ai:backendtype" to know what type of AI backend is being used (OpenAI, Claude, Gemini, etc.) |
Expand Down
7 changes: 7 additions & 0 deletions frontend/types/gotypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -824,9 +824,13 @@ declare global {
"autoupdate:enabled"?: boolean;
"loc:countrycode"?: string;
"loc:regioncode"?: string;
"settings:customwidgets"?: number;
"settings:customaipresets"?: number;
"settings:customsettings"?: number;
"activity:activeminutes"?: number;
"activity:fgminutes"?: number;
"activity:openminutes"?: number;
"app:firstday"?: boolean;
"action:initiator"?: "keyboard" | "mouse";
Comment on lines +827 to 834
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Ensure the TS generator is updated, since this file is generated

This file is marked as generated. Please confirm cmd/generate/main-generatets.go (or related generator) includes these new fields to avoid losing them on the next regen.

Run this to verify generator references exist:


🏁 Script executed:

#!/bin/bash
# Look for new telemetry fields in the generator sources
rg -n --hidden -C2 -g 'cmd/generate/**' -e 'settings:customwidgets|settings:customaipresets|settings:customsettings|app:firstday'

Length of output: 280


It looks like the original ripgrep glob didn’t match any files. Let’s explicitly locate the TS generator and search it for the new fields:


🏁 Script executed:

#!/bin/bash
# 1. Locate the TypeScript generator file
gen_file=$(fd --hidden --full-path 'main-generatets.go' || echo "")

if [ -z "$gen_file" ]; then
  echo "⚠️ Generator file main-generatets.go not found under cmd/generate or elsewhere."
  exit 0
fi

echo "🔍 Found generator at: $gen_file"
echo "🔎 Searching for new telemetry fields in the generator…"

# 2. Search for the new telemetry keys
rg -n 'settings:customwidgets|settings:customaipresets|settings:customsettings|activity:activeminutes|activity:fgminutes|activity:openminutes|app:firstday|action:initiator' "$gen_file" || \
  echo "❌ No references to the new fields were found in the TS generator."

Length of output: 731


TS generator missing new telemetry fields

I verified that cmd/generatets/main-generatets.go does not reference the newly added keys in frontend/types/gotypes.d.ts. Without updating the generator, these fields will be dropped on the next regeneration. Please add support for the following telemetry keys in the TS generator:

  • settings:customwidgets
  • settings:customaipresets
  • settings:customsettings
  • activity:activeminutes
  • activity:fgminutes
  • activity:openminutes
  • app:firstday
  • action:initiator
🤖 Prompt for AI Agents
In frontend/types/gotypes.d.ts around lines 827-834, the TS generator
cmd/generatets/main-generatets.go is missing the newly added telemetry keys so
they will be dropped on regen; update the generator by adding entries for these
keys to the telemetry/field mapping (or schema list) with the correct types:
settings:customwidgets -> number, settings:customaipresets -> number,
settings:customsettings -> number, activity:activeminutes -> number,
activity:fgminutes -> number, activity:openminutes -> number, app:firstday ->
boolean, action:initiator -> union "keyboard" | "mouse"; after adding those
mappings, run the generator to confirm the keys appear in the produced
frontend/types/gotypes.d.ts.

"debug:panictype"?: string;
"block:view"?: string;
Expand Down Expand Up @@ -862,6 +866,9 @@ declare global {
"autoupdate:enabled"?: boolean;
"loc:countrycode"?: string;
"loc:regioncode"?: string;
"settings:customwidgets"?: number;
"settings:customaipresets"?: number;
"settings:customsettings"?: number;
};

// waveobj.Tab
Expand Down
30 changes: 30 additions & 0 deletions pkg/telemetry/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"encoding/json"
"fmt"
"log"
"sync/atomic"
"time"

"github.com/google/uuid"
Expand All @@ -18,6 +19,7 @@ import (
"github.com/wavetermdev/waveterm/pkg/util/dbutil"
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
"github.com/wavetermdev/waveterm/pkg/wavebase"
"github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wconfig"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wstore"
Expand All @@ -26,6 +28,25 @@ import (
const MaxTzNameLen = 50
const ActivityEventName = "app:activity"

var cachedTosAgreedTs atomic.Int64

func GetTosAgreedTs() int64 {
cached := cachedTosAgreedTs.Load()
if cached != 0 {
return cached
}

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
if err != nil || client == nil || client.TosAgreed == 0 {
return 0
}

cachedTosAgreedTs.Store(client.TosAgreed)
return client.TosAgreed
}
Comment on lines +33 to +48
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Make GetTosAgreedTs context-aware and avoid unconditional background work.

Use the caller's context so RecordTEvent's 2s budget is respected and cancellations propagate. Keep a bounded timeout only when ctx is nil.

-func GetTosAgreedTs() int64 {
+// GetTosAgreedTs returns the cached TosAgreed timestamp (unix ms) if available.
+// If not cached, it reads it from the store using the provided context. If ctx is nil,
+// a short timeout is used to avoid blocking telemetry paths.
+func GetTosAgreedTs(ctx context.Context) int64 {
 	cached := cachedTosAgreedTs.Load()
 	if cached != 0 {
 		return cached
 	}
 
-	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
-	defer cancel()
+	if ctx == nil {
+		var cancel context.CancelFunc
+		ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
+		defer cancel()
+	}
 	client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
 	if err != nil || client == nil || client.TosAgreed == 0 {
 		return 0
 	}
 
 	cachedTosAgreedTs.Store(client.TosAgreed)
 	return client.TosAgreed
 }

Follow-up: update the sole call site in RecordTEvent (see separate comment) to pass ctx.

Run this to confirm no other call sites exist and the signature update compiles cleanly:


🏁 Script executed:

#!/bin/bash
# Find calls to GetTosAgreedTs (old signature) that need ctx
rg -nP '\bGetTosAgreedTs\s*\(' -C2

Length of output: 696


Make GetTosAgreedTs context-aware and update its sole call site

To ensure timeouts and cancellations propagate from the caller, change GetTosAgreedTs to accept a context.Context and only synthesize a background timeout when none is provided. This is a breaking signature change—update the one call site in RecordTEvent to pass its ctx.

• File pkg/telemetry/telemetry.go, lines 33–48:

-func GetTosAgreedTs() int64 {
+// GetTosAgreedTs returns the cached TosAgreed timestamp (unix ms) if available.
// If not cached, it reads it from the store using the provided context.
// If ctx is nil, a 2s background timeout is used.
+func GetTosAgreedTs(ctx context.Context) int64 {
     cached := cachedTosAgreedTs.Load()
     if cached != 0 {
         return cached
     }

-    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
-    defer cancel()
+    if ctx == nil {
+        var cancel context.CancelFunc
+        ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
+        defer cancel()
+    }

     client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
     if err != nil || client == nil || client.TosAgreed == 0 {
         return 0
     }

     cachedTosAgreedTs.Store(client.TosAgreed)
     return client.TosAgreed
 }

• File pkg/telemetry/telemetry.go, around line 237 (in RecordTEvent):

-   tosAgreedTs := GetTosAgreedTs()
+   tosAgreedTs := GetTosAgreedTs(ctx)

With only this one call site, the change will compile cleanly once updated.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func GetTosAgreedTs() int64 {
cached := cachedTosAgreedTs.Load()
if cached != 0 {
return cached
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
if err != nil || client == nil || client.TosAgreed == 0 {
return 0
}
cachedTosAgreedTs.Store(client.TosAgreed)
return client.TosAgreed
}
// pkg/telemetry/telemetry.go
// GetTosAgreedTs returns the cached TosAgreed timestamp (unix ms) if available.
// If not cached, it reads it from the store using the provided context.
// If ctx is nil, a 2s background timeout is used.
func GetTosAgreedTs(ctx context.Context) int64 {
cached := cachedTosAgreedTs.Load()
if cached != 0 {
return cached
}
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
if ctx == nil {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
}
client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
if err != nil || client == nil || client.TosAgreed == 0 {
return 0
}
cachedTosAgreedTs.Store(client.TosAgreed)
return client.TosAgreed
}
Suggested change
func GetTosAgreedTs() int64 {
cached := cachedTosAgreedTs.Load()
if cached != 0 {
return cached
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
if err != nil || client == nil || client.TosAgreed == 0 {
return 0
}
cachedTosAgreedTs.Store(client.TosAgreed)
return client.TosAgreed
}
// pkg/telemetry/telemetry.go, inside RecordTEvent (around original line 237)
tosAgreedTs := GetTosAgreedTs(ctx)
🤖 Prompt for AI Agents
In pkg/telemetry/telemetry.go around lines 33–48 change GetTosAgreedTs to accept
a context.Context parameter (breaking change) and use that context when calling
wstore.DBGetSingleton; if the passed ctx is nil, synthesize a background context
with a 2s timeout (use context.WithTimeout(context.Background(), 2*time.Second)
and defer cancel), otherwise use the caller's ctx directly so
timeouts/cancellations propagate; also update the single caller in
pkg/telemetry/telemetry.go around line 237 (RecordTEvent) to pass its ctx into
the new GetTosAgreedTs(ctx) signature so the code compiles.


type ActivityType struct {
Day string `json:"day"`
Uploaded bool `json:"-"`
Expand Down Expand Up @@ -135,6 +156,7 @@ func updateActivityTEvent(ctx context.Context, tevent *telemetrydata.TEvent) err
eventTs := time.Now()
// compute to hour boundary, and round up to next hour
eventTs = eventTs.Truncate(time.Hour).Add(time.Hour)

return wstore.WithTx(ctx, func(tx *wstore.TxWrap) error {
// find event that matches this timestamp with event name "app:activity"
var hasRow bool
Expand All @@ -150,6 +172,7 @@ func updateActivityTEvent(ctx context.Context, tevent *telemetrydata.TEvent) err
}
}
mergeActivity(&curActivity, tevent.Props)

if hasRow {
query := `UPDATE db_tevent SET props = ? WHERE uuid = ?`
tx.Exec(query, dbutil.QuickJson(curActivity), uuidStr)
Expand Down Expand Up @@ -209,6 +232,13 @@ func RecordTEvent(ctx context.Context, tevent *telemetrydata.TEvent) error {
return err
}
tevent.EnsureTimestamps()

// Set AppFirstDay if within first day of TOS agreement
tosAgreedTs := GetTosAgreedTs()
if tosAgreedTs == 0 || (tosAgreedTs != 0 && time.Now().UnixMilli()-tosAgreedTs <= int64(24*60*60*1000)) {
tevent.Props.AppFirstDay = true
}

if tevent.Event == ActivityEventName {
return updateActivityTEvent(ctx, tevent)
}
Expand Down
16 changes: 12 additions & 4 deletions pkg/telemetry/telemetrydata/telemetrydata.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,16 @@ type TEventUserProps struct {
ClientBuildTime string `json:"client:buildtime,omitempty"`
ClientOSRelease string `json:"client:osrelease,omitempty"`
ClientIsDev bool `json:"client:isdev,omitempty"`
AutoUpdateChannel string `json:"autoupdate:channel,omitempty"`
AutoUpdateEnabled bool `json:"autoupdate:enabled,omitempty"`
LocCountryCode string `json:"loc:countrycode,omitempty"`
LocRegionCode string `json:"loc:regioncode,omitempty"`

AutoUpdateChannel string `json:"autoupdate:channel,omitempty"`
AutoUpdateEnabled bool `json:"autoupdate:enabled,omitempty"`

LocCountryCode string `json:"loc:countrycode,omitempty"`
LocRegionCode string `json:"loc:regioncode,omitempty"`

SettingsCustomWidgets int `json:"settings:customwidgets,omitempty"`
SettingsCustomAIPresets int `json:"settings:customaipresets,omitempty"`
SettingsCustomSettings int `json:"settings:customsettings,omitempty"`
}

type TEventProps struct {
Expand All @@ -64,6 +70,8 @@ type TEventProps struct {
FgMinutes int `json:"activity:fgminutes,omitempty"`
OpenMinutes int `json:"activity:openminutes,omitempty"`

AppFirstDay bool `json:"app:firstday,omitempty"`

ActionInitiator string `json:"action:initiator,omitempty" tstype:"\"keyboard\" | \"mouse\""`
PanicType string `json:"debug:panictype,omitempty"`
BlockView string `json:"block:view,omitempty"`
Expand Down
2 changes: 1 addition & 1 deletion pkg/waveobj/wtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ type Client struct {
Version int `json:"version"`
WindowIds []string `json:"windowids"`
Meta MetaMapType `json:"meta"`
TosAgreed int64 `json:"tosagreed,omitempty"`
TosAgreed int64 `json:"tosagreed,omitempty"` // unix milli
HasOldHistory bool `json:"hasoldhistory,omitempty"`
TempOID string `json:"tempoid,omitempty"`
}
Expand Down
45 changes: 45 additions & 0 deletions pkg/wconfig/settingsconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,3 +741,48 @@ type TermThemeType struct {
Background string `json:"background"`
Cursor string `json:"cursor"`
}

// CountCustomWidgets returns the number of custom widgets the user has defined.
// Custom widgets are identified as widgets whose ID doesn't start with "defwidget@".
func (fc *FullConfigType) CountCustomWidgets() int {
count := 0
for widgetID := range fc.Widgets {
if !strings.HasPrefix(widgetID, "defwidget@") {
count++
}
}
return count
}

// CountCustomAIPresets returns the number of custom AI presets the user has defined.
// Custom AI presets are identified as presets that start with "ai@" but aren't "ai@global" or "ai@wave".
func (fc *FullConfigType) CountCustomAIPresets() int {
count := 0
for presetID := range fc.Presets {
if strings.HasPrefix(presetID, "ai@") && presetID != "ai@global" && presetID != "ai@wave" {
count++
}
}
return count
}

// CountCustomSettings returns the number of settings in the user's settings file.
// This excludes telemetry:enabled which doesn't count as a customization.
func CountCustomSettings() int {
// Load user settings
userSettings, _ := ReadWaveHomeConfigFile("settings.json")
if userSettings == nil {
return 0
}

// Count all keys except telemetry:enabled
count := 0
for key := range userSettings {
if key == "telemetry:enabled" {
continue
}
count++
}

return count
}
46 changes: 27 additions & 19 deletions pkg/wcore/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ func createSubBlockObj(ctx context.Context, parentBlockId string, blockDef *wave
}

func CreateBlock(ctx context.Context, tabId string, blockDef *waveobj.BlockDef, rtOpts *waveobj.RuntimeOpts) (rtnBlock *waveobj.Block, rtnErr error) {
return CreateBlockWithTelemetry(ctx, tabId, blockDef, rtOpts, true)
}

func CreateBlockWithTelemetry(ctx context.Context, tabId string, blockDef *waveobj.BlockDef, rtOpts *waveobj.RuntimeOpts, recordTelemetry bool) (rtnBlock *waveobj.Block, rtnErr error) {
var blockCreated bool
var newBlockOID string
defer func() {
Expand Down Expand Up @@ -94,29 +98,33 @@ func CreateBlock(ctx context.Context, tabId string, blockDef *waveobj.BlockDef,
}
}
}
go func() {
defer func() {
panichandler.PanicHandler("CreateBlock:telemetry", recover())
}()
if recordTelemetry {
blockView := blockDef.Meta.GetString(waveobj.MetaKey_View, "")
if blockView == "" {
return
}
tctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
defer cancelFn()
telemetry.UpdateActivity(tctx, wshrpc.ActivityUpdate{
Renderers: map[string]int{blockView: 1},
})
telemetry.RecordTEvent(tctx, &telemetrydata.TEvent{
Event: "action:createblock",
Props: telemetrydata.TEventProps{
BlockView: blockView,
},
})
}()
go recordBlockCreationTelemetry(blockView)
}
return blockData, nil
}

func recordBlockCreationTelemetry(blockView string) {
defer func() {
panichandler.PanicHandler("CreateBlock:telemetry", recover())
}()
if blockView == "" {
return
}
tctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
defer cancelFn()
telemetry.UpdateActivity(tctx, wshrpc.ActivityUpdate{
Renderers: map[string]int{blockView: 1},
})
telemetry.RecordTEvent(tctx, &telemetrydata.TEvent{
Event: "action:createblock",
Props: telemetrydata.TEventProps{
BlockView: blockView,
},
})
}

func createBlockObj(ctx context.Context, tabId string, blockDef *waveobj.BlockDef, rtOpts *waveobj.RuntimeOpts) (*waveobj.Block, error) {
return wstore.WithTxRtn(ctx, func(tx *wstore.TxWrap) (*waveobj.Block, error) {
tab, _ := wstore.DBGet[*waveobj.Tab](tx.Context(), tabId)
Expand Down
7 changes: 3 additions & 4 deletions pkg/wcore/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,13 @@ func QueueLayoutActionForTab(ctx context.Context, tabId string, actions ...waveo
return QueueLayoutAction(ctx, layoutStateId, actions...)
}

func ApplyPortableLayout(ctx context.Context, tabId string, layout PortableLayout) error {
log.Printf("ApplyPortableLayout, tabId: %s, layout: %v\n", tabId, layout)
func ApplyPortableLayout(ctx context.Context, tabId string, layout PortableLayout, recordTelemetry bool) error {
actions := make([]waveobj.LayoutActionData, len(layout)+1)
actions[0] = waveobj.LayoutActionData{ActionType: LayoutActionDataType_ClearTree}
for i := 0; i < len(layout); i++ {
layoutAction := layout[i]

blockData, err := CreateBlock(ctx, tabId, layoutAction.BlockDef, &waveobj.RuntimeOpts{})
blockData, err := CreateBlockWithTelemetry(ctx, tabId, layoutAction.BlockDef, &waveobj.RuntimeOpts{}, recordTelemetry)
if err != nil {
return fmt.Errorf("unable to create block to apply portable layout to tab %s: %w", tabId, err)
}
Expand Down Expand Up @@ -184,7 +183,7 @@ func BootstrapStarterLayout(ctx context.Context) error {

starterLayout := GetStarterLayout()

err = ApplyPortableLayout(ctx, tabId, starterLayout)
err = ApplyPortableLayout(ctx, tabId, starterLayout, false)
if err != nil {
return fmt.Errorf("error applying starter layout: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/wcore/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func DeleteWorkspace(ctx context.Context, workspaceId string, force bool) (bool,
return false, "", fmt.Errorf("error closing tab: %w", err)
}
}
windowId, err := wstore.DBFindWindowForWorkspaceId(ctx, workspaceId)
windowId, _ := wstore.DBFindWindowForWorkspaceId(ctx, workspaceId)
err = wstore.DBDelete(ctx, waveobj.OType_Workspace, workspaceId)
if err != nil {
return false, "", fmt.Errorf("error deleting workspace: %w", err)
Expand Down Expand Up @@ -221,7 +221,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 TOS modal dismissal
if !isInitialLaunch {
err = ApplyPortableLayout(ctx, tab.OID, GetNewTabLayout())
err = ApplyPortableLayout(ctx, tab.OID, GetNewTabLayout(), true)
if err != nil {
return tab.OID, fmt.Errorf("error applying new tab layout: %w", err)
}
Expand Down
Loading