From c309664a3dab292131c4222cd4ebe38c579a84a4 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 10:12:57 +0200 Subject: [PATCH 01/22] feat: switch terminal fonts --- site/package.json | 1 + site/pnpm-lock.yaml | 8 ++++++++ site/src/pages/TerminalPage/TerminalPage.tsx | 4 ++-- site/src/theme/constants.ts | 5 +++++ site/src/theme/globalFonts.ts | 3 +++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/site/package.json b/site/package.json index d9fd1fbec2b05..bc40e094df28c 100644 --- a/site/package.json +++ b/site/package.json @@ -42,6 +42,7 @@ "@emotion/styled": "11.14.0", "@fastly/performance-observer-polyfill": "2.0.0", "@fontsource-variable/inter": "5.1.1", + "@fontsource/fira-code": "5.2.5", "@fontsource/ibm-plex-mono": "5.1.1", "@monaco-editor/react": "4.6.0", "@mui/icons-material": "5.16.14", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 55c4c955da4d9..3b7ff150e36ef 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -40,6 +40,9 @@ importers: '@fontsource-variable/inter': specifier: 5.1.1 version: 5.1.1 + '@fontsource/fira-code': + specifier: 5.2.5 + version: 5.2.5 '@fontsource/ibm-plex-mono': specifier: 5.1.1 version: 5.1.1 @@ -1034,6 +1037,9 @@ packages: '@fontsource-variable/inter@5.1.1': resolution: {integrity: sha512-OpXFTmiH6tHkYijMvQTycFKBLK4X+SRV6tet1m4YOUH7SzIIlMqDja+ocDtiCA72UthBH/vF+3ZtlMr2rN/wIw==, tarball: https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.1.1.tgz} + '@fontsource/fira-code@5.2.5': + resolution: {integrity: sha512-Rn9PJoyfRr5D6ukEhZpzhpD+rbX2rtoz9QjkOuGxqFxrL69fQvhadMUBxQIOuTF4sTTkPRSKlAEpPjTKaI12QA==, tarball: https://registry.npmjs.org/@fontsource/fira-code/-/fira-code-5.2.5.tgz} + '@fontsource/ibm-plex-mono@5.1.1': resolution: {integrity: sha512-1aayqPe/ZkD3MlvqpmOHecfA3f2B8g+fAEkgvcCd3lkPP0pS1T0xG5Zmn2EsJQqr1JURtugPUH+5NqvKyfFZMQ==, tarball: https://registry.npmjs.org/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.1.1.tgz} @@ -6954,6 +6960,8 @@ snapshots: '@fontsource-variable/inter@5.1.1': {} + '@fontsource/fira-code@5.2.5': {} + '@fontsource/ibm-plex-mono@5.1.1': {} '@humanwhocodes/config-array@0.11.14': diff --git a/site/src/pages/TerminalPage/TerminalPage.tsx b/site/src/pages/TerminalPage/TerminalPage.tsx index c86a3f9ed5396..d1ad415fb48e0 100644 --- a/site/src/pages/TerminalPage/TerminalPage.tsx +++ b/site/src/pages/TerminalPage/TerminalPage.tsx @@ -18,7 +18,7 @@ import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import themes from "theme"; -import { MONOSPACE_FONT_FAMILY } from "theme/constants"; +import { MONOSPACE_FONT_FAMILY, TERMINAL_FONT_FAMILY_2 } from "theme/constants"; import { pageTitle } from "utils/page"; import { openMaybePortForwardedURL } from "utils/portForward"; import { terminalWebsocketUrl } from "utils/terminal"; @@ -110,7 +110,7 @@ const TerminalPage: FC = () => { allowProposedApi: true, allowTransparency: true, disableStdin: false, - fontFamily: MONOSPACE_FONT_FAMILY, + fontFamily: TERMINAL_FONT_FAMILY_2, fontSize: 16, theme: { background: theme.palette.background.default, diff --git a/site/src/theme/constants.ts b/site/src/theme/constants.ts index b95998640efde..63092b0f99650 100644 --- a/site/src/theme/constants.ts +++ b/site/src/theme/constants.ts @@ -1,6 +1,11 @@ export const borderRadius = 8; export const MONOSPACE_FONT_FAMILY = "'IBM Plex Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'Liberation Mono', 'Monaco', 'Courier New', Courier, monospace"; +export const TERMINAL_FONT_FAMILY_1 = MONOSPACE_FONT_FAMILY; +export const TERMINAL_FONT_FAMILY_2 = MONOSPACE_FONT_FAMILY.replace( + "IBM Plex Mono", + "Fira Code", +); export const BODY_FONT_FAMILY = `"Inter Variable", system-ui, sans-serif`; export const navHeight = 62; export const containerWidth = 1380; diff --git a/site/src/theme/globalFonts.ts b/site/src/theme/globalFonts.ts index 24371dd57568e..db8089f9db266 100644 --- a/site/src/theme/globalFonts.ts +++ b/site/src/theme/globalFonts.ts @@ -3,3 +3,6 @@ import "@fontsource/ibm-plex-mono/400.css"; import "@fontsource/ibm-plex-mono/600.css"; // Main body copy font import "@fontsource-variable/inter"; +// Alternative font for Terminal +import "@fontsource/fira-code/400.css"; +import "@fontsource/fira-code/600.css"; From d9e9627e7d038bf6dd176939f30465e6b6cbc3fa Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 10:46:11 +0200 Subject: [PATCH 02/22] Rename GetAppearanceSettings to GetUserThemePreferences --- .../TestProvisioners_Golden/list.golden | 2 +- cli/testdata/coder_provisioner_list.golden | 2 +- coderd/apidoc/docs.go | 36 ++++++++ coderd/apidoc/swagger.json | 27 +++++- coderd/database/dbauthz/dbauthz.go | 44 +++++----- coderd/database/dbauthz/dbauthz_test.go | 8 +- coderd/database/dbmem/dbmem.go | 82 ++++++++--------- coderd/database/dbmetrics/querymetrics.go | 28 +++--- coderd/database/dbmock/dbmock.go | 60 ++++++------- coderd/database/querier.go | 4 +- coderd/database/queries.sql.go | 88 +++++++++---------- coderd/database/queries/users.sql | 4 +- coderd/users.go | 4 +- codersdk/users.go | 15 +++- docs/reference/api/schemas.md | 46 ++++++++-- docs/reference/api/users.md | 3 + .../terraform/testdata/resources/version.txt | 2 +- site/site.go | 2 +- site/src/api/typesGenerated.ts | 11 +++ 19 files changed, 294 insertions(+), 174 deletions(-) diff --git a/cli/testdata/TestProvisioners_Golden/list.golden b/cli/testdata/TestProvisioners_Golden/list.golden index 3f50f90746744..35844d8b9c50e 100644 --- a/cli/testdata/TestProvisioners_Golden/list.golden +++ b/cli/testdata/TestProvisioners_Golden/list.golden @@ -1,4 +1,4 @@ -ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION +ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION 00000000-0000-0000-aaaa-000000000000 ====[timestamp]===== ====[timestamp]===== default-provisioner v0.0.0-devel map[owner: scope:organization] built-in idle 00000000-0000-0000-bbbb-000000000001 succeeded Coder 00000000-0000-0000-aaaa-000000000001 ====[timestamp]===== ====[timestamp]===== provisioner-1 v0.0.0 map[foo:bar owner: scope:organization] built-in busy 00000000-0000-0000-bbbb-000000000002 running Coder 00000000-0000-0000-aaaa-000000000002 ====[timestamp]===== ====[timestamp]===== provisioner-2 v0.0.0 map[owner: scope:organization] built-in offline 00000000-0000-0000-bbbb-000000000003 succeeded Coder diff --git a/cli/testdata/coder_provisioner_list.golden b/cli/testdata/coder_provisioner_list.golden index 64941eebf5b89..e34db5605fd81 100644 --- a/cli/testdata/coder_provisioner_list.golden +++ b/cli/testdata/coder_provisioner_list.golden @@ -1,2 +1,2 @@ -CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS +CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS ====[timestamp]===== ====[timestamp]===== built-in test v0.0.0-devel idle map[owner: scope:organization] diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index c93af6a64a41c..bc11caf62facf 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -15595,6 +15595,19 @@ const docTemplate = `{ "TemplateVersionWarningUnsupportedWorkspaces" ] }, + "codersdk.TerminalFontName": { + "type": "string", + "enum": [ + "", + "ibm-mono-plex", + "fira-code" + ], + "x-enum-varnames": [ + "TerminalFontUnknown", + "TerminalFontIbmMonoPlex", + "TerminalFontFiraCode" + ] + }, "codersdk.TimingStage": { "type": "string", "enum": [ @@ -15768,9 +15781,21 @@ const docTemplate = `{ "codersdk.UpdateUserAppearanceSettingsRequest": { "type": "object", "required": [ + "terminal_font", "theme_preference" ], "properties": { + "terminal_font": { + "enum": [ + "ibm-mono-plex", + "fira-code" + ], + "allOf": [ + { + "$ref": "#/definitions/codersdk.TerminalFontName" + } + ] + }, "theme_preference": { "type": "string" } @@ -16062,6 +16087,17 @@ const docTemplate = `{ "codersdk.UserAppearanceSettings": { "type": "object", "properties": { + "terminal_font": { + "enum": [ + "ibm-mono-plex", + "fira-code" + ], + "allOf": [ + { + "$ref": "#/definitions/codersdk.TerminalFontName" + } + ] + }, "theme_preference": { "type": "string" } diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index da4d7a4fcf41c..a9cdae584b627 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -14180,6 +14180,15 @@ "enum": ["UNSUPPORTED_WORKSPACES"], "x-enum-varnames": ["TemplateVersionWarningUnsupportedWorkspaces"] }, + "codersdk.TerminalFontName": { + "type": "string", + "enum": ["", "ibm-mono-plex", "fira-code"], + "x-enum-varnames": [ + "TerminalFontUnknown", + "TerminalFontIbmMonoPlex", + "TerminalFontFiraCode" + ] + }, "codersdk.TimingStage": { "type": "string", "enum": [ @@ -14350,8 +14359,16 @@ }, "codersdk.UpdateUserAppearanceSettingsRequest": { "type": "object", - "required": ["theme_preference"], + "required": ["terminal_font", "theme_preference"], "properties": { + "terminal_font": { + "enum": ["ibm-mono-plex", "fira-code"], + "allOf": [ + { + "$ref": "#/definitions/codersdk.TerminalFontName" + } + ] + }, "theme_preference": { "type": "string" } @@ -14617,6 +14634,14 @@ "codersdk.UserAppearanceSettings": { "type": "object", "properties": { + "terminal_font": { + "enum": ["ibm-mono-plex", "fira-code"], + "allOf": [ + { + "$ref": "#/definitions/codersdk.TerminalFontName" + } + ] + }, "theme_preference": { "type": "string" } diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 3815f713c0f4e..e496eda90ea54 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1061,6 +1061,28 @@ func (q *querier) customRoleCheck(ctx context.Context, role database.CustomRole) return nil } +func (q *querier) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) { + u, err := q.db.GetUserByID(ctx, userID) + if err != nil { + return "", err + } + if err := q.authorizeContext(ctx, policy.ActionReadPersonal, u); err != nil { + return "", err + } + return q.db.GetUserThemePreference(ctx, userID) +} + +func (q *querier) UpdateUserThemePreference(ctx context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { + u, err := q.db.GetUserByID(ctx, arg.UserID) + if err != nil { + return database.UserConfig{}, err + } + if err := q.authorizeContext(ctx, policy.ActionUpdatePersonal, u); err != nil { + return database.UserConfig{}, err + } + return q.db.UpdateUserThemePreference(ctx, arg) +} + func (q *querier) AcquireLock(ctx context.Context, id int64) error { return q.db.AcquireLock(ctx, id) } @@ -2711,17 +2733,6 @@ func (q *querier) GetUserActivityInsights(ctx context.Context, arg database.GetU return q.db.GetUserActivityInsights(ctx, arg) } -func (q *querier) GetUserAppearanceSettings(ctx context.Context, userID uuid.UUID) (string, error) { - u, err := q.db.GetUserByID(ctx, userID) - if err != nil { - return "", err - } - if err := q.authorizeContext(ctx, policy.ActionReadPersonal, u); err != nil { - return "", err - } - return q.db.GetUserAppearanceSettings(ctx, userID) -} - func (q *querier) GetUserByEmailOrUsername(ctx context.Context, arg database.GetUserByEmailOrUsernameParams) (database.User, error) { return fetch(q.log, q.auth, q.db.GetUserByEmailOrUsername)(ctx, arg) } @@ -4311,17 +4322,6 @@ func (q *querier) UpdateTemplateWorkspacesLastUsedAt(ctx context.Context, arg da return fetchAndExec(q.log, q.auth, policy.ActionUpdate, fetch, q.db.UpdateTemplateWorkspacesLastUsedAt)(ctx, arg) } -func (q *querier) UpdateUserAppearanceSettings(ctx context.Context, arg database.UpdateUserAppearanceSettingsParams) (database.UserConfig, error) { - u, err := q.db.GetUserByID(ctx, arg.UserID) - if err != nil { - return database.UserConfig{}, err - } - if err := q.authorizeContext(ctx, policy.ActionUpdatePersonal, u); err != nil { - return database.UserConfig{}, err - } - return q.db.UpdateUserAppearanceSettings(ctx, arg) -} - func (q *querier) UpdateUserDeletedByID(ctx context.Context, id uuid.UUID) error { return deleteQ(q.log, q.auth, q.db.GetUserByID, q.db.UpdateUserDeletedByID)(ctx, id) } diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 0fe17f886b1b2..b110c1b17b327 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -1630,23 +1630,23 @@ func (s *MethodTestSuite) TestUser() { []database.GetUserWorkspaceBuildParametersRow{}, ) })) - s.Run("GetUserAppearanceSettings", s.Subtest(func(db database.Store, check *expects) { + s.Run("GetUserThemePreference", s.Subtest(func(db database.Store, check *expects) { ctx := context.Background() u := dbgen.User(s.T(), db, database.User{}) - db.UpdateUserAppearanceSettings(ctx, database.UpdateUserAppearanceSettingsParams{ + db.UpdateUserThemePreference(ctx, database.UpdateUserThemePreferenceParams{ UserID: u.ID, ThemePreference: "light", }) check.Args(u.ID).Asserts(u, policy.ActionReadPersonal).Returns("light") })) - s.Run("UpdateUserAppearanceSettings", s.Subtest(func(db database.Store, check *expects) { + s.Run("UpdateUserThemePreference", s.Subtest(func(db database.Store, check *expects) { u := dbgen.User(s.T(), db, database.User{}) uc := database.UserConfig{ UserID: u.ID, Key: "theme_preference", Value: "dark", } - check.Args(database.UpdateUserAppearanceSettingsParams{ + check.Args(database.UpdateUserThemePreferenceParams{ UserID: u.ID, ThemePreference: uc.Value, }).Asserts(u, policy.ActionUpdatePersonal).Returns(uc) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index bfae69fa68b98..d76a6d0b7aacd 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -1379,6 +1379,47 @@ func (q *FakeQuerier) getProvisionerJobsByIDsWithQueuePositionLockedGlobalQueue( return jobs, nil } +func (q *FakeQuerier) GetUserThemePreference(_ context.Context, userID uuid.UUID) (string, error) { + q.mutex.RLock() + defer q.mutex.RUnlock() + + for _, uc := range q.userConfigs { + if uc.UserID != userID || uc.Key != "theme_preference" { + continue + } + return uc.Value, nil + } + + return "", sql.ErrNoRows +} + +func (q *FakeQuerier) UpdateUserThemePreference(_ context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { + err := validateDatabaseType(arg) + if err != nil { + return database.UserConfig{}, err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + for i, uc := range q.userConfigs { + if uc.UserID != arg.UserID || uc.Key != "theme_preference" { + continue + } + uc.Value = arg.ThemePreference + q.userConfigs[i] = uc + return uc, nil + } + + uc := database.UserConfig{ + UserID: arg.UserID, + Key: "theme_preference", + Value: arg.ThemePreference, + } + q.userConfigs = append(q.userConfigs, uc) + return uc, nil +} + func (*FakeQuerier) AcquireLock(_ context.Context, _ int64) error { return xerrors.New("AcquireLock must only be called within a transaction") } @@ -6434,20 +6475,6 @@ func (q *FakeQuerier) GetUserActivityInsights(_ context.Context, arg database.Ge return rows, nil } -func (q *FakeQuerier) GetUserAppearanceSettings(_ context.Context, userID uuid.UUID) (string, error) { - q.mutex.RLock() - defer q.mutex.RUnlock() - - for _, uc := range q.userConfigs { - if uc.UserID != userID || uc.Key != "theme_preference" { - continue - } - return uc.Value, nil - } - - return "", sql.ErrNoRows -} - func (q *FakeQuerier) GetUserByEmailOrUsername(_ context.Context, arg database.GetUserByEmailOrUsernameParams) (database.User, error) { if err := validateDatabaseType(arg); err != nil { return database.User{}, err @@ -10996,33 +11023,6 @@ func (q *FakeQuerier) UpdateTemplateWorkspacesLastUsedAt(_ context.Context, arg return nil } -func (q *FakeQuerier) UpdateUserAppearanceSettings(_ context.Context, arg database.UpdateUserAppearanceSettingsParams) (database.UserConfig, error) { - err := validateDatabaseType(arg) - if err != nil { - return database.UserConfig{}, err - } - - q.mutex.Lock() - defer q.mutex.Unlock() - - for i, uc := range q.userConfigs { - if uc.UserID != arg.UserID || uc.Key != "theme_preference" { - continue - } - uc.Value = arg.ThemePreference - q.userConfigs[i] = uc - return uc, nil - } - - uc := database.UserConfig{ - UserID: arg.UserID, - Key: "theme_preference", - Value: arg.ThemePreference, - } - q.userConfigs = append(q.userConfigs, uc) - return uc, nil -} - func (q *FakeQuerier) UpdateUserDeletedByID(_ context.Context, id uuid.UUID) error { q.mutex.Lock() defer q.mutex.Unlock() diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go index b29d95752d195..b8abb638c6484 100644 --- a/coderd/database/dbmetrics/querymetrics.go +++ b/coderd/database/dbmetrics/querymetrics.go @@ -88,6 +88,20 @@ func (m queryMetricsStore) DeleteOrganization(ctx context.Context, id uuid.UUID) return r0 } +func (m queryMetricsStore) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) { + start := time.Now() + r0, r1 := m.s.GetUserThemePreference(ctx, userID) + m.queryLatencies.WithLabelValues("GetUserThemePreference").Observe(time.Since(start).Seconds()) + return r0, r1 +} + +func (m queryMetricsStore) UpdateUserThemePreference(ctx context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { + start := time.Now() + r0, r1 := m.s.UpdateUserThemePreference(ctx, arg) + m.queryLatencies.WithLabelValues("UpdateUserThemePreference").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m queryMetricsStore) AcquireLock(ctx context.Context, pgAdvisoryXactLock int64) error { start := time.Now() err := m.s.AcquireLock(ctx, pgAdvisoryXactLock) @@ -1502,13 +1516,6 @@ func (m queryMetricsStore) GetUserActivityInsights(ctx context.Context, arg data return r0, r1 } -func (m queryMetricsStore) GetUserAppearanceSettings(ctx context.Context, userID uuid.UUID) (string, error) { - start := time.Now() - r0, r1 := m.s.GetUserAppearanceSettings(ctx, userID) - m.queryLatencies.WithLabelValues("GetUserAppearanceSettings").Observe(time.Since(start).Seconds()) - return r0, r1 -} - func (m queryMetricsStore) GetUserByEmailOrUsername(ctx context.Context, arg database.GetUserByEmailOrUsernameParams) (database.User, error) { start := time.Now() user, err := m.s.GetUserByEmailOrUsername(ctx, arg) @@ -2727,13 +2734,6 @@ func (m queryMetricsStore) UpdateTemplateWorkspacesLastUsedAt(ctx context.Contex return r0 } -func (m queryMetricsStore) UpdateUserAppearanceSettings(ctx context.Context, arg database.UpdateUserAppearanceSettingsParams) (database.UserConfig, error) { - start := time.Now() - r0, r1 := m.s.UpdateUserAppearanceSettings(ctx, arg) - m.queryLatencies.WithLabelValues("UpdateUserAppearanceSettings").Observe(time.Since(start).Seconds()) - return r0, r1 -} - func (m queryMetricsStore) UpdateUserDeletedByID(ctx context.Context, id uuid.UUID) error { start := time.Now() r0 := m.s.UpdateUserDeletedByID(ctx, id) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index e30759c6bba42..d00649c4e0cad 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -3139,21 +3139,6 @@ func (mr *MockStoreMockRecorder) GetUserActivityInsights(ctx, arg any) *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserActivityInsights", reflect.TypeOf((*MockStore)(nil).GetUserActivityInsights), ctx, arg) } -// GetUserAppearanceSettings mocks base method. -func (m *MockStore) GetUserAppearanceSettings(ctx context.Context, userID uuid.UUID) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUserAppearanceSettings", ctx, userID) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetUserAppearanceSettings indicates an expected call of GetUserAppearanceSettings. -func (mr *MockStoreMockRecorder) GetUserAppearanceSettings(ctx, userID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserAppearanceSettings", reflect.TypeOf((*MockStore)(nil).GetUserAppearanceSettings), ctx, userID) -} - // GetUserByEmailOrUsername mocks base method. func (m *MockStore) GetUserByEmailOrUsername(ctx context.Context, arg database.GetUserByEmailOrUsernameParams) (database.User, error) { m.ctrl.T.Helper() @@ -3289,6 +3274,21 @@ func (mr *MockStoreMockRecorder) GetUserStatusCounts(ctx, arg any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserStatusCounts", reflect.TypeOf((*MockStore)(nil).GetUserStatusCounts), ctx, arg) } +// GetUserThemePreference mocks base method. +func (m *MockStore) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUserThemePreference", ctx, userID) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetUserThemePreference indicates an expected call of GetUserThemePreference. +func (mr *MockStoreMockRecorder) GetUserThemePreference(ctx, userID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserThemePreference", reflect.TypeOf((*MockStore)(nil).GetUserThemePreference), ctx, userID) +} + // GetUserWorkspaceBuildParameters mocks base method. func (m *MockStore) GetUserWorkspaceBuildParameters(ctx context.Context, arg database.GetUserWorkspaceBuildParametersParams) ([]database.GetUserWorkspaceBuildParametersRow, error) { m.ctrl.T.Helper() @@ -5768,21 +5768,6 @@ func (mr *MockStoreMockRecorder) UpdateTemplateWorkspacesLastUsedAt(ctx, arg any return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTemplateWorkspacesLastUsedAt", reflect.TypeOf((*MockStore)(nil).UpdateTemplateWorkspacesLastUsedAt), ctx, arg) } -// UpdateUserAppearanceSettings mocks base method. -func (m *MockStore) UpdateUserAppearanceSettings(ctx context.Context, arg database.UpdateUserAppearanceSettingsParams) (database.UserConfig, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateUserAppearanceSettings", ctx, arg) - ret0, _ := ret[0].(database.UserConfig) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// UpdateUserAppearanceSettings indicates an expected call of UpdateUserAppearanceSettings. -func (mr *MockStoreMockRecorder) UpdateUserAppearanceSettings(ctx, arg any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserAppearanceSettings", reflect.TypeOf((*MockStore)(nil).UpdateUserAppearanceSettings), ctx, arg) -} - // UpdateUserDeletedByID mocks base method. func (m *MockStore) UpdateUserDeletedByID(ctx context.Context, id uuid.UUID) error { m.ctrl.T.Helper() @@ -5974,6 +5959,21 @@ func (mr *MockStoreMockRecorder) UpdateUserStatus(ctx, arg any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserStatus", reflect.TypeOf((*MockStore)(nil).UpdateUserStatus), ctx, arg) } +// UpdateUserThemePreference mocks base method. +func (m *MockStore) UpdateUserThemePreference(ctx context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateUserThemePreference", ctx, arg) + ret0, _ := ret[0].(database.UserConfig) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateUserThemePreference indicates an expected call of UpdateUserThemePreference. +func (mr *MockStoreMockRecorder) UpdateUserThemePreference(ctx, arg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserThemePreference", reflect.TypeOf((*MockStore)(nil).UpdateUserThemePreference), ctx, arg) +} + // UpdateVolumeResourceMonitor mocks base method. func (m *MockStore) UpdateVolumeResourceMonitor(ctx context.Context, arg database.UpdateVolumeResourceMonitorParams) error { m.ctrl.T.Helper() diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 54483c2176f4e..d0aba4d376b3d 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -343,7 +343,6 @@ type sqlcQuerier interface { // produces a bloated value if a user has used multiple templates // simultaneously. GetUserActivityInsights(ctx context.Context, arg GetUserActivityInsightsParams) ([]GetUserActivityInsightsRow, error) - GetUserAppearanceSettings(ctx context.Context, userID uuid.UUID) (string, error) GetUserByEmailOrUsername(ctx context.Context, arg GetUserByEmailOrUsernameParams) (User, error) GetUserByID(ctx context.Context, id uuid.UUID) (User, error) GetUserCount(ctx context.Context, includeSystem bool) (int64, error) @@ -369,6 +368,7 @@ type sqlcQuerier interface { // We do not start counting from 0 at the start_time. We check the last status change before the start_time for each user. As such, // the result shows the total number of users in each status on any particular day. GetUserStatusCounts(ctx context.Context, arg GetUserStatusCountsParams) ([]GetUserStatusCountsRow, error) + GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) GetUserWorkspaceBuildParameters(ctx context.Context, arg GetUserWorkspaceBuildParametersParams) ([]GetUserWorkspaceBuildParametersRow, error) // This will never return deleted users. GetUsers(ctx context.Context, arg GetUsersParams) ([]GetUsersRow, error) @@ -570,7 +570,6 @@ type sqlcQuerier interface { UpdateTemplateVersionDescriptionByJobID(ctx context.Context, arg UpdateTemplateVersionDescriptionByJobIDParams) error UpdateTemplateVersionExternalAuthProvidersByJobID(ctx context.Context, arg UpdateTemplateVersionExternalAuthProvidersByJobIDParams) error UpdateTemplateWorkspacesLastUsedAt(ctx context.Context, arg UpdateTemplateWorkspacesLastUsedAtParams) error - UpdateUserAppearanceSettings(ctx context.Context, arg UpdateUserAppearanceSettingsParams) (UserConfig, error) UpdateUserDeletedByID(ctx context.Context, id uuid.UUID) error UpdateUserGithubComUserID(ctx context.Context, arg UpdateUserGithubComUserIDParams) error UpdateUserHashedOneTimePasscode(ctx context.Context, arg UpdateUserHashedOneTimePasscodeParams) error @@ -584,6 +583,7 @@ type sqlcQuerier interface { UpdateUserQuietHoursSchedule(ctx context.Context, arg UpdateUserQuietHoursScheduleParams) (User, error) UpdateUserRoles(ctx context.Context, arg UpdateUserRolesParams) (User, error) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusParams) (User, error) + UpdateUserThemePreference(ctx context.Context, arg UpdateUserThemePreferenceParams) (UserConfig, error) UpdateVolumeResourceMonitor(ctx context.Context, arg UpdateVolumeResourceMonitorParams) error UpdateWorkspace(ctx context.Context, arg UpdateWorkspaceParams) (WorkspaceTable, error) UpdateWorkspaceAgentConnectionByID(ctx context.Context, arg UpdateWorkspaceAgentConnectionByIDParams) error diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index e1c7c3e65ab92..6e0246c534efd 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -12159,23 +12159,6 @@ func (q *sqlQuerier) GetAuthorizationUserRoles(ctx context.Context, userID uuid. return i, err } -const getUserAppearanceSettings = `-- name: GetUserAppearanceSettings :one -SELECT - value as theme_preference -FROM - user_configs -WHERE - user_id = $1 - AND key = 'theme_preference' -` - -func (q *sqlQuerier) GetUserAppearanceSettings(ctx context.Context, userID uuid.UUID) (string, error) { - row := q.db.QueryRowContext(ctx, getUserAppearanceSettings, userID) - var theme_preference string - err := row.Scan(&theme_preference) - return theme_preference, err -} - const getUserByEmailOrUsername = `-- name: GetUserByEmailOrUsername :one SELECT id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, name, github_com_user_id, hashed_one_time_passcode, one_time_passcode_expires_at, is_system @@ -12273,6 +12256,23 @@ func (q *sqlQuerier) GetUserCount(ctx context.Context, includeSystem bool) (int6 return count, err } +const getUserThemePreference = `-- name: GetUserThemePreference :one +SELECT + value as theme_preference +FROM + user_configs +WHERE + user_id = $1 + AND key = 'theme_preference' +` + +func (q *sqlQuerier) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) { + row := q.db.QueryRowContext(ctx, getUserThemePreference, userID) + var theme_preference string + err := row.Scan(&theme_preference) + return theme_preference, err +} + const getUsers = `-- name: GetUsers :many SELECT id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, name, github_com_user_id, hashed_one_time_passcode, one_time_passcode_expires_at, is_system, COUNT(*) OVER() AS count @@ -12636,33 +12636,6 @@ func (q *sqlQuerier) UpdateInactiveUsersToDormant(ctx context.Context, arg Updat return items, nil } -const updateUserAppearanceSettings = `-- name: UpdateUserAppearanceSettings :one -INSERT INTO - user_configs (user_id, key, value) -VALUES - ($1, 'theme_preference', $2) -ON CONFLICT - ON CONSTRAINT user_configs_pkey -DO UPDATE -SET - value = $2 -WHERE user_configs.user_id = $1 - AND user_configs.key = 'theme_preference' -RETURNING user_id, key, value -` - -type UpdateUserAppearanceSettingsParams struct { - UserID uuid.UUID `db:"user_id" json:"user_id"` - ThemePreference string `db:"theme_preference" json:"theme_preference"` -} - -func (q *sqlQuerier) UpdateUserAppearanceSettings(ctx context.Context, arg UpdateUserAppearanceSettingsParams) (UserConfig, error) { - row := q.db.QueryRowContext(ctx, updateUserAppearanceSettings, arg.UserID, arg.ThemePreference) - var i UserConfig - err := row.Scan(&i.UserID, &i.Key, &i.Value) - return i, err -} - const updateUserDeletedByID = `-- name: UpdateUserDeletedByID :exec UPDATE users @@ -13010,6 +12983,33 @@ func (q *sqlQuerier) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusP return i, err } +const updateUserThemePreference = `-- name: UpdateUserThemePreference :one +INSERT INTO + user_configs (user_id, key, value) +VALUES + ($1, 'theme_preference', $2) +ON CONFLICT + ON CONSTRAINT user_configs_pkey +DO UPDATE +SET + value = $2 +WHERE user_configs.user_id = $1 + AND user_configs.key = 'theme_preference' +RETURNING user_id, key, value +` + +type UpdateUserThemePreferenceParams struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + ThemePreference string `db:"theme_preference" json:"theme_preference"` +} + +func (q *sqlQuerier) UpdateUserThemePreference(ctx context.Context, arg UpdateUserThemePreferenceParams) (UserConfig, error) { + row := q.db.QueryRowContext(ctx, updateUserThemePreference, arg.UserID, arg.ThemePreference) + var i UserConfig + err := row.Scan(&i.UserID, &i.Key, &i.Value) + return i, err +} + const getWorkspaceAgentDevcontainersByAgentID = `-- name: GetWorkspaceAgentDevcontainersByAgentID :many SELECT id, workspace_agent_id, created_at, workspace_folder, config_path, name diff --git a/coderd/database/queries/users.sql b/coderd/database/queries/users.sql index c4304cfc3e60e..fe3bd3a414631 100644 --- a/coderd/database/queries/users.sql +++ b/coderd/database/queries/users.sql @@ -102,7 +102,7 @@ SET WHERE id = $1; --- name: GetUserAppearanceSettings :one +-- name: GetUserThemePreference :one SELECT value as theme_preference FROM @@ -111,7 +111,7 @@ WHERE user_id = @user_id AND key = 'theme_preference'; --- name: UpdateUserAppearanceSettings :one +-- name: UpdateUserThemePreference :one INSERT INTO user_configs (user_id, key, value) VALUES diff --git a/coderd/users.go b/coderd/users.go index 069e1fc240302..934b0b9486111 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -976,7 +976,7 @@ func (api *API) userAppearanceSettings(rw http.ResponseWriter, r *http.Request) user = httpmw.UserParam(r) ) - themePreference, err := api.Database.GetUserAppearanceSettings(ctx, user.ID) + themePreference, err := api.Database.GetUserThemePreference(ctx, user.ID) if err != nil { if !errors.Is(err, sql.ErrNoRows) { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -1015,7 +1015,7 @@ func (api *API) putUserAppearanceSettings(rw http.ResponseWriter, r *http.Reques return } - updatedSettings, err := api.Database.UpdateUserAppearanceSettings(ctx, database.UpdateUserAppearanceSettingsParams{ + updatedSettings, err := api.Database.UpdateUserThemePreference(ctx, database.UpdateUserThemePreferenceParams{ UserID: user.ID, ThemePreference: params.ThemePreference, }) diff --git a/codersdk/users.go b/codersdk/users.go index 31854731a0ae1..3be661a9d22ff 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -189,12 +189,23 @@ type ValidateUserPasswordResponse struct { Details string `json:"details"` } +// TerminalFontName is the name of supported terminal font +type TerminalFontName string + +const ( + TerminalFontUnknown TerminalFontName = "" + TerminalFontIbmMonoPlex TerminalFontName = "ibm-mono-plex" + TerminalFontFiraCode TerminalFontName = "fira-code" +) + type UserAppearanceSettings struct { - ThemePreference string `json:"theme_preference"` + ThemePreference string `json:"theme_preference"` + TerminalFont TerminalFontName `json:"terminal_font" enums:"ibm-mono-plex,fira-code"` } type UpdateUserAppearanceSettingsRequest struct { - ThemePreference string `json:"theme_preference" validate:"required"` + ThemePreference string `json:"theme_preference" validate:"required"` + TerminalFont TerminalFontName `json:"terminal_font" validate:"required" enums:"ibm-mono-plex,fira-code"` } type UpdateUserPasswordRequest struct { diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 4791967b53c9e..08c211c06d9cc 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -6712,6 +6712,22 @@ Restarts will only happen on weekdays in this list on weeks which line up with W |--------------------------| | `UNSUPPORTED_WORKSPACES` | +## codersdk.TerminalFontName + +```json +"" +``` + +### Properties + +#### Enumerated Values + +| Value | +|-----------------| +| `` | +| `ibm-mono-plex` | +| `fira-code` | + ## codersdk.TimingStage ```json @@ -6909,15 +6925,24 @@ Restarts will only happen on weekdays in this list on weeks which line up with W ```json { + "terminal_font": "ibm-mono-plex", "theme_preference": "string" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -|--------------------|--------|----------|--------------|-------------| -| `theme_preference` | string | true | | | +| Name | Type | Required | Restrictions | Description | +|--------------------|--------------------------------------------------------|----------|--------------|-------------| +| `terminal_font` | [codersdk.TerminalFontName](#codersdkterminalfontname) | true | | | +| `theme_preference` | string | true | | | + +#### Enumerated Values + +| Property | Value | +|-----------------|-----------------| +| `terminal_font` | `ibm-mono-plex` | +| `terminal_font` | `fira-code` | ## codersdk.UpdateUserNotificationPreferences @@ -7260,15 +7285,24 @@ If the schedule is empty, the user will be updated to use the default schedule.| ```json { + "terminal_font": "ibm-mono-plex", "theme_preference": "string" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -|--------------------|--------|----------|--------------|-------------| -| `theme_preference` | string | false | | | +| Name | Type | Required | Restrictions | Description | +|--------------------|--------------------------------------------------------|----------|--------------|-------------| +| `terminal_font` | [codersdk.TerminalFontName](#codersdkterminalfontname) | false | | | +| `theme_preference` | string | false | | | + +#### Enumerated Values + +| Property | Value | +|-----------------|-----------------| +| `terminal_font` | `ibm-mono-plex` | +| `terminal_font` | `fira-code` | ## codersdk.UserLatency diff --git a/docs/reference/api/users.md b/docs/reference/api/users.md index 3f0c38571f7c4..09358064b2118 100644 --- a/docs/reference/api/users.md +++ b/docs/reference/api/users.md @@ -501,6 +501,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/appearance \ ```json { + "terminal_font": "ibm-mono-plex", "theme_preference": "string" } ``` @@ -531,6 +532,7 @@ curl -X PUT http://coder-server:8080/api/v2/users/{user}/appearance \ ```json { + "terminal_font": "ibm-mono-plex", "theme_preference": "string" } ``` @@ -548,6 +550,7 @@ curl -X PUT http://coder-server:8080/api/v2/users/{user}/appearance \ ```json { + "terminal_font": "ibm-mono-plex", "theme_preference": "string" } ``` diff --git a/provisioner/terraform/testdata/resources/version.txt b/provisioner/terraform/testdata/resources/version.txt index ca7176690dd6f..720c7384c6195 100644 --- a/provisioner/terraform/testdata/resources/version.txt +++ b/provisioner/terraform/testdata/resources/version.txt @@ -1 +1 @@ -1.11.2 +1.11.1 diff --git a/site/site.go b/site/site.go index f4d5509479db5..710ad6174fbae 100644 --- a/site/site.go +++ b/site/site.go @@ -436,7 +436,7 @@ func (h *Handler) renderHTMLWithState(r *http.Request, filePath string, state ht }) eg.Go(func() error { var err error - themePreference, err = h.opts.Database.GetUserAppearanceSettings(ctx, apiKey.UserID) + themePreference, err = h.opts.Database.GetUserThemePreference(ctx, apiKey.UserID) if errors.Is(err, sql.ErrNoRows) { themePreference = "" return nil diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 2df1c351d9db1..05e7c807ed614 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -2655,6 +2655,15 @@ export interface TemplateVersionsByTemplateRequest extends Pagination { readonly include_archived: boolean; } +// From codersdk/users.go +export type TerminalFontName = "fira-code" | "ibm-mono-plex" | ""; + +export const TerminalFontNames: TerminalFontName[] = [ + "fira-code", + "ibm-mono-plex", + "", +]; + // From codersdk/workspacebuilds.go export type TimingStage = | "apply" @@ -2788,6 +2797,7 @@ export interface UpdateTemplateMeta { // From codersdk/users.go export interface UpdateUserAppearanceSettingsRequest { readonly theme_preference: string; + readonly terminal_font: TerminalFontName; } // From codersdk/notifications.go @@ -2904,6 +2914,7 @@ export interface UserActivityInsightsResponse { // From codersdk/users.go export interface UserAppearanceSettings { readonly theme_preference: string; + readonly terminal_font: TerminalFontName; } // From codersdk/insights.go From fd6c9e427512cacfa2d7c98dee451348574a2678 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 11:05:10 +0200 Subject: [PATCH 03/22] GetUserTerminalFont --- coderd/database/dbauthz/dbauthz.go | 66 ++++++++---- coderd/database/dbmem/dbmem.go | 123 ++++++++++++++-------- coderd/database/dbmetrics/querymetrics.go | 42 +++++--- coderd/database/dbmock/dbmock.go | 30 ++++++ coderd/database/querier.go | 2 + coderd/database/queries.sql.go | 44 ++++++++ coderd/database/queries/users.sql | 23 ++++ 7 files changed, 253 insertions(+), 77 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index e496eda90ea54..042e97f6569f6 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1061,28 +1061,6 @@ func (q *querier) customRoleCheck(ctx context.Context, role database.CustomRole) return nil } -func (q *querier) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) { - u, err := q.db.GetUserByID(ctx, userID) - if err != nil { - return "", err - } - if err := q.authorizeContext(ctx, policy.ActionReadPersonal, u); err != nil { - return "", err - } - return q.db.GetUserThemePreference(ctx, userID) -} - -func (q *querier) UpdateUserThemePreference(ctx context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { - u, err := q.db.GetUserByID(ctx, arg.UserID) - if err != nil { - return database.UserConfig{}, err - } - if err := q.authorizeContext(ctx, policy.ActionUpdatePersonal, u); err != nil { - return database.UserConfig{}, err - } - return q.db.UpdateUserThemePreference(ctx, arg) -} - func (q *querier) AcquireLock(ctx context.Context, id int64) error { return q.db.AcquireLock(ctx, id) } @@ -2805,6 +2783,28 @@ func (q *querier) GetUserStatusCounts(ctx context.Context, arg database.GetUserS return q.db.GetUserStatusCounts(ctx, arg) } +func (q *querier) GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) { + u, err := q.db.GetUserByID(ctx, userID) + if err != nil { + return "", err + } + if err := q.authorizeContext(ctx, policy.ActionReadPersonal, u); err != nil { + return "", err + } + return q.db.GetUserTerminalFont(ctx, userID) +} + +func (q *querier) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) { + u, err := q.db.GetUserByID(ctx, userID) + if err != nil { + return "", err + } + if err := q.authorizeContext(ctx, policy.ActionReadPersonal, u); err != nil { + return "", err + } + return q.db.GetUserThemePreference(ctx, userID) +} + func (q *querier) GetUserWorkspaceBuildParameters(ctx context.Context, params database.GetUserWorkspaceBuildParametersParams) ([]database.GetUserWorkspaceBuildParametersRow, error) { u, err := q.db.GetUserByID(ctx, params.OwnerID) if err != nil { @@ -4459,6 +4459,28 @@ func (q *querier) UpdateUserStatus(ctx context.Context, arg database.UpdateUserS return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateUserStatus)(ctx, arg) } +func (q *querier) UpdateUserTerminalFont(ctx context.Context, arg database.UpdateUserTerminalFontParams) (database.UserConfig, error) { + u, err := q.db.GetUserByID(ctx, arg.UserID) + if err != nil { + return database.UserConfig{}, err + } + if err := q.authorizeContext(ctx, policy.ActionUpdatePersonal, u); err != nil { + return database.UserConfig{}, err + } + return q.db.UpdateUserTerminalFont(ctx, arg) +} + +func (q *querier) UpdateUserThemePreference(ctx context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { + u, err := q.db.GetUserByID(ctx, arg.UserID) + if err != nil { + return database.UserConfig{}, err + } + if err := q.authorizeContext(ctx, policy.ActionUpdatePersonal, u); err != nil { + return database.UserConfig{}, err + } + return q.db.UpdateUserThemePreference(ctx, arg) +} + func (q *querier) UpdateVolumeResourceMonitor(ctx context.Context, arg database.UpdateVolumeResourceMonitorParams) error { if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceWorkspaceAgentResourceMonitor); err != nil { return err diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index d76a6d0b7aacd..a8351cbb8ca16 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -1379,47 +1379,6 @@ func (q *FakeQuerier) getProvisionerJobsByIDsWithQueuePositionLockedGlobalQueue( return jobs, nil } -func (q *FakeQuerier) GetUserThemePreference(_ context.Context, userID uuid.UUID) (string, error) { - q.mutex.RLock() - defer q.mutex.RUnlock() - - for _, uc := range q.userConfigs { - if uc.UserID != userID || uc.Key != "theme_preference" { - continue - } - return uc.Value, nil - } - - return "", sql.ErrNoRows -} - -func (q *FakeQuerier) UpdateUserThemePreference(_ context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { - err := validateDatabaseType(arg) - if err != nil { - return database.UserConfig{}, err - } - - q.mutex.Lock() - defer q.mutex.Unlock() - - for i, uc := range q.userConfigs { - if uc.UserID != arg.UserID || uc.Key != "theme_preference" { - continue - } - uc.Value = arg.ThemePreference - q.userConfigs[i] = uc - return uc, nil - } - - uc := database.UserConfig{ - UserID: arg.UserID, - Key: "theme_preference", - Value: arg.ThemePreference, - } - q.userConfigs = append(q.userConfigs, uc) - return uc, nil -} - func (*FakeQuerier) AcquireLock(_ context.Context, _ int64) error { return xerrors.New("AcquireLock must only be called within a transaction") } @@ -6687,6 +6646,34 @@ func (q *FakeQuerier) GetUserStatusCounts(_ context.Context, arg database.GetUse return result, nil } +func (q *FakeQuerier) GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) { + q.mutex.RLock() + defer q.mutex.RUnlock() + + for _, uc := range q.userConfigs { + if uc.UserID != userID || uc.Key != "terminal_font" { + continue + } + return uc.Value, nil + } + + return "", sql.ErrNoRows +} + +func (q *FakeQuerier) GetUserThemePreference(_ context.Context, userID uuid.UUID) (string, error) { + q.mutex.RLock() + defer q.mutex.RUnlock() + + for _, uc := range q.userConfigs { + if uc.UserID != userID || uc.Key != "theme_preference" { + continue + } + return uc.Value, nil + } + + return "", sql.ErrNoRows +} + func (q *FakeQuerier) GetUserWorkspaceBuildParameters(_ context.Context, params database.GetUserWorkspaceBuildParametersParams) ([]database.GetUserWorkspaceBuildParametersRow, error) { q.mutex.RLock() defer q.mutex.RUnlock() @@ -11348,6 +11335,60 @@ func (q *FakeQuerier) UpdateUserStatus(_ context.Context, arg database.UpdateUse return database.User{}, sql.ErrNoRows } +func (q *FakeQuerier) UpdateUserTerminalFont(ctx context.Context, arg database.UpdateUserTerminalFontParams) (database.UserConfig, error) { + err := validateDatabaseType(arg) + if err != nil { + return database.UserConfig{}, err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + for i, uc := range q.userConfigs { + if uc.UserID != arg.UserID || uc.Key != "terminal_font" { + continue + } + uc.Value = arg.TerminalFont + q.userConfigs[i] = uc + return uc, nil + } + + uc := database.UserConfig{ + UserID: arg.UserID, + Key: "terminal_font", + Value: arg.TerminalFont, + } + q.userConfigs = append(q.userConfigs, uc) + return uc, nil +} + +func (q *FakeQuerier) UpdateUserThemePreference(_ context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { + err := validateDatabaseType(arg) + if err != nil { + return database.UserConfig{}, err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + for i, uc := range q.userConfigs { + if uc.UserID != arg.UserID || uc.Key != "theme_preference" { + continue + } + uc.Value = arg.ThemePreference + q.userConfigs[i] = uc + return uc, nil + } + + uc := database.UserConfig{ + UserID: arg.UserID, + Key: "theme_preference", + Value: arg.ThemePreference, + } + q.userConfigs = append(q.userConfigs, uc) + return uc, nil +} + func (q *FakeQuerier) UpdateVolumeResourceMonitor(_ context.Context, arg database.UpdateVolumeResourceMonitorParams) error { err := validateDatabaseType(arg) if err != nil { diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go index b8abb638c6484..31ce37e096f1f 100644 --- a/coderd/database/dbmetrics/querymetrics.go +++ b/coderd/database/dbmetrics/querymetrics.go @@ -88,20 +88,6 @@ func (m queryMetricsStore) DeleteOrganization(ctx context.Context, id uuid.UUID) return r0 } -func (m queryMetricsStore) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) { - start := time.Now() - r0, r1 := m.s.GetUserThemePreference(ctx, userID) - m.queryLatencies.WithLabelValues("GetUserThemePreference").Observe(time.Since(start).Seconds()) - return r0, r1 -} - -func (m queryMetricsStore) UpdateUserThemePreference(ctx context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { - start := time.Now() - r0, r1 := m.s.UpdateUserThemePreference(ctx, arg) - m.queryLatencies.WithLabelValues("UpdateUserThemePreference").Observe(time.Since(start).Seconds()) - return r0, r1 -} - func (m queryMetricsStore) AcquireLock(ctx context.Context, pgAdvisoryXactLock int64) error { start := time.Now() err := m.s.AcquireLock(ctx, pgAdvisoryXactLock) @@ -1579,6 +1565,20 @@ func (m queryMetricsStore) GetUserStatusCounts(ctx context.Context, arg database return r0, r1 } +func (m queryMetricsStore) GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) { + start := time.Now() + r0, r1 := m.s.GetUserTerminalFont(ctx, userID) + m.queryLatencies.WithLabelValues("GetUserTerminalFont").Observe(time.Since(start).Seconds()) + return r0, r1 +} + +func (m queryMetricsStore) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) { + start := time.Now() + r0, r1 := m.s.GetUserThemePreference(ctx, userID) + m.queryLatencies.WithLabelValues("GetUserThemePreference").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m queryMetricsStore) GetUserWorkspaceBuildParameters(ctx context.Context, ownerID database.GetUserWorkspaceBuildParametersParams) ([]database.GetUserWorkspaceBuildParametersRow, error) { start := time.Now() r0, r1 := m.s.GetUserWorkspaceBuildParameters(ctx, ownerID) @@ -2825,6 +2825,20 @@ func (m queryMetricsStore) UpdateUserStatus(ctx context.Context, arg database.Up return user, err } +func (m queryMetricsStore) UpdateUserTerminalFont(ctx context.Context, arg database.UpdateUserTerminalFontParams) (database.UserConfig, error) { + start := time.Now() + r0, r1 := m.s.UpdateUserTerminalFont(ctx, arg) + m.queryLatencies.WithLabelValues("UpdateUserTerminalFont").Observe(time.Since(start).Seconds()) + return r0, r1 +} + +func (m queryMetricsStore) UpdateUserThemePreference(ctx context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { + start := time.Now() + r0, r1 := m.s.UpdateUserThemePreference(ctx, arg) + m.queryLatencies.WithLabelValues("UpdateUserThemePreference").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m queryMetricsStore) UpdateVolumeResourceMonitor(ctx context.Context, arg database.UpdateVolumeResourceMonitorParams) error { start := time.Now() r0 := m.s.UpdateVolumeResourceMonitor(ctx, arg) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index d00649c4e0cad..4117cb71074be 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -3274,6 +3274,21 @@ func (mr *MockStoreMockRecorder) GetUserStatusCounts(ctx, arg any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserStatusCounts", reflect.TypeOf((*MockStore)(nil).GetUserStatusCounts), ctx, arg) } +// GetUserTerminalFont mocks base method. +func (m *MockStore) GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUserTerminalFont", ctx, userID) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetUserTerminalFont indicates an expected call of GetUserTerminalFont. +func (mr *MockStoreMockRecorder) GetUserTerminalFont(ctx, userID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserTerminalFont", reflect.TypeOf((*MockStore)(nil).GetUserTerminalFont), ctx, userID) +} + // GetUserThemePreference mocks base method. func (m *MockStore) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) { m.ctrl.T.Helper() @@ -5959,6 +5974,21 @@ func (mr *MockStoreMockRecorder) UpdateUserStatus(ctx, arg any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserStatus", reflect.TypeOf((*MockStore)(nil).UpdateUserStatus), ctx, arg) } +// UpdateUserTerminalFont mocks base method. +func (m *MockStore) UpdateUserTerminalFont(ctx context.Context, arg database.UpdateUserTerminalFontParams) (database.UserConfig, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateUserTerminalFont", ctx, arg) + ret0, _ := ret[0].(database.UserConfig) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateUserTerminalFont indicates an expected call of UpdateUserTerminalFont. +func (mr *MockStoreMockRecorder) UpdateUserTerminalFont(ctx, arg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserTerminalFont", reflect.TypeOf((*MockStore)(nil).UpdateUserTerminalFont), ctx, arg) +} + // UpdateUserThemePreference mocks base method. func (m *MockStore) UpdateUserThemePreference(ctx context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) { m.ctrl.T.Helper() diff --git a/coderd/database/querier.go b/coderd/database/querier.go index d0aba4d376b3d..028c025dde921 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -368,6 +368,7 @@ type sqlcQuerier interface { // We do not start counting from 0 at the start_time. We check the last status change before the start_time for each user. As such, // the result shows the total number of users in each status on any particular day. GetUserStatusCounts(ctx context.Context, arg GetUserStatusCountsParams) ([]GetUserStatusCountsRow, error) + GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) GetUserWorkspaceBuildParameters(ctx context.Context, arg GetUserWorkspaceBuildParametersParams) ([]GetUserWorkspaceBuildParametersRow, error) // This will never return deleted users. @@ -583,6 +584,7 @@ type sqlcQuerier interface { UpdateUserQuietHoursSchedule(ctx context.Context, arg UpdateUserQuietHoursScheduleParams) (User, error) UpdateUserRoles(ctx context.Context, arg UpdateUserRolesParams) (User, error) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusParams) (User, error) + UpdateUserTerminalFont(ctx context.Context, arg UpdateUserTerminalFontParams) (UserConfig, error) UpdateUserThemePreference(ctx context.Context, arg UpdateUserThemePreferenceParams) (UserConfig, error) UpdateVolumeResourceMonitor(ctx context.Context, arg UpdateVolumeResourceMonitorParams) error UpdateWorkspace(ctx context.Context, arg UpdateWorkspaceParams) (WorkspaceTable, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 6e0246c534efd..e9acf4bb5462b 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -12256,6 +12256,23 @@ func (q *sqlQuerier) GetUserCount(ctx context.Context, includeSystem bool) (int6 return count, err } +const getUserTerminalFont = `-- name: GetUserTerminalFont :one +SELECT + value as terminal_font +FROM + user_configs +WHERE + user_id = $1 + AND key = 'terminal_font' +` + +func (q *sqlQuerier) GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) { + row := q.db.QueryRowContext(ctx, getUserTerminalFont, userID) + var terminal_font string + err := row.Scan(&terminal_font) + return terminal_font, err +} + const getUserThemePreference = `-- name: GetUserThemePreference :one SELECT value as theme_preference @@ -12983,6 +13000,33 @@ func (q *sqlQuerier) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusP return i, err } +const updateUserTerminalFont = `-- name: UpdateUserTerminalFont :one +INSERT INTO + user_configs (user_id, key, value) +VALUES + ($1, 'terminal_font', $2) +ON CONFLICT + ON CONSTRAINT user_configs_pkey +DO UPDATE +SET + value = $2 +WHERE user_configs.user_id = $1 + AND user_configs.key = 'terminal_font' +RETURNING user_id, key, value +` + +type UpdateUserTerminalFontParams struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + TerminalFont string `db:"terminal_font" json:"terminal_font"` +} + +func (q *sqlQuerier) UpdateUserTerminalFont(ctx context.Context, arg UpdateUserTerminalFontParams) (UserConfig, error) { + row := q.db.QueryRowContext(ctx, updateUserTerminalFont, arg.UserID, arg.TerminalFont) + var i UserConfig + err := row.Scan(&i.UserID, &i.Key, &i.Value) + return i, err +} + const updateUserThemePreference = `-- name: UpdateUserThemePreference :one INSERT INTO user_configs (user_id, key, value) diff --git a/coderd/database/queries/users.sql b/coderd/database/queries/users.sql index fe3bd3a414631..0bac76c8df14a 100644 --- a/coderd/database/queries/users.sql +++ b/coderd/database/queries/users.sql @@ -125,6 +125,29 @@ WHERE user_configs.user_id = @user_id AND user_configs.key = 'theme_preference' RETURNING *; +-- name: GetUserTerminalFont :one +SELECT + value as terminal_font +FROM + user_configs +WHERE + user_id = @user_id + AND key = 'terminal_font'; + +-- name: UpdateUserTerminalFont :one +INSERT INTO + user_configs (user_id, key, value) +VALUES + (@user_id, 'terminal_font', @terminal_font) +ON CONFLICT + ON CONSTRAINT user_configs_pkey +DO UPDATE +SET + value = @terminal_font +WHERE user_configs.user_id = @user_id + AND user_configs.key = 'terminal_font' +RETURNING *; + -- name: UpdateUserRoles :one UPDATE users From 40ee863bebb94016278700a9e3c28c73b405003d Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 11:35:20 +0200 Subject: [PATCH 04/22] ibm-plex-mono --- coderd/apidoc/docs.go | 8 ++--- coderd/apidoc/swagger.json | 8 ++--- coderd/users.go | 33 +++++++++++++++++-- codersdk/users.go | 6 ++-- docs/reference/api/schemas.md | 10 +++--- docs/reference/api/users.md | 6 ++-- site/src/api/typesGenerated.ts | 4 +-- .../AppearancePage/AppearanceForm.stories.tsx | 2 +- .../AppearancePage/AppearanceForm.tsx | 5 +-- .../AppearancePage/AppearancePage.test.tsx | 2 ++ .../AppearancePage/AppearancePage.tsx | 1 + site/src/testHelpers/entities.ts | 1 + site/src/theme/index.ts | 2 ++ 13 files changed, 61 insertions(+), 27 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index bc11caf62facf..350486f25c8e0 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -15599,12 +15599,12 @@ const docTemplate = `{ "type": "string", "enum": [ "", - "ibm-mono-plex", + "ibm-plex-mono", "fira-code" ], "x-enum-varnames": [ "TerminalFontUnknown", - "TerminalFontIbmMonoPlex", + "TerminalFontIbmPlexMono", "TerminalFontFiraCode" ] }, @@ -15787,7 +15787,7 @@ const docTemplate = `{ "properties": { "terminal_font": { "enum": [ - "ibm-mono-plex", + "ibm-plex-mono", "fira-code" ], "allOf": [ @@ -16089,7 +16089,7 @@ const docTemplate = `{ "properties": { "terminal_font": { "enum": [ - "ibm-mono-plex", + "ibm-plex-mono", "fira-code" ], "allOf": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index a9cdae584b627..4f89e11c53582 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -14182,10 +14182,10 @@ }, "codersdk.TerminalFontName": { "type": "string", - "enum": ["", "ibm-mono-plex", "fira-code"], + "enum": ["", "ibm-plex-mono", "fira-code"], "x-enum-varnames": [ "TerminalFontUnknown", - "TerminalFontIbmMonoPlex", + "TerminalFontIbmPlexMono", "TerminalFontFiraCode" ] }, @@ -14362,7 +14362,7 @@ "required": ["terminal_font", "theme_preference"], "properties": { "terminal_font": { - "enum": ["ibm-mono-plex", "fira-code"], + "enum": ["ibm-plex-mono", "fira-code"], "allOf": [ { "$ref": "#/definitions/codersdk.TerminalFontName" @@ -14635,7 +14635,7 @@ "type": "object", "properties": { "terminal_font": { - "enum": ["ibm-mono-plex", "fira-code"], + "enum": ["ibm-plex-mono", "fira-code"], "allOf": [ { "$ref": "#/definitions/codersdk.TerminalFontName" diff --git a/coderd/users.go b/coderd/users.go index 934b0b9486111..d0ed0223362df 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -989,8 +989,22 @@ func (api *API) userAppearanceSettings(rw http.ResponseWriter, r *http.Request) themePreference = "" } + terminalFont, err := api.Database.GetUserTerminalFont(ctx, user.ID) + if err != nil { + if !errors.Is(err, sql.ErrNoRows) { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Error reading user settings.", + Detail: err.Error(), + }) + return + } + + terminalFont = "" + } + httpapi.Write(ctx, rw, http.StatusOK, codersdk.UserAppearanceSettings{ ThemePreference: themePreference, + TerminalFont: codersdk.TerminalFontName(terminalFont), }) } @@ -1015,20 +1029,33 @@ func (api *API) putUserAppearanceSettings(rw http.ResponseWriter, r *http.Reques return } - updatedSettings, err := api.Database.UpdateUserThemePreference(ctx, database.UpdateUserThemePreferenceParams{ + updatedThemePreference, err := api.Database.UpdateUserThemePreference(ctx, database.UpdateUserThemePreferenceParams{ UserID: user.ID, ThemePreference: params.ThemePreference, }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error updating user.", + Message: "Internal error updating user theme prefence.", + Detail: err.Error(), + }) + return + } + + updatedTerminalFont, err := api.Database.UpdateUserTerminalFont(ctx, database.UpdateUserTerminalFontParams{ + UserID: user.ID, + TerminalFont: string(params.ThemePreference), + }) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error updating user terminal font.", Detail: err.Error(), }) return } httpapi.Write(ctx, rw, http.StatusOK, codersdk.UserAppearanceSettings{ - ThemePreference: updatedSettings.Value, + ThemePreference: updatedThemePreference.Value, + TerminalFont: codersdk.TerminalFontName(updatedTerminalFont.Value), }) } diff --git a/codersdk/users.go b/codersdk/users.go index 3be661a9d22ff..9f136f00235db 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -194,18 +194,18 @@ type TerminalFontName string const ( TerminalFontUnknown TerminalFontName = "" - TerminalFontIbmMonoPlex TerminalFontName = "ibm-mono-plex" + TerminalFontIbmPlexMono TerminalFontName = "ibm-plex-mono" TerminalFontFiraCode TerminalFontName = "fira-code" ) type UserAppearanceSettings struct { ThemePreference string `json:"theme_preference"` - TerminalFont TerminalFontName `json:"terminal_font" enums:"ibm-mono-plex,fira-code"` + TerminalFont TerminalFontName `json:"terminal_font" enums:"ibm-plex-mono,fira-code"` } type UpdateUserAppearanceSettingsRequest struct { ThemePreference string `json:"theme_preference" validate:"required"` - TerminalFont TerminalFontName `json:"terminal_font" validate:"required" enums:"ibm-mono-plex,fira-code"` + TerminalFont TerminalFontName `json:"terminal_font" validate:"required" enums:"ibm-plex-mono,fira-code"` } type UpdateUserPasswordRequest struct { diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 08c211c06d9cc..5b3b132b6546c 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -6725,7 +6725,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W | Value | |-----------------| | `` | -| `ibm-mono-plex` | +| `ibm-plex-mono` | | `fira-code` | ## codersdk.TimingStage @@ -6925,7 +6925,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W ```json { - "terminal_font": "ibm-mono-plex", + "terminal_font": "ibm-plex-mono", "theme_preference": "string" } ``` @@ -6941,7 +6941,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W | Property | Value | |-----------------|-----------------| -| `terminal_font` | `ibm-mono-plex` | +| `terminal_font` | `ibm-plex-mono` | | `terminal_font` | `fira-code` | ## codersdk.UpdateUserNotificationPreferences @@ -7285,7 +7285,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| ```json { - "terminal_font": "ibm-mono-plex", + "terminal_font": "ibm-plex-mono", "theme_preference": "string" } ``` @@ -7301,7 +7301,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| | Property | Value | |-----------------|-----------------| -| `terminal_font` | `ibm-mono-plex` | +| `terminal_font` | `ibm-plex-mono` | | `terminal_font` | `fira-code` | ## codersdk.UserLatency diff --git a/docs/reference/api/users.md b/docs/reference/api/users.md index 09358064b2118..61f4d390fccd4 100644 --- a/docs/reference/api/users.md +++ b/docs/reference/api/users.md @@ -501,7 +501,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/appearance \ ```json { - "terminal_font": "ibm-mono-plex", + "terminal_font": "ibm-plex-mono", "theme_preference": "string" } ``` @@ -532,7 +532,7 @@ curl -X PUT http://coder-server:8080/api/v2/users/{user}/appearance \ ```json { - "terminal_font": "ibm-mono-plex", + "terminal_font": "ibm-plex-mono", "theme_preference": "string" } ``` @@ -550,7 +550,7 @@ curl -X PUT http://coder-server:8080/api/v2/users/{user}/appearance \ ```json { - "terminal_font": "ibm-mono-plex", + "terminal_font": "ibm-plex-mono", "theme_preference": "string" } ``` diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 05e7c807ed614..e205e8938e999 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -2656,11 +2656,11 @@ export interface TemplateVersionsByTemplateRequest extends Pagination { } // From codersdk/users.go -export type TerminalFontName = "fira-code" | "ibm-mono-plex" | ""; +export type TerminalFontName = "fira-code" | "ibm-plex-mono" | ""; export const TerminalFontNames: TerminalFontName[] = [ "fira-code", - "ibm-mono-plex", + "ibm-plex-mono", "", ]; diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.stories.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.stories.tsx index 4f2c5965dc957..436e2e7e38c2d 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.stories.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.stories.tsx @@ -18,6 +18,6 @@ type Story = StoryObj; export const Example: Story = { args: { - initialValues: { theme_preference: "" }, + initialValues: { theme_preference: "", terminal_font: "" }, }, }; diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx index 3468685a246cb..cda4ae198c444 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx @@ -6,7 +6,7 @@ import { PreviewBadge } from "components/Badges/Badges"; import { Stack } from "components/Stack/Stack"; import { ThemeOverride } from "contexts/ThemeProvider"; import type { FC } from "react"; -import themes, { DEFAULT_THEME, type Theme } from "theme"; +import themes, { DEFAULT_TERMINAL_FONT, DEFAULT_THEME, type Theme } from "theme"; export interface AppearanceFormProps { isUpdating?: boolean; @@ -22,13 +22,14 @@ export const AppearanceForm: FC = ({ initialValues, }) => { const currentTheme = initialValues.theme_preference || DEFAULT_THEME; + const currentTerminalFont = initialValues.terminal_font || DEFAULT_TERMINAL_FONT; const onChangeTheme = async (theme: string) => { if (isUpdating) { return; } - await onSubmit({ theme_preference: theme }); + await onSubmit({ theme_preference: theme, terminal_font: currentTerminalFont }); }; return ( diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.test.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.test.tsx index c48c265460a4e..5b8db73dd95ab 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.test.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.test.tsx @@ -12,6 +12,7 @@ describe("appearance page", () => { jest.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({ ...MockUser, theme_preference: "dark", + terminal_font: "", }); const dark = await screen.findByText("Dark"); @@ -27,6 +28,7 @@ describe("appearance page", () => { jest.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({ ...MockUser, theme_preference: "light", + terminal_font: "", }); const light = await screen.findByText("Light"); diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.tsx index 1379e42d0e909..68621751da553 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.tsx @@ -47,6 +47,7 @@ export const AppearancePage: FC = () => { error={updateAppearanceSettingsMutation.error} initialValues={{ theme_preference: appearanceSettingsQuery.data.theme_preference, + terminal_font: appearanceSettingsQuery.data.terminal_font, }} onSubmit={updateAppearanceSettingsMutation.mutateAsync} /> diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index a298dea4ffd9d..02b08f03c5376 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -536,6 +536,7 @@ export const SuspendedMockUser: TypesGen.User = { export const MockUserAppearanceSettings: TypesGen.UserAppearanceSettings = { theme_preference: "dark", + terminal_font: "", }; export const MockOrganizationMember: TypesGen.OrganizationMemberWithUserData = { diff --git a/site/src/theme/index.ts b/site/src/theme/index.ts index a36bd9b223e8d..504241c8f8e0f 100644 --- a/site/src/theme/index.ts +++ b/site/src/theme/index.ts @@ -36,3 +36,5 @@ const theme = { } satisfies Record; export default theme; + +export const DEFAULT_TERMINAL_FONT = "ibm-plex-mono"; From 6a6272b24d362a7ca68ec6ae18b70f55ca6b4b51 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 12:48:31 +0200 Subject: [PATCH 05/22] form --- coderd/users.go | 2 +- site/site.go | 11 ++ site/src/api/queries/users.ts | 1 + .../AppearancePage/AppearanceForm.tsx | 125 ++++++++++++++---- .../AppearancePage/AppearancePage.tsx | 33 ++--- 5 files changed, 123 insertions(+), 49 deletions(-) diff --git a/coderd/users.go b/coderd/users.go index d0ed0223362df..030935871ce56 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -1043,7 +1043,7 @@ func (api *API) putUserAppearanceSettings(rw http.ResponseWriter, r *http.Reques updatedTerminalFont, err := api.Database.UpdateUserTerminalFont(ctx, database.UpdateUserTerminalFontParams{ UserID: user.ID, - TerminalFont: string(params.ThemePreference), + TerminalFont: string(params.TerminalFont), }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ diff --git a/site/site.go b/site/site.go index 710ad6174fbae..e47e15848cda0 100644 --- a/site/site.go +++ b/site/site.go @@ -428,6 +428,7 @@ func (h *Handler) renderHTMLWithState(r *http.Request, filePath string, state ht var eg errgroup.Group var user database.User var themePreference string + var terminalFont string orgIDs := []uuid.UUID{} eg.Go(func() error { var err error @@ -443,6 +444,15 @@ func (h *Handler) renderHTMLWithState(r *http.Request, filePath string, state ht } return err }) + eg.Go(func() error { + var err error + terminalFont, err = h.opts.Database.GetUserTerminalFont(ctx, apiKey.UserID) + if errors.Is(err, sql.ErrNoRows) { + terminalFont = "" + return nil + } + return err + }) eg.Go(func() error { memberIDs, err := h.opts.Database.GetOrganizationIDsByMemberIDs(ctx, []uuid.UUID{apiKey.UserID}) if errors.Is(err, sql.ErrNoRows) || len(memberIDs) == 0 { @@ -471,6 +481,7 @@ func (h *Handler) renderHTMLWithState(r *http.Request, filePath string, state ht defer wg.Done() userAppearance, err := json.Marshal(codersdk.UserAppearanceSettings{ ThemePreference: themePreference, + TerminalFont: codersdk.TerminalFontName(terminalFont), }) if err == nil { state.UserAppearance = html.EscapeString(string(userAppearance)) diff --git a/site/src/api/queries/users.ts b/site/src/api/queries/users.ts index 5de828b6eac22..82b10213b4409 100644 --- a/site/src/api/queries/users.ts +++ b/site/src/api/queries/users.ts @@ -251,6 +251,7 @@ export const updateAppearanceSettings = ( // more responsive. queryClient.setQueryData(myAppearanceKey, { theme_preference: patch.theme_preference, + terminal_font: patch.terminal_font, }); }, onSuccess: async () => diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx index cda4ae198c444..219c3df892e6d 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx @@ -1,12 +1,26 @@ import type { Interpolation } from "@emotion/react"; +import CircularProgress from "@mui/material/CircularProgress"; +import FormControl from "@mui/material/FormControl"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import Radio from "@mui/material/Radio"; +import RadioGroup from "@mui/material/RadioGroup"; import { visuallyHidden } from "@mui/utils"; -import type { UpdateUserAppearanceSettingsRequest } from "api/typesGenerated"; +import { + type TerminalFontName, + TerminalFontNames, + type UpdateUserAppearanceSettingsRequest, +} from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { PreviewBadge } from "components/Badges/Badges"; import { Stack } from "components/Stack/Stack"; import { ThemeOverride } from "contexts/ThemeProvider"; import type { FC } from "react"; -import themes, { DEFAULT_TERMINAL_FONT, DEFAULT_THEME, type Theme } from "theme"; +import themes, { + DEFAULT_TERMINAL_FONT, + DEFAULT_THEME, + type Theme, +} from "theme"; +import { Section } from "../Section"; export interface AppearanceFormProps { isUpdating?: boolean; @@ -22,44 +36,107 @@ export const AppearanceForm: FC = ({ initialValues, }) => { const currentTheme = initialValues.theme_preference || DEFAULT_THEME; - const currentTerminalFont = initialValues.terminal_font || DEFAULT_TERMINAL_FONT; + const currentTerminalFont = + initialValues.terminal_font || DEFAULT_TERMINAL_FONT; const onChangeTheme = async (theme: string) => { if (isUpdating) { return; } + await onSubmit({ + theme_preference: theme, + terminal_font: currentTerminalFont, + }); + }; - await onSubmit({ theme_preference: theme, terminal_font: currentTerminalFont }); + const onChangeTerminalFont = async (terminalFont: TerminalFontName) => { + if (isUpdating) { + return; + } + await onSubmit({ + theme_preference: currentTheme, + terminal_font: terminalFont, + }); }; return (
{Boolean(error) && } - - onChangeTheme("auto")} - /> - onChangeTheme("dark")} - /> - onChangeTheme("light")} - /> - +
+ Theme + {isUpdating && } + + } + layout="fluid" + > + + onChangeTheme("auto")} + /> + onChangeTheme("dark")} + /> + onChangeTheme("light")} + /> + +
+
+
+ Terminal Font + {isUpdating && } + + } + layout="fluid" + > + + + onChangeTerminalFont(toTerminalFontName(value)) + } + > + } + label={ +
IBM Plex Mono
+ } + /> + } + label={
Fira Code
} + /> +
+
+
); }; +export function toTerminalFontName(value: string): TerminalFontName { + return TerminalFontNames.includes(value as TerminalFontName) + ? (value as TerminalFontName) + : ""; +} + interface AutoThemePreviewButtonProps extends Omit { themes: [Theme, Theme]; onSelect?: () => void; diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.tsx index 68621751da553..679ad6aeef3bd 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.tsx @@ -1,13 +1,10 @@ -import CircularProgress from "@mui/material/CircularProgress"; import { updateAppearanceSettings } from "api/queries/users"; import { appearanceSettings } from "api/queries/users"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; -import { Stack } from "components/Stack/Stack"; import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; import type { FC } from "react"; import { useMutation, useQuery, useQueryClient } from "react-query"; -import { Section } from "../Section"; import { AppearanceForm } from "./AppearanceForm"; export const AppearancePage: FC = () => { @@ -31,27 +28,15 @@ export const AppearancePage: FC = () => { return ( <> -
- Theme - {updateAppearanceSettingsMutation.isLoading && ( - - )} - - } - layout="fluid" - > - -
+ ); }; From 40ee7976eb2e1fa13e6cda648f395038654bb1bd Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 13:55:32 +0200 Subject: [PATCH 06/22] Terminal uses font --- site/src/pages/TerminalPage/TerminalPage.tsx | 22 ++++++++++++++++---- site/src/theme/constants.ts | 14 ++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/site/src/pages/TerminalPage/TerminalPage.tsx b/site/src/pages/TerminalPage/TerminalPage.tsx index d1ad415fb48e0..a6d6b712de497 100644 --- a/site/src/pages/TerminalPage/TerminalPage.tsx +++ b/site/src/pages/TerminalPage/TerminalPage.tsx @@ -7,18 +7,20 @@ import { WebLinksAddon } from "@xterm/addon-web-links"; import { WebglAddon } from "@xterm/addon-webgl"; import { Terminal } from "@xterm/xterm"; import { deploymentConfig } from "api/queries/deployment"; +import { appearanceSettings } from "api/queries/users"; import { workspaceByOwnerAndName, workspaceUsage, } from "api/queries/workspaces"; import { useProxy } from "contexts/ProxyContext"; import { ThemeOverride } from "contexts/ThemeProvider"; +import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; import { type FC, useCallback, useEffect, useRef, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { useNavigate, useParams, useSearchParams } from "react-router-dom"; -import themes from "theme"; -import { MONOSPACE_FONT_FAMILY, TERMINAL_FONT_FAMILY_2 } from "theme/constants"; +import themes, { DEFAULT_TERMINAL_FONT } from "theme"; +import { terminalFonts } from "theme/constants"; import { pageTitle } from "utils/page"; import { openMaybePortForwardedURL } from "utils/portForward"; import { terminalWebsocketUrl } from "utils/terminal"; @@ -100,6 +102,13 @@ const TerminalPage: FC = () => { handleWebLinkRef.current = handleWebLink; }, [handleWebLink]); + const { metadata } = useEmbeddedMetadata(); + const appearanceSettingsQuery = useQuery( + appearanceSettings(metadata.userAppearance), + ); + const currentTerminalFont = + appearanceSettingsQuery.data?.terminal_font || DEFAULT_TERMINAL_FONT; + // Create the terminal! const fitAddonRef = useRef(); useEffect(() => { @@ -110,7 +119,7 @@ const TerminalPage: FC = () => { allowProposedApi: true, allowTransparency: true, disableStdin: false, - fontFamily: TERMINAL_FONT_FAMILY_2, + fontFamily: terminalFonts[currentTerminalFont], fontSize: 16, theme: { background: theme.palette.background.default, @@ -150,7 +159,12 @@ const TerminalPage: FC = () => { window.removeEventListener("resize", listener); terminal.dispose(); }; - }, [config.isLoading, renderer, theme.palette.background.default]); + }, [ + config.isLoading, + renderer, + theme.palette.background.default, + currentTerminalFont, + ]); // Updates the reconnection token into the URL if necessary. useEffect(() => { diff --git a/site/src/theme/constants.ts b/site/src/theme/constants.ts index 63092b0f99650..e2d5a07a5026c 100644 --- a/site/src/theme/constants.ts +++ b/site/src/theme/constants.ts @@ -1,11 +1,15 @@ +import type { TerminalFontName } from "api/typesGenerated"; + export const borderRadius = 8; export const MONOSPACE_FONT_FAMILY = "'IBM Plex Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'Liberation Mono', 'Monaco', 'Courier New', Courier, monospace"; -export const TERMINAL_FONT_FAMILY_1 = MONOSPACE_FONT_FAMILY; -export const TERMINAL_FONT_FAMILY_2 = MONOSPACE_FONT_FAMILY.replace( - "IBM Plex Mono", - "Fira Code", -); +export const terminalFonts: Record = { + "fira-code": MONOSPACE_FONT_FAMILY.replace("IBM Plex Mono", "Fira Code"), + "ibm-plex-mono": MONOSPACE_FONT_FAMILY, + + "": MONOSPACE_FONT_FAMILY, +}; + export const BODY_FONT_FAMILY = `"Inter Variable", system-ui, sans-serif`; export const navHeight = 62; export const containerWidth = 1380; From ae1cb128e31fa0e0cdd6a021fd4a859ba00935a5 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 11:59:43 +0000 Subject: [PATCH 07/22] Fix golden --- cli/testdata/TestProvisioners_Golden/list.golden | 2 +- cli/testdata/coder_provisioner_list.golden | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/testdata/TestProvisioners_Golden/list.golden b/cli/testdata/TestProvisioners_Golden/list.golden index 35844d8b9c50e..3f50f90746744 100644 --- a/cli/testdata/TestProvisioners_Golden/list.golden +++ b/cli/testdata/TestProvisioners_Golden/list.golden @@ -1,4 +1,4 @@ -ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION +ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION 00000000-0000-0000-aaaa-000000000000 ====[timestamp]===== ====[timestamp]===== default-provisioner v0.0.0-devel map[owner: scope:organization] built-in idle 00000000-0000-0000-bbbb-000000000001 succeeded Coder 00000000-0000-0000-aaaa-000000000001 ====[timestamp]===== ====[timestamp]===== provisioner-1 v0.0.0 map[foo:bar owner: scope:organization] built-in busy 00000000-0000-0000-bbbb-000000000002 running Coder 00000000-0000-0000-aaaa-000000000002 ====[timestamp]===== ====[timestamp]===== provisioner-2 v0.0.0 map[owner: scope:organization] built-in offline 00000000-0000-0000-bbbb-000000000003 succeeded Coder diff --git a/cli/testdata/coder_provisioner_list.golden b/cli/testdata/coder_provisioner_list.golden index e34db5605fd81..64941eebf5b89 100644 --- a/cli/testdata/coder_provisioner_list.golden +++ b/cli/testdata/coder_provisioner_list.golden @@ -1,2 +1,2 @@ -CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS +CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS ====[timestamp]===== ====[timestamp]===== built-in test v0.0.0-devel idle map[owner: scope:organization] From 8f9e313e89be20603f6551114d7ae1869a64c116 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 14:03:37 +0200 Subject: [PATCH 08/22] fix version --- provisioner/terraform/testdata/resources/version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/terraform/testdata/resources/version.txt b/provisioner/terraform/testdata/resources/version.txt index 720c7384c6195..ca7176690dd6f 100644 --- a/provisioner/terraform/testdata/resources/version.txt +++ b/provisioner/terraform/testdata/resources/version.txt @@ -1 +1 @@ -1.11.1 +1.11.2 From 94a358798b411017dd57e51b37b4a259bdeda376 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 14:23:21 +0200 Subject: [PATCH 09/22] WIP --- coderd/users.go | 2 +- .../TerminalPage/TerminalPage.stories.tsx | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/coderd/users.go b/coderd/users.go index 030935871ce56..7af8fb6508d76 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -1035,7 +1035,7 @@ func (api *API) putUserAppearanceSettings(rw http.ResponseWriter, r *http.Reques }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error updating user theme prefence.", + Message: "Internal error updating user theme preference.", Detail: err.Error(), }) return diff --git a/site/src/pages/TerminalPage/TerminalPage.stories.tsx b/site/src/pages/TerminalPage/TerminalPage.stories.tsx index 4cf052668bb06..aa24485353894 100644 --- a/site/src/pages/TerminalPage/TerminalPage.stories.tsx +++ b/site/src/pages/TerminalPage/TerminalPage.stories.tsx @@ -17,6 +17,7 @@ import { MockEntitlements, MockExperiments, MockUser, + MockUserAppearanceSettings, MockWorkspace, MockWorkspaceAgent, } from "testHelpers/entities"; @@ -76,6 +77,7 @@ const meta = { key: getAuthorizationKey({ checks: permissionChecks }), data: { editWorkspaceProxies: true }, }, + { key: ["me", "appearance"], data: MockUserAppearanceSettings }, ], chromatic: { delay: 300 }, }, @@ -106,6 +108,38 @@ export const Starting: Story = { }, }; +export const FontFiraCode: Story = { + decorators: [withWebSocket], + parameters: { + ...meta.parameters, + webSocket: [ + { + event: "message", + // Copied and pasted this from browser + data: "➜ codergit:(bq/refactor-web-term-notifications) ✗", + }, + ], + queries: [ + ...meta.parameters.queries.filter( + (q) => + !( + Array.isArray(q.key) && + q.key[0] === "me" && + q.key[1] === "appearance" + ), + ), + { + key: ["me", "appearance"], + data: { + ...MockUserAppearanceSettings, + terminal_font: "fira-code", + }, + }, + createWorkspaceWithAgent("ready"), + ], + }, +}; + export const Ready: Story = { decorators: [withWebSocket], parameters: { From bdbae61d457906f2c32d199f23c284fafc9b8387 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 14:34:27 +0200 Subject: [PATCH 10/22] jest --- .../AppearancePage/AppearancePage.test.tsx | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.test.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.test.tsx index 5b8db73dd95ab..59dc62980b9f0 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.test.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.test.tsx @@ -12,14 +12,14 @@ describe("appearance page", () => { jest.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({ ...MockUser, theme_preference: "dark", - terminal_font: "", + terminal_font: "fira-code", }); const dark = await screen.findByText("Dark"); await userEvent.click(dark); // Check if the API was called correctly - expect(API.updateAppearanceSettings).toBeCalledTimes(0); + expect(API.updateAppearanceSettings).toHaveBeenCalledTimes(0); }); it("changes theme to light", async () => { @@ -27,17 +27,38 @@ describe("appearance page", () => { jest.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({ ...MockUser, + terminal_font: "ibm-plex-mono", theme_preference: "light", - terminal_font: "", }); const light = await screen.findByText("Light"); await userEvent.click(light); // Check if the API was called correctly - expect(API.updateAppearanceSettings).toBeCalledTimes(1); + expect(API.updateAppearanceSettings).toHaveBeenCalledTimes(1); expect(API.updateAppearanceSettings).toHaveBeenCalledWith({ + terminal_font: "ibm-plex-mono", theme_preference: "light", }); }); + + it("changes font to fira code", async () => { + renderWithAuth(); + + jest.spyOn(API, "updateAppearanceSettings").mockResolvedValueOnce({ + ...MockUser, + terminal_font: "fira-code", + theme_preference: "dark", + }); + + const ibmPlex = await screen.findByText("Fira Code"); + await userEvent.click(ibmPlex); + + // Check if the API was called correctly + expect(API.updateAppearanceSettings).toHaveBeenCalledTimes(1); + expect(API.updateAppearanceSettings).toHaveBeenCalledWith({ + terminal_font: "fira-code", + theme_preference: "dark", + }); + }); }); From 33a6177e8206b6f8f1c2817cb79500fdffb61b31 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 14:39:45 +0200 Subject: [PATCH 11/22] dbauthz_test.g --- coderd/database/dbauthz/dbauthz_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index b110c1b17b327..8525ffa1095f2 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -1651,6 +1651,27 @@ func (s *MethodTestSuite) TestUser() { ThemePreference: uc.Value, }).Asserts(u, policy.ActionUpdatePersonal).Returns(uc) })) + s.Run("GetUserTerminalFont", s.Subtest(func(db database.Store, check *expects) { + ctx := context.Background() + u := dbgen.User(s.T(), db, database.User{}) + db.UpdateUserTerminalFont(ctx, database.UpdateUserTerminalFontParams{ + UserID: u.ID, + TerminalFont: "ibm-plex-mono", + }) + check.Args(u.ID).Asserts(u, policy.ActionReadPersonal).Returns("ibm-plex-mono") + })) + s.Run("UpdateUserTerminalFont", s.Subtest(func(db database.Store, check *expects) { + u := dbgen.User(s.T(), db, database.User{}) + uc := database.UserConfig{ + UserID: u.ID, + Key: "terminal-font", + Value: "ibm-plex-mono", + } + check.Args(database.UpdateUserTerminalFontParams{ + UserID: u.ID, + TerminalFont: uc.Value, + }).Asserts(u, policy.ActionUpdatePersonal).Returns(uc) + })) s.Run("UpdateUserStatus", s.Subtest(func(db database.Store, check *expects) { u := dbgen.User(s.T(), db, database.User{}) check.Args(database.UpdateUserStatusParams{ From 23a0768802fd74833352de9d73a0c77b827f2085 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 15:15:13 +0200 Subject: [PATCH 12/22] check --- coderd/database/dbauthz/dbauthz_test.go | 2 +- coderd/users.go | 16 ++++++ coderd/users_test.go | 72 +++++++++++++++++++++++++ codersdk/users.go | 22 ++++++-- 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 8525ffa1095f2..bd6ef69528335 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -1664,7 +1664,7 @@ func (s *MethodTestSuite) TestUser() { u := dbgen.User(s.T(), db, database.User{}) uc := database.UserConfig{ UserID: u.ID, - Key: "terminal-font", + Key: "terminal_font", Value: "ibm-plex-mono", } check.Args(database.UpdateUserTerminalFontParams{ diff --git a/coderd/users.go b/coderd/users.go index 7af8fb6508d76..9abc761e3d6eb 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -1029,6 +1029,13 @@ func (api *API) putUserAppearanceSettings(rw http.ResponseWriter, r *http.Reques return } + if !isValidFontName(params.TerminalFont) { + httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ + Message: "Unsupported font family.", + }) + return + } + updatedThemePreference, err := api.Database.UpdateUserThemePreference(ctx, database.UpdateUserThemePreferenceParams{ UserID: user.ID, ThemePreference: params.ThemePreference, @@ -1059,6 +1066,15 @@ func (api *API) putUserAppearanceSettings(rw http.ResponseWriter, r *http.Reques }) } +func isValidFontName(font codersdk.TerminalFontName) bool { + switch font { + case codersdk.TerminalFontIbmPlexMono, codersdk.TerminalFontFiraCode, "": + return true + default: + return false + } +} + // @Summary Update user password // @ID update-user-password // @Security CoderSessionToken diff --git a/coderd/users_test.go b/coderd/users_test.go index c21eca85a5ee7..2d6f773ecc167 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1972,6 +1972,78 @@ func TestPostTokens(t *testing.T) { require.NoError(t, err) } +func TestUserTerminalFont(t *testing.T) { + t.Parallel() + + t.Run("valid font", func(t *testing.T) { + adminClient := coderdtest.New(t, nil) + firstUser := coderdtest.CreateFirstUser(t, adminClient) + client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + // given + initial, err := client.GetUserAppearanceSettings(ctx, "me") + require.Equal(t, codersdk.TerminalFontName(""), initial.TerminalFont) + + // when + updated, err := client.UpdateUserAppearanceSettings(ctx, "me", codersdk.UpdateUserAppearanceSettingsRequest{ + ThemePreference: "light", + TerminalFont: "fira-code", + }) + require.NoError(t, err) + + // then + require.Equal(t, codersdk.TerminalFontFiraCode, updated.TerminalFont) + }) + + t.Run("unsupported font", func(t *testing.T) { + adminClient := coderdtest.New(t, nil) + firstUser := coderdtest.CreateFirstUser(t, adminClient) + client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + // given + initial, err := client.GetUserAppearanceSettings(ctx, "me") + require.Equal(t, codersdk.TerminalFontName(""), initial.TerminalFont) + + // when + _, err = client.UpdateUserAppearanceSettings(ctx, "me", codersdk.UpdateUserAppearanceSettingsRequest{ + ThemePreference: "light", + TerminalFont: "foobar", + }) + + // then + require.Error(t, err) + }) + + t.Run("empty font is ok", func(t *testing.T) { + adminClient := coderdtest.New(t, nil) + firstUser := coderdtest.CreateFirstUser(t, adminClient) + client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + // given + initial, err := client.GetUserAppearanceSettings(ctx, "me") + require.Equal(t, codersdk.TerminalFontName(""), initial.TerminalFont) + + // when + updated, err := client.UpdateUserAppearanceSettings(ctx, "me", codersdk.UpdateUserAppearanceSettingsRequest{ + ThemePreference: "light", + TerminalFont: "", + }) + + // then + require.NoError(t, err) + require.Equal(t, codersdk.TerminalFontName(""), updated.TerminalFont) + }) +} + func TestWorkspacesByUser(t *testing.T) { t.Parallel() t.Run("Empty", func(t *testing.T) { diff --git a/codersdk/users.go b/codersdk/users.go index 9f136f00235db..a4c2eea1f7012 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -477,17 +477,31 @@ func (c *Client) UpdateUserStatus(ctx context.Context, user string, status UserS return resp, json.NewDecoder(res.Body).Decode(&resp) } +// GetUserAppearanceSettings fetches the appearance settings for a user. +func (c *Client) GetUserAppearanceSettings(ctx context.Context, user string) (UserAppearanceSettings, error) { + res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/appearance", user), nil) + if err != nil { + return UserAppearanceSettings{}, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return UserAppearanceSettings{}, ReadBodyAsError(res) + } + var resp UserAppearanceSettings + return resp, json.NewDecoder(res.Body).Decode(&resp) +} + // UpdateUserAppearanceSettings updates the appearance settings for a user. -func (c *Client) UpdateUserAppearanceSettings(ctx context.Context, user string, req UpdateUserAppearanceSettingsRequest) (User, error) { +func (c *Client) UpdateUserAppearanceSettings(ctx context.Context, user string, req UpdateUserAppearanceSettingsRequest) (UserAppearanceSettings, error) { res, err := c.Request(ctx, http.MethodPut, fmt.Sprintf("/api/v2/users/%s/appearance", user), req) if err != nil { - return User{}, err + return UserAppearanceSettings{}, err } defer res.Body.Close() if res.StatusCode != http.StatusOK { - return User{}, ReadBodyAsError(res) + return UserAppearanceSettings{}, ReadBodyAsError(res) } - var resp User + var resp UserAppearanceSettings return resp, json.NewDecoder(res.Body).Decode(&resp) } From 9bed6bc668f208f966778a2bab19314d037d0ff3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 15:35:21 +0200 Subject: [PATCH 13/22] go tests --- coderd/users_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/coderd/users_test.go b/coderd/users_test.go index 2d6f773ecc167..cbba319d1c0ce 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -2020,7 +2020,7 @@ func TestUserTerminalFont(t *testing.T) { require.Error(t, err) }) - t.Run("empty font is ok", func(t *testing.T) { + t.Run("undefined font is not ok", func(t *testing.T) { adminClient := coderdtest.New(t, nil) firstUser := coderdtest.CreateFirstUser(t, adminClient) client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) @@ -2033,14 +2033,13 @@ func TestUserTerminalFont(t *testing.T) { require.Equal(t, codersdk.TerminalFontName(""), initial.TerminalFont) // when - updated, err := client.UpdateUserAppearanceSettings(ctx, "me", codersdk.UpdateUserAppearanceSettingsRequest{ + _, err = client.UpdateUserAppearanceSettings(ctx, "me", codersdk.UpdateUserAppearanceSettingsRequest{ ThemePreference: "light", TerminalFont: "", }) // then - require.NoError(t, err) - require.Equal(t, codersdk.TerminalFontName(""), updated.TerminalFont) + require.Error(t, err) }) } From f4c24efe2b06fe328ced15dc4a881791a0e03bf0 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 15:41:54 +0200 Subject: [PATCH 14/22] fix --- coderd/users_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coderd/users_test.go b/coderd/users_test.go index cbba319d1c0ce..ce084500a0c25 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -2008,6 +2008,7 @@ func TestUserTerminalFont(t *testing.T) { // given initial, err := client.GetUserAppearanceSettings(ctx, "me") + require.NoError(t, err) require.Equal(t, codersdk.TerminalFontName(""), initial.TerminalFont) // when @@ -2030,6 +2031,7 @@ func TestUserTerminalFont(t *testing.T) { // given initial, err := client.GetUserAppearanceSettings(ctx, "me") + require.NoError(t, err) require.Equal(t, codersdk.TerminalFontName(""), initial.TerminalFont) // when From 449e079867fbde93247e8731eed3f4804e61c790 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 4 Apr 2025 15:47:32 +0200 Subject: [PATCH 15/22] t.Parallel --- coderd/users_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/coderd/users_test.go b/coderd/users_test.go index ce084500a0c25..fdaad21a826a9 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1976,6 +1976,8 @@ func TestUserTerminalFont(t *testing.T) { t.Parallel() t.Run("valid font", func(t *testing.T) { + t.Parallel() + adminClient := coderdtest.New(t, nil) firstUser := coderdtest.CreateFirstUser(t, adminClient) client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) @@ -1985,6 +1987,7 @@ func TestUserTerminalFont(t *testing.T) { // given initial, err := client.GetUserAppearanceSettings(ctx, "me") + require.NoError(t, err) require.Equal(t, codersdk.TerminalFontName(""), initial.TerminalFont) // when @@ -1999,6 +2002,8 @@ func TestUserTerminalFont(t *testing.T) { }) t.Run("unsupported font", func(t *testing.T) { + t.Parallel() + adminClient := coderdtest.New(t, nil) firstUser := coderdtest.CreateFirstUser(t, adminClient) client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) @@ -2022,6 +2027,8 @@ func TestUserTerminalFont(t *testing.T) { }) t.Run("undefined font is not ok", func(t *testing.T) { + t.Parallel() + adminClient := coderdtest.New(t, nil) firstUser := coderdtest.CreateFirstUser(t, adminClient) client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) From 2a68eebf9fa0d0e0029d563e60c051afa6c0a864 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 7 Apr 2025 13:05:38 +0200 Subject: [PATCH 16/22] Review fixes --- .../TestProvisioners_Golden/list.golden | 2 +- cli/testdata/coder_provisioner_list.golden | 2 +- coderd/apidoc/docs.go | 22 +++------------- coderd/apidoc/swagger.json | 16 +++--------- coderd/users.go | 8 ++---- codersdk/users.go | 8 +++--- docs/reference/api/schemas.md | 18 ++----------- docs/reference/api/users.md | 6 ++--- .../terraform/testdata/resources/version.txt | 2 +- .../AppearancePage/AppearanceForm.tsx | 25 ++++++++++--------- site/src/theme/index.ts | 9 +++++++ 11 files changed, 43 insertions(+), 75 deletions(-) diff --git a/cli/testdata/TestProvisioners_Golden/list.golden b/cli/testdata/TestProvisioners_Golden/list.golden index 3f50f90746744..35844d8b9c50e 100644 --- a/cli/testdata/TestProvisioners_Golden/list.golden +++ b/cli/testdata/TestProvisioners_Golden/list.golden @@ -1,4 +1,4 @@ -ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION +ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION 00000000-0000-0000-aaaa-000000000000 ====[timestamp]===== ====[timestamp]===== default-provisioner v0.0.0-devel map[owner: scope:organization] built-in idle 00000000-0000-0000-bbbb-000000000001 succeeded Coder 00000000-0000-0000-aaaa-000000000001 ====[timestamp]===== ====[timestamp]===== provisioner-1 v0.0.0 map[foo:bar owner: scope:organization] built-in busy 00000000-0000-0000-bbbb-000000000002 running Coder 00000000-0000-0000-aaaa-000000000002 ====[timestamp]===== ====[timestamp]===== provisioner-2 v0.0.0 map[owner: scope:organization] built-in offline 00000000-0000-0000-bbbb-000000000003 succeeded Coder diff --git a/cli/testdata/coder_provisioner_list.golden b/cli/testdata/coder_provisioner_list.golden index 64941eebf5b89..e34db5605fd81 100644 --- a/cli/testdata/coder_provisioner_list.golden +++ b/cli/testdata/coder_provisioner_list.golden @@ -1,2 +1,2 @@ -CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS +CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS ====[timestamp]===== ====[timestamp]===== built-in test v0.0.0-devel idle map[owner: scope:organization] diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 350486f25c8e0..61deac3008a8b 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -15604,7 +15604,7 @@ const docTemplate = `{ ], "x-enum-varnames": [ "TerminalFontUnknown", - "TerminalFontIbmPlexMono", + "TerminalFontIBMPlexMono", "TerminalFontFiraCode" ] }, @@ -15786,15 +15786,7 @@ const docTemplate = `{ ], "properties": { "terminal_font": { - "enum": [ - "ibm-plex-mono", - "fira-code" - ], - "allOf": [ - { - "$ref": "#/definitions/codersdk.TerminalFontName" - } - ] + "$ref": "#/definitions/codersdk.TerminalFontName" }, "theme_preference": { "type": "string" @@ -16088,15 +16080,7 @@ const docTemplate = `{ "type": "object", "properties": { "terminal_font": { - "enum": [ - "ibm-plex-mono", - "fira-code" - ], - "allOf": [ - { - "$ref": "#/definitions/codersdk.TerminalFontName" - } - ] + "$ref": "#/definitions/codersdk.TerminalFontName" }, "theme_preference": { "type": "string" diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 4f89e11c53582..0d2b63ef3131a 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -14185,7 +14185,7 @@ "enum": ["", "ibm-plex-mono", "fira-code"], "x-enum-varnames": [ "TerminalFontUnknown", - "TerminalFontIbmPlexMono", + "TerminalFontIBMPlexMono", "TerminalFontFiraCode" ] }, @@ -14362,12 +14362,7 @@ "required": ["terminal_font", "theme_preference"], "properties": { "terminal_font": { - "enum": ["ibm-plex-mono", "fira-code"], - "allOf": [ - { - "$ref": "#/definitions/codersdk.TerminalFontName" - } - ] + "$ref": "#/definitions/codersdk.TerminalFontName" }, "theme_preference": { "type": "string" @@ -14635,12 +14630,7 @@ "type": "object", "properties": { "terminal_font": { - "enum": ["ibm-plex-mono", "fira-code"], - "allOf": [ - { - "$ref": "#/definitions/codersdk.TerminalFontName" - } - ] + "$ref": "#/definitions/codersdk.TerminalFontName" }, "theme_preference": { "type": "string" diff --git a/coderd/users.go b/coderd/users.go index 9abc761e3d6eb..03f900c01ddeb 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net/http" + "slices" "github.com/go-chi/chi/v5" "github.com/go-chi/render" @@ -1067,12 +1068,7 @@ func (api *API) putUserAppearanceSettings(rw http.ResponseWriter, r *http.Reques } func isValidFontName(font codersdk.TerminalFontName) bool { - switch font { - case codersdk.TerminalFontIbmPlexMono, codersdk.TerminalFontFiraCode, "": - return true - default: - return false - } + return slices.Contains(codersdk.TerminalFontNames, font) } // @Summary Update user password diff --git a/codersdk/users.go b/codersdk/users.go index a4c2eea1f7012..bdc9b521367f0 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -192,20 +192,22 @@ type ValidateUserPasswordResponse struct { // TerminalFontName is the name of supported terminal font type TerminalFontName string +var TerminalFontNames = []TerminalFontName{TerminalFontUnknown, TerminalFontIBMPlexMono, TerminalFontFiraCode} + const ( TerminalFontUnknown TerminalFontName = "" - TerminalFontIbmPlexMono TerminalFontName = "ibm-plex-mono" + TerminalFontIBMPlexMono TerminalFontName = "ibm-plex-mono" TerminalFontFiraCode TerminalFontName = "fira-code" ) type UserAppearanceSettings struct { ThemePreference string `json:"theme_preference"` - TerminalFont TerminalFontName `json:"terminal_font" enums:"ibm-plex-mono,fira-code"` + TerminalFont TerminalFontName `json:"terminal_font"` } type UpdateUserAppearanceSettingsRequest struct { ThemePreference string `json:"theme_preference" validate:"required"` - TerminalFont TerminalFontName `json:"terminal_font" validate:"required" enums:"ibm-plex-mono,fira-code"` + TerminalFont TerminalFontName `json:"terminal_font" validate:"required"` } type UpdateUserPasswordRequest struct { diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 5b3b132b6546c..1359c2feec9fb 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -6925,7 +6925,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W ```json { - "terminal_font": "ibm-plex-mono", + "terminal_font": "", "theme_preference": "string" } ``` @@ -6937,13 +6937,6 @@ Restarts will only happen on weekdays in this list on weeks which line up with W | `terminal_font` | [codersdk.TerminalFontName](#codersdkterminalfontname) | true | | | | `theme_preference` | string | true | | | -#### Enumerated Values - -| Property | Value | -|-----------------|-----------------| -| `terminal_font` | `ibm-plex-mono` | -| `terminal_font` | `fira-code` | - ## codersdk.UpdateUserNotificationPreferences ```json @@ -7285,7 +7278,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| ```json { - "terminal_font": "ibm-plex-mono", + "terminal_font": "", "theme_preference": "string" } ``` @@ -7297,13 +7290,6 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `terminal_font` | [codersdk.TerminalFontName](#codersdkterminalfontname) | false | | | | `theme_preference` | string | false | | | -#### Enumerated Values - -| Property | Value | -|-----------------|-----------------| -| `terminal_font` | `ibm-plex-mono` | -| `terminal_font` | `fira-code` | - ## codersdk.UserLatency ```json diff --git a/docs/reference/api/users.md b/docs/reference/api/users.md index 61f4d390fccd4..43842fde6539b 100644 --- a/docs/reference/api/users.md +++ b/docs/reference/api/users.md @@ -501,7 +501,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/appearance \ ```json { - "terminal_font": "ibm-plex-mono", + "terminal_font": "", "theme_preference": "string" } ``` @@ -532,7 +532,7 @@ curl -X PUT http://coder-server:8080/api/v2/users/{user}/appearance \ ```json { - "terminal_font": "ibm-plex-mono", + "terminal_font": "", "theme_preference": "string" } ``` @@ -550,7 +550,7 @@ curl -X PUT http://coder-server:8080/api/v2/users/{user}/appearance \ ```json { - "terminal_font": "ibm-plex-mono", + "terminal_font": "", "theme_preference": "string" } ``` diff --git a/provisioner/terraform/testdata/resources/version.txt b/provisioner/terraform/testdata/resources/version.txt index ca7176690dd6f..720c7384c6195 100644 --- a/provisioner/terraform/testdata/resources/version.txt +++ b/provisioner/terraform/testdata/resources/version.txt @@ -1 +1 @@ -1.11.2 +1.11.1 diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx index 219c3df892e6d..9d8761305583e 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx @@ -18,6 +18,7 @@ import type { FC } from "react"; import themes, { DEFAULT_TERMINAL_FONT, DEFAULT_THEME, + fontLabels, type Theme, } from "theme"; import { Section } from "../Section"; @@ -112,18 +113,18 @@ export const AppearanceForm: FC = ({ onChangeTerminalFont(toTerminalFontName(value)) } > - } - label={ -
IBM Plex Mono
- } - /> - } - label={
Fira Code
} - /> + {TerminalFontNames.filter((name) => name !== "").map((name) => ( + } + label={ +
+ IBM Plex Mono +
+ } + /> + ))} diff --git a/site/src/theme/index.ts b/site/src/theme/index.ts index 504241c8f8e0f..f9ca4cc82f134 100644 --- a/site/src/theme/index.ts +++ b/site/src/theme/index.ts @@ -1,7 +1,10 @@ // biome-ignore lint/nursery/noRestrictedImports: We still use `Theme` as a basis for our actual theme, for now. import type { Theme as MuiTheme } from "@mui/material/styles"; +import type { TerminalFontName } from "api/typesGenerated"; import type * as monaco from "monaco-editor"; +import { of } from "rxjs"; import type { Branding } from "./branding"; +import { terminalFonts } from "./constants"; import dark from "./dark"; import type { NewTheme } from "./experimental"; import type { ExternalImageModeStyles } from "./externalImages"; @@ -37,4 +40,10 @@ const theme = { export default theme; +export const fontLabels: Record = { + "fira-code": "Fira Code", + "ibm-plex-mono": "IBM Plex Mono", + "": "", // needed for enum completeness, otherwise fails the build +}; + export const DEFAULT_TERMINAL_FONT = "ibm-plex-mono"; From b9fa3fef31ab09578a5a447124be554b16b0ac55 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 7 Apr 2025 11:10:46 +0000 Subject: [PATCH 17/22] make gen --- cli/testdata/TestProvisioners_Golden/list.golden | 2 +- cli/testdata/coder_provisioner_list.golden | 2 +- provisioner/terraform/testdata/resources/version.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/testdata/TestProvisioners_Golden/list.golden b/cli/testdata/TestProvisioners_Golden/list.golden index 35844d8b9c50e..3f50f90746744 100644 --- a/cli/testdata/TestProvisioners_Golden/list.golden +++ b/cli/testdata/TestProvisioners_Golden/list.golden @@ -1,4 +1,4 @@ -ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION +ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION 00000000-0000-0000-aaaa-000000000000 ====[timestamp]===== ====[timestamp]===== default-provisioner v0.0.0-devel map[owner: scope:organization] built-in idle 00000000-0000-0000-bbbb-000000000001 succeeded Coder 00000000-0000-0000-aaaa-000000000001 ====[timestamp]===== ====[timestamp]===== provisioner-1 v0.0.0 map[foo:bar owner: scope:organization] built-in busy 00000000-0000-0000-bbbb-000000000002 running Coder 00000000-0000-0000-aaaa-000000000002 ====[timestamp]===== ====[timestamp]===== provisioner-2 v0.0.0 map[owner: scope:organization] built-in offline 00000000-0000-0000-bbbb-000000000003 succeeded Coder diff --git a/cli/testdata/coder_provisioner_list.golden b/cli/testdata/coder_provisioner_list.golden index e34db5605fd81..64941eebf5b89 100644 --- a/cli/testdata/coder_provisioner_list.golden +++ b/cli/testdata/coder_provisioner_list.golden @@ -1,2 +1,2 @@ -CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS +CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS ====[timestamp]===== ====[timestamp]===== built-in test v0.0.0-devel idle map[owner: scope:organization] diff --git a/provisioner/terraform/testdata/resources/version.txt b/provisioner/terraform/testdata/resources/version.txt index 720c7384c6195..0a5af26df3fdb 100644 --- a/provisioner/terraform/testdata/resources/version.txt +++ b/provisioner/terraform/testdata/resources/version.txt @@ -1 +1 @@ -1.11.1 +1.11.3 From 45245fc6808e0f4d661aa0f4b0c861104865ba27 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 7 Apr 2025 14:06:57 +0200 Subject: [PATCH 18/22] fixes --- .../UserSettingsPage/AppearancePage/AppearanceForm.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx index 9d8761305583e..079ed3c523ddb 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx @@ -106,9 +106,9 @@ export const AppearanceForm: FC = ({ > onChangeTerminalFont(toTerminalFontName(value)) } @@ -120,7 +120,7 @@ export const AppearanceForm: FC = ({ control={} label={
- IBM Plex Mono + {fontLabels[name]}
} /> From 922bac233ea41427f4ddcc562150640a9b82534a Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 7 Apr 2025 14:07:30 +0200 Subject: [PATCH 19/22] fix --- provisioner/terraform/testdata/resources/version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/terraform/testdata/resources/version.txt b/provisioner/terraform/testdata/resources/version.txt index 0a5af26df3fdb..ca7176690dd6f 100644 --- a/provisioner/terraform/testdata/resources/version.txt +++ b/provisioner/terraform/testdata/resources/version.txt @@ -1 +1 @@ -1.11.3 +1.11.2 From 59854a66eb24902ed673490b1340249498edfd86 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 7 Apr 2025 14:11:05 +0200 Subject: [PATCH 20/22] Fix --- .../AppearancePage/AppearanceForm.tsx | 12 ++++-------- site/src/theme/constants.ts | 9 ++++++++- site/src/theme/index.ts | 10 ---------- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx index 079ed3c523ddb..9ecee2dfac83a 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx @@ -15,12 +15,8 @@ import { PreviewBadge } from "components/Badges/Badges"; import { Stack } from "components/Stack/Stack"; import { ThemeOverride } from "contexts/ThemeProvider"; import type { FC } from "react"; -import themes, { - DEFAULT_TERMINAL_FONT, - DEFAULT_THEME, - fontLabels, - type Theme, -} from "theme"; +import themes, { DEFAULT_THEME, type Theme } from "theme"; +import { DEFAULT_TERMINAL_FONT, terminalFontLabels } from "theme/constants"; import { Section } from "../Section"; export interface AppearanceFormProps { @@ -119,8 +115,8 @@ export const AppearanceForm: FC = ({ value={name} control={} label={ -
- {fontLabels[name]} +
+ {terminalFontLabels[name]}
} /> diff --git a/site/src/theme/constants.ts b/site/src/theme/constants.ts index e2d5a07a5026c..162e67310749c 100644 --- a/site/src/theme/constants.ts +++ b/site/src/theme/constants.ts @@ -3,14 +3,21 @@ import type { TerminalFontName } from "api/typesGenerated"; export const borderRadius = 8; export const MONOSPACE_FONT_FAMILY = "'IBM Plex Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'Liberation Mono', 'Monaco', 'Courier New', Courier, monospace"; +export const BODY_FONT_FAMILY = `"Inter Variable", system-ui, sans-serif`; + export const terminalFonts: Record = { "fira-code": MONOSPACE_FONT_FAMILY.replace("IBM Plex Mono", "Fira Code"), "ibm-plex-mono": MONOSPACE_FONT_FAMILY, "": MONOSPACE_FONT_FAMILY, }; +export const terminalFontLabels: Record = { + "fira-code": "Fira Code", + "ibm-plex-mono": "IBM Plex Mono", + "": "", // needed for enum completeness, otherwise fails the build +}; +export const DEFAULT_TERMINAL_FONT = "ibm-plex-mono"; -export const BODY_FONT_FAMILY = `"Inter Variable", system-ui, sans-serif`; export const navHeight = 62; export const containerWidth = 1380; export const containerWidthMedium = 1080; diff --git a/site/src/theme/index.ts b/site/src/theme/index.ts index f9ca4cc82f134..15dc4349dc238 100644 --- a/site/src/theme/index.ts +++ b/site/src/theme/index.ts @@ -2,9 +2,7 @@ import type { Theme as MuiTheme } from "@mui/material/styles"; import type { TerminalFontName } from "api/typesGenerated"; import type * as monaco from "monaco-editor"; -import { of } from "rxjs"; import type { Branding } from "./branding"; -import { terminalFonts } from "./constants"; import dark from "./dark"; import type { NewTheme } from "./experimental"; import type { ExternalImageModeStyles } from "./externalImages"; @@ -39,11 +37,3 @@ const theme = { } satisfies Record; export default theme; - -export const fontLabels: Record = { - "fira-code": "Fira Code", - "ibm-plex-mono": "IBM Plex Mono", - "": "", // needed for enum completeness, otherwise fails the build -}; - -export const DEFAULT_TERMINAL_FONT = "ibm-plex-mono"; From 61fca63a6491162ac15fd13dfe372eecd33349ba Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 7 Apr 2025 14:12:21 +0200 Subject: [PATCH 21/22] Fix --- site/src/pages/TerminalPage/TerminalPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/TerminalPage/TerminalPage.tsx b/site/src/pages/TerminalPage/TerminalPage.tsx index a6d6b712de497..9740e239233a4 100644 --- a/site/src/pages/TerminalPage/TerminalPage.tsx +++ b/site/src/pages/TerminalPage/TerminalPage.tsx @@ -19,8 +19,8 @@ import { type FC, useCallback, useEffect, useRef, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { useNavigate, useParams, useSearchParams } from "react-router-dom"; -import themes, { DEFAULT_TERMINAL_FONT } from "theme"; -import { terminalFonts } from "theme/constants"; +import themes from "theme"; +import { DEFAULT_TERMINAL_FONT, terminalFonts } from "theme/constants"; import { pageTitle } from "utils/page"; import { openMaybePortForwardedURL } from "utils/portForward"; import { terminalWebsocketUrl } from "utils/terminal"; From b22e626500e8d9942717b92b74bf9781fa7614d3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 7 Apr 2025 14:15:31 +0200 Subject: [PATCH 22/22] cleanup --- site/src/theme/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/site/src/theme/index.ts b/site/src/theme/index.ts index 15dc4349dc238..a36bd9b223e8d 100644 --- a/site/src/theme/index.ts +++ b/site/src/theme/index.ts @@ -1,6 +1,5 @@ // biome-ignore lint/nursery/noRestrictedImports: We still use `Theme` as a basis for our actual theme, for now. import type { Theme as MuiTheme } from "@mui/material/styles"; -import type { TerminalFontName } from "api/typesGenerated"; import type * as monaco from "monaco-editor"; import type { Branding } from "./branding"; import dark from "./dark";