ソースを参照

Build dark themed meetings dashboard UI.

Implement the first-page layout with refined navigation, meeting tabs, schedule cards, and a tuned color/typography system to match the requested design updates.

Made-with: Cursor
huzaifahayat12 2 週間 前
コミット
c02309f5c2
共有2 個のファイルを変更した469 個の追加5 個の削除を含む
  1. 2 1
      meetings_app/AppDelegate.swift
  2. 467 4
      meetings_app/ViewController.swift

+ 2 - 1
meetings_app/AppDelegate.swift

@@ -14,7 +14,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
14 14
 
15 15
 
16 16
     func applicationDidFinishLaunching(_ aNotification: Notification) {
17
-        // Insert code here to initialize your application
17
+        // Force app-wide dark appearance for consistent dark UI.
18
+        NSApp.appearance = NSAppearance(named: .darkAqua)
18 19
     }
19 20
 
20 21
     func applicationWillTerminate(_ aNotification: Notification) {

+ 467 - 4
meetings_app/ViewController.swift

@@ -7,20 +7,483 @@
7 7
 
8 8
 import Cocoa
9 9
 
10
-class ViewController: NSViewController {
10
+final class ViewController: NSViewController {
11
+    private let palette = Palette()
12
+    private let typography = Typography()
11 13
 
12 14
     override func viewDidLoad() {
13 15
         super.viewDidLoad()
16
+        setupRootView()
17
+        buildMainLayout()
18
+    }
14 19
 
15
-        // Do any additional setup after loading the view.
20
+    override func viewDidAppear() {
21
+        super.viewDidAppear()
22
+        view.window?.setContentSize(NSSize(width: 1120, height: 690))
23
+        view.window?.minSize = NSSize(width: 940, height: 600)
24
+        view.window?.title = "App for Google Meet"
16 25
     }
17 26
 
18 27
     override var representedObject: Any? {
19
-        didSet {
20
-        // Update the view, if already loaded.
28
+        didSet {}
29
+    }
30
+}
31
+
32
+private extension ViewController {
33
+    func setupRootView() {
34
+        view.appearance = NSAppearance(named: .darkAqua)
35
+        view.wantsLayer = true
36
+        view.layer?.backgroundColor = palette.pageBackground.cgColor
37
+    }
38
+
39
+    func buildMainLayout() {
40
+        let splitContainer = NSStackView()
41
+        splitContainer.translatesAutoresizingMaskIntoConstraints = false
42
+        splitContainer.orientation = .horizontal
43
+        splitContainer.spacing = 0
44
+        splitContainer.alignment = .top
45
+        view.addSubview(splitContainer)
46
+
47
+        NSLayoutConstraint.activate([
48
+            splitContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor),
49
+            splitContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor),
50
+            splitContainer.topAnchor.constraint(equalTo: view.topAnchor),
51
+            splitContainer.bottomAnchor.constraint(equalTo: view.bottomAnchor)
52
+        ])
53
+
54
+        let sidebar = makeSidebar()
55
+        let mainPanel = makeMainPanel()
56
+        splitContainer.addArrangedSubview(sidebar)
57
+        splitContainer.addArrangedSubview(mainPanel)
58
+    }
59
+
60
+    func makeSidebar() -> NSView {
61
+        let sidebar = NSView()
62
+        sidebar.translatesAutoresizingMaskIntoConstraints = false
63
+        sidebar.wantsLayer = true
64
+        sidebar.layer?.backgroundColor = palette.sidebarBackground.cgColor
65
+        sidebar.layer?.borderColor = palette.separator.cgColor
66
+        sidebar.layer?.borderWidth = 1
67
+        sidebar.layer?.shadowColor = NSColor.black.cgColor
68
+        sidebar.layer?.shadowOpacity = 0.25
69
+        sidebar.layer?.shadowOffset = CGSize(width: 2, height: 0)
70
+        sidebar.layer?.shadowRadius = 10
71
+        sidebar.widthAnchor.constraint(equalToConstant: 210).isActive = true
72
+
73
+        let titleRow = NSStackView(views: [
74
+            iconLabel("📅", size: 24),
75
+            textLabel("Meetings", font: typography.sidebarBrand, color: palette.textPrimary)
76
+        ])
77
+        titleRow.translatesAutoresizingMaskIntoConstraints = false
78
+        titleRow.orientation = .horizontal
79
+        titleRow.alignment = .centerY
80
+        titleRow.spacing = 8
81
+
82
+        let menuStack = NSStackView()
83
+        menuStack.translatesAutoresizingMaskIntoConstraints = false
84
+        menuStack.orientation = .vertical
85
+        menuStack.spacing = 10
86
+
87
+        menuStack.addArrangedSubview(sidebarSectionTitle("Meetings"))
88
+        menuStack.addArrangedSubview(sidebarItem("Join Meetings", icon: "􀉣", selected: true))
89
+        menuStack.addArrangedSubview(sidebarSectionTitle("Backgrounds"))
90
+        menuStack.addArrangedSubview(sidebarItem("Photo", icon: "􀏂", selected: false))
91
+        menuStack.addArrangedSubview(sidebarItem("Video", icon: "􀎚", selected: false))
92
+        menuStack.addArrangedSubview(sidebarSectionTitle("Additional"))
93
+        menuStack.addArrangedSubview(sidebarItem("Tutorials", icon: "􀛩", selected: false))
94
+        menuStack.addArrangedSubview(sidebarItem("Settings", icon: "􀍟", selected: false))
95
+
96
+        sidebar.addSubview(titleRow)
97
+        sidebar.addSubview(menuStack)
98
+
99
+        NSLayoutConstraint.activate([
100
+            titleRow.leadingAnchor.constraint(equalTo: sidebar.leadingAnchor, constant: 16),
101
+            titleRow.topAnchor.constraint(equalTo: sidebar.topAnchor, constant: 24),
102
+            titleRow.trailingAnchor.constraint(lessThanOrEqualTo: sidebar.trailingAnchor, constant: -16),
103
+
104
+            menuStack.leadingAnchor.constraint(equalTo: sidebar.leadingAnchor, constant: 12),
105
+            menuStack.trailingAnchor.constraint(equalTo: sidebar.trailingAnchor, constant: -12),
106
+            menuStack.topAnchor.constraint(equalTo: titleRow.bottomAnchor, constant: 20)
107
+        ])
108
+
109
+        return sidebar
110
+    }
111
+
112
+    func makeMainPanel() -> NSView {
113
+        let panel = NSView()
114
+        panel.translatesAutoresizingMaskIntoConstraints = false
115
+        panel.wantsLayer = true
116
+        panel.layer?.backgroundColor = palette.pageBackground.cgColor
117
+
118
+        let contentStack = NSStackView()
119
+        contentStack.translatesAutoresizingMaskIntoConstraints = false
120
+        contentStack.orientation = .vertical
121
+        contentStack.spacing = 14
122
+        contentStack.alignment = .leading
123
+
124
+        contentStack.addArrangedSubview(textLabel("Join Meetings", font: typography.pageTitle, color: palette.textPrimary))
125
+        contentStack.addArrangedSubview(meetingTypeTabs())
126
+        contentStack.addArrangedSubview(textLabel("Join with URL", font: typography.sectionTitle, color: palette.textPrimary))
127
+        contentStack.addArrangedSubview(meetingUrlSection())
128
+        contentStack.addArrangedSubview(scheduleHeader())
129
+        contentStack.addArrangedSubview(textLabel("Tuesday, 14 Apr", font: typography.dateHeading, color: palette.textSecondary))
130
+        contentStack.addArrangedSubview(scheduleCardsRow())
131
+
132
+        panel.addSubview(contentStack)
133
+
134
+        NSLayoutConstraint.activate([
135
+            contentStack.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 28),
136
+            contentStack.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -28),
137
+            contentStack.topAnchor.constraint(equalTo: panel.topAnchor, constant: 26)
138
+        ])
139
+
140
+        return panel
141
+    }
142
+
143
+    func meetingTypeTabs() -> NSView {
144
+        let shell = roundedContainer(cornerRadius: 20, color: palette.tabBarBackground)
145
+        shell.translatesAutoresizingMaskIntoConstraints = false
146
+        shell.heightAnchor.constraint(equalToConstant: 56).isActive = true
147
+
148
+        let stack = NSStackView()
149
+        stack.translatesAutoresizingMaskIntoConstraints = false
150
+        stack.orientation = .horizontal
151
+        stack.distribution = .fillEqually
152
+        stack.spacing = 10
153
+
154
+        stack.addArrangedSubview(topTab("Meet", icon: "􀤆", selected: true))
155
+        stack.addArrangedSubview(topTab("Zoom", icon: "􀤉", selected: false))
156
+        stack.addArrangedSubview(topTab("Teams", icon: "􀉨", selected: false))
157
+        stack.addArrangedSubview(topTab("Zoho", icon: "􀯶", selected: false))
158
+
159
+        shell.addSubview(stack)
160
+        NSLayoutConstraint.activate([
161
+            shell.widthAnchor.constraint(greaterThanOrEqualToConstant: 700),
162
+            stack.leadingAnchor.constraint(equalTo: shell.leadingAnchor, constant: 10),
163
+            stack.trailingAnchor.constraint(equalTo: shell.trailingAnchor, constant: -10),
164
+            stack.topAnchor.constraint(equalTo: shell.topAnchor, constant: 8),
165
+            stack.bottomAnchor.constraint(equalTo: shell.bottomAnchor, constant: -8)
166
+        ])
167
+
168
+        return shell
169
+    }
170
+
171
+    func meetingUrlSection() -> NSView {
172
+        let wrapper = NSView()
173
+        wrapper.translatesAutoresizingMaskIntoConstraints = false
174
+
175
+        let title = textLabel("Meeting URL", font: typography.fieldLabel, color: palette.textSecondary)
176
+        let textFieldContainer = roundedContainer(cornerRadius: 10, color: palette.inputBackground)
177
+        textFieldContainer.translatesAutoresizingMaskIntoConstraints = false
178
+        textFieldContainer.heightAnchor.constraint(equalToConstant: 40).isActive = true
179
+        styleSurface(textFieldContainer, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
180
+
181
+        let placeholder = textLabel("Enter meeting URL...", font: typography.inputPlaceholder, color: palette.textMuted)
182
+        placeholder.translatesAutoresizingMaskIntoConstraints = false
183
+        textFieldContainer.addSubview(placeholder)
184
+
185
+        let actions = NSStackView()
186
+        actions.orientation = .horizontal
187
+        actions.spacing = 10
188
+        actions.translatesAutoresizingMaskIntoConstraints = false
189
+        actions.alignment = .centerY
190
+        actions.addArrangedSubview(actionButton(title: "Cancel", color: palette.cancelButton, textColor: palette.textSecondary, width: 110))
191
+        actions.addArrangedSubview(actionButton(title: "Join", color: palette.primaryBlue, textColor: .white, width: 116))
192
+
193
+        wrapper.addSubview(title)
194
+        wrapper.addSubview(textFieldContainer)
195
+        wrapper.addSubview(actions)
196
+
197
+        NSLayoutConstraint.activate([
198
+            wrapper.widthAnchor.constraint(greaterThanOrEqualToConstant: 780),
199
+
200
+            title.leadingAnchor.constraint(equalTo: wrapper.leadingAnchor),
201
+            title.topAnchor.constraint(equalTo: wrapper.topAnchor),
202
+
203
+            textFieldContainer.leadingAnchor.constraint(equalTo: wrapper.leadingAnchor),
204
+            textFieldContainer.trailingAnchor.constraint(equalTo: wrapper.trailingAnchor),
205
+            textFieldContainer.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 10),
206
+
207
+            placeholder.leadingAnchor.constraint(equalTo: textFieldContainer.leadingAnchor, constant: 12),
208
+            placeholder.centerYAnchor.constraint(equalTo: textFieldContainer.centerYAnchor),
209
+
210
+            actions.trailingAnchor.constraint(equalTo: wrapper.trailingAnchor),
211
+            actions.topAnchor.constraint(equalTo: textFieldContainer.bottomAnchor, constant: 14),
212
+            actions.bottomAnchor.constraint(equalTo: wrapper.bottomAnchor)
213
+        ])
214
+
215
+        return wrapper
216
+    }
217
+
218
+    func scheduleHeader() -> NSView {
219
+        let row = NSStackView()
220
+        row.translatesAutoresizingMaskIntoConstraints = false
221
+        row.orientation = .horizontal
222
+        row.alignment = .centerY
223
+        row.distribution = .fill
224
+        row.spacing = 12
225
+
226
+        row.addArrangedSubview(textLabel("Schedule", font: typography.sectionTitleBold, color: palette.textPrimary))
227
+
228
+        let spacer = NSView()
229
+        spacer.translatesAutoresizingMaskIntoConstraints = false
230
+        row.addArrangedSubview(spacer)
231
+        spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
232
+
233
+        row.addArrangedSubview(iconRoundButton("?", size: 34))
234
+        row.addArrangedSubview(iconRoundButton("⟳", size: 34))
235
+
236
+        let filter = roundedContainer(cornerRadius: 8, color: palette.inputBackground)
237
+        filter.translatesAutoresizingMaskIntoConstraints = false
238
+        filter.widthAnchor.constraint(equalToConstant: 156).isActive = true
239
+        filter.heightAnchor.constraint(equalToConstant: 34).isActive = true
240
+        styleSurface(filter, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
241
+        let filterText = textLabel("All", font: typography.filterText, color: palette.textSecondary)
242
+        let arrow = textLabel("▾", font: typography.filterArrow, color: palette.textMuted)
243
+        filterText.translatesAutoresizingMaskIntoConstraints = false
244
+        arrow.translatesAutoresizingMaskIntoConstraints = false
245
+        filter.addSubview(filterText)
246
+        filter.addSubview(arrow)
247
+
248
+        NSLayoutConstraint.activate([
249
+            filterText.leadingAnchor.constraint(equalTo: filter.leadingAnchor, constant: 12),
250
+            filterText.centerYAnchor.constraint(equalTo: filter.centerYAnchor),
251
+            arrow.trailingAnchor.constraint(equalTo: filter.trailingAnchor, constant: -10),
252
+            arrow.centerYAnchor.constraint(equalTo: filter.centerYAnchor)
253
+        ])
254
+
255
+        row.addArrangedSubview(filter)
256
+        row.widthAnchor.constraint(greaterThanOrEqualToConstant: 780).isActive = true
257
+        return row
258
+    }
259
+
260
+    func scheduleCardsRow() -> NSView {
261
+        let row = NSStackView()
262
+        row.translatesAutoresizingMaskIntoConstraints = false
263
+        row.orientation = .horizontal
264
+        row.spacing = 14
265
+        row.alignment = .top
266
+        row.distribution = .fillEqually
267
+        row.widthAnchor.constraint(greaterThanOrEqualToConstant: 780).isActive = true
268
+        row.heightAnchor.constraint(equalToConstant: 168).isActive = true
269
+
270
+        row.addArrangedSubview(scheduleCard())
271
+        row.addArrangedSubview(scheduleCard())
272
+        return row
273
+    }
274
+
275
+    func scheduleCard() -> NSView {
276
+        let card = roundedContainer(cornerRadius: 12, color: palette.sectionCard)
277
+        styleSurface(card, borderColor: palette.inputBorder, borderWidth: 1, shadow: true)
278
+        card.translatesAutoresizingMaskIntoConstraints = false
279
+        card.heightAnchor.constraint(equalToConstant: 168).isActive = true
280
+
281
+        let icon = roundedContainer(cornerRadius: 7, color: palette.meetingBadge)
282
+        icon.translatesAutoresizingMaskIntoConstraints = false
283
+        icon.widthAnchor.constraint(equalToConstant: 30).isActive = true
284
+        icon.heightAnchor.constraint(equalToConstant: 30).isActive = true
285
+        let iconText = textLabel("••", font: typography.cardIcon, color: .white)
286
+        iconText.translatesAutoresizingMaskIntoConstraints = false
287
+        icon.addSubview(iconText)
288
+        NSLayoutConstraint.activate([
289
+            iconText.centerXAnchor.constraint(equalTo: icon.centerXAnchor),
290
+            iconText.centerYAnchor.constraint(equalTo: icon.centerYAnchor)
291
+        ])
292
+
293
+        let title = textLabel("General Meeting", font: typography.cardTitle, color: palette.textPrimary)
294
+        let subtitle = textLabel("Baisakhi", font: typography.cardSubtitle, color: palette.textSecondary)
295
+        let time = textLabel("12:00 AM - 11:59 PM", font: typography.cardTime, color: palette.textTertiary)
296
+
297
+        card.addSubview(icon)
298
+        card.addSubview(title)
299
+        card.addSubview(subtitle)
300
+        card.addSubview(time)
301
+
302
+        NSLayoutConstraint.activate([
303
+            icon.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 14),
304
+            icon.topAnchor.constraint(equalTo: card.topAnchor, constant: 14),
305
+
306
+            title.leadingAnchor.constraint(equalTo: icon.trailingAnchor, constant: 8),
307
+            title.centerYAnchor.constraint(equalTo: icon.centerYAnchor),
308
+            title.trailingAnchor.constraint(lessThanOrEqualTo: card.trailingAnchor, constant: -12),
309
+
310
+            subtitle.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 14),
311
+            subtitle.topAnchor.constraint(equalTo: icon.bottomAnchor, constant: 12),
312
+
313
+            time.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 14),
314
+            time.topAnchor.constraint(equalTo: subtitle.bottomAnchor, constant: 8)
315
+        ])
316
+
317
+        return card
318
+    }
319
+}
320
+
321
+private extension ViewController {
322
+    func roundedContainer(cornerRadius: CGFloat, color: NSColor) -> NSView {
323
+        let view = NSView()
324
+        view.wantsLayer = true
325
+        view.layer?.backgroundColor = color.cgColor
326
+        view.layer?.cornerRadius = cornerRadius
327
+        return view
328
+    }
329
+
330
+    func styleSurface(_ view: NSView, borderColor: NSColor, borderWidth: CGFloat, shadow: Bool) {
331
+        view.layer?.borderColor = borderColor.cgColor
332
+        view.layer?.borderWidth = borderWidth
333
+        if shadow {
334
+            view.layer?.shadowColor = NSColor.black.cgColor
335
+            view.layer?.shadowOpacity = 0.28
336
+            view.layer?.shadowOffset = CGSize(width: 0, height: -2)
337
+            view.layer?.shadowRadius = 8
21 338
         }
22 339
     }
23 340
 
341
+    func textLabel(_ text: String, font: NSFont, color: NSColor) -> NSTextField {
342
+        let label = NSTextField(labelWithString: text)
343
+        label.translatesAutoresizingMaskIntoConstraints = false
344
+        label.textColor = color
345
+        label.font = font
346
+        return label
347
+    }
348
+
349
+    func iconLabel(_ text: String, size: CGFloat) -> NSTextField {
350
+        let label = NSTextField(labelWithString: text)
351
+        label.translatesAutoresizingMaskIntoConstraints = false
352
+        label.font = NSFont.systemFont(ofSize: size)
353
+        return label
354
+    }
355
+
356
+    func sidebarSectionTitle(_ text: String) -> NSTextField {
357
+        textLabel(text, font: typography.sidebarSection, color: palette.textMuted)
358
+    }
359
+
360
+    func sidebarItem(_ text: String, icon: String, selected: Bool) -> NSView {
361
+        let item = roundedContainer(cornerRadius: 9, color: selected ? palette.sidebarItemSelectedBackground : .clear)
362
+        item.translatesAutoresizingMaskIntoConstraints = false
363
+        item.heightAnchor.constraint(equalToConstant: 36).isActive = true
364
+        if selected {
365
+            styleSurface(item, borderColor: palette.sidebarItemSelectedBorder, borderWidth: 1, shadow: false)
366
+        }
367
+
368
+        let iconLabelView = textLabel(icon, font: typography.sidebarIcon, color: selected ? palette.textPrimary : palette.textSecondary)
369
+        let titleLabel = textLabel(text, font: typography.sidebarItem, color: selected ? palette.textPrimary : palette.textSecondary)
370
+
371
+        item.addSubview(iconLabelView)
372
+        item.addSubview(titleLabel)
373
+
374
+        NSLayoutConstraint.activate([
375
+            iconLabelView.leadingAnchor.constraint(equalTo: item.leadingAnchor, constant: 12),
376
+            iconLabelView.centerYAnchor.constraint(equalTo: item.centerYAnchor),
377
+            titleLabel.leadingAnchor.constraint(equalTo: iconLabelView.trailingAnchor, constant: 8),
378
+            titleLabel.centerYAnchor.constraint(equalTo: item.centerYAnchor)
379
+        ])
380
+
381
+        return item
382
+    }
383
+
384
+    func topTab(_ title: String, icon: String, selected: Bool) -> NSView {
385
+        let tab = roundedContainer(cornerRadius: 18, color: selected ? palette.primaryBlue : .clear)
386
+        tab.translatesAutoresizingMaskIntoConstraints = false
387
+
388
+        let iconLabelView = textLabel(icon, font: typography.tabIcon, color: palette.textPrimary)
389
+        let titleLabel = textLabel(title, font: typography.tabTitle, color: palette.textPrimary)
390
+
391
+        tab.addSubview(iconLabelView)
392
+        tab.addSubview(titleLabel)
393
+
394
+        NSLayoutConstraint.activate([
395
+            iconLabelView.leadingAnchor.constraint(equalTo: tab.leadingAnchor, constant: 16),
396
+            iconLabelView.centerYAnchor.constraint(equalTo: tab.centerYAnchor),
397
+            titleLabel.leadingAnchor.constraint(equalTo: iconLabelView.trailingAnchor, constant: 6),
398
+            titleLabel.centerYAnchor.constraint(equalTo: tab.centerYAnchor)
399
+        ])
400
+
401
+        return tab
402
+    }
403
+
404
+    func actionButton(title: String, color: NSColor, textColor: NSColor, width: CGFloat) -> NSView {
405
+        let button = roundedContainer(cornerRadius: 9, color: color)
406
+        button.translatesAutoresizingMaskIntoConstraints = false
407
+        button.widthAnchor.constraint(equalToConstant: width).isActive = true
408
+        button.heightAnchor.constraint(equalToConstant: 36).isActive = true
409
+        styleSurface(button, borderColor: title == "Cancel" ? palette.inputBorder : palette.primaryBlueBorder, borderWidth: 1, shadow: false)
410
+        if title == "Cancel" {
411
+            button.layer?.backgroundColor = palette.cancelButton.cgColor
412
+        }
413
+
414
+        let label = textLabel(title, font: typography.buttonText, color: textColor)
415
+        button.addSubview(label)
416
+        NSLayoutConstraint.activate([
417
+            label.centerXAnchor.constraint(equalTo: button.centerXAnchor),
418
+            label.centerYAnchor.constraint(equalTo: button.centerYAnchor)
419
+        ])
420
+
421
+        return button
422
+    }
423
+
424
+    func iconRoundButton(_ symbol: String, size: CGFloat) -> NSView {
425
+        let button = roundedContainer(cornerRadius: size / 2, color: palette.inputBackground)
426
+        button.translatesAutoresizingMaskIntoConstraints = false
427
+        button.widthAnchor.constraint(equalToConstant: size).isActive = true
428
+        button.heightAnchor.constraint(equalToConstant: size).isActive = true
429
+        styleSurface(button, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
430
+
431
+        let label = textLabel(symbol, font: typography.iconButton, color: palette.textSecondary)
432
+        button.addSubview(label)
433
+        NSLayoutConstraint.activate([
434
+            label.centerXAnchor.constraint(equalTo: button.centerXAnchor),
435
+            label.centerYAnchor.constraint(equalTo: button.centerYAnchor)
436
+        ])
437
+
438
+        return button
439
+    }
440
+}
441
+
442
+private struct Palette {
443
+    let pageBackground = NSColor(calibratedRed: 23.0 / 255.0, green: 24.0 / 255.0, blue: 24.0 / 255.0, alpha: 1)
444
+    let sidebarBackground = NSColor(calibratedRed: 32.0 / 255.0, green: 33.0 / 255.0, blue: 35.0 / 255.0, alpha: 1)
445
+    let sectionCard = NSColor(calibratedRed: 32.0 / 255.0, green: 33.0 / 255.0, blue: 35.0 / 255.0, alpha: 1)
446
+    let tabBarBackground = NSColor(calibratedRed: 32.0 / 255.0, green: 33.0 / 255.0, blue: 35.0 / 255.0, alpha: 1)
447
+    let tabIdleBackground = NSColor(calibratedRed: 32.0 / 255.0, green: 33.0 / 255.0, blue: 35.0 / 255.0, alpha: 1)
448
+    let inputBackground = NSColor(calibratedRed: 32.0 / 255.0, green: 33.0 / 255.0, blue: 35.0 / 255.0, alpha: 1)
449
+    let inputBorder = NSColor(calibratedRed: 0.14, green: 0.16, blue: 0.21, alpha: 1)
450
+    let primaryBlue = NSColor(calibratedRed: 27.0 / 255.0, green: 115.0 / 255.0, blue: 232.0 / 255.0, alpha: 1)
451
+    let primaryBlueBorder = NSColor(calibratedRed: 58.0 / 255.0, green: 139.0 / 255.0, blue: 246.0 / 255.0, alpha: 1)
452
+    let sidebarItemSelectedBackground = NSColor(calibratedRed: 27.0 / 255.0, green: 115.0 / 255.0, blue: 232.0 / 255.0, alpha: 0.28)
453
+    let sidebarItemSelectedBorder = NSColor(calibratedRed: 81.0 / 255.0, green: 152.0 / 255.0, blue: 249.0 / 255.0, alpha: 0.7)
454
+    let cancelButton = NSColor(calibratedRed: 32.0 / 255.0, green: 33.0 / 255.0, blue: 35.0 / 255.0, alpha: 1)
455
+    let meetingBadge = NSColor(calibratedRed: 0.94, green: 0.73, blue: 0.19, alpha: 1)
456
+    let separator = NSColor(calibratedWhite: 0.10, alpha: 1)
457
+    let textPrimary = NSColor(calibratedWhite: 0.96, alpha: 1)
458
+    let textSecondary = NSColor(calibratedWhite: 0.82, alpha: 1)
459
+    let textTertiary = NSColor(calibratedWhite: 0.74, alpha: 1)
460
+    let textMuted = NSColor(calibratedWhite: 0.50, alpha: 1)
461
+}
462
+
463
+private struct Typography {
464
+    let sidebarBrand = NSFont.systemFont(ofSize: 26, weight: .bold)
465
+    let sidebarSection = NSFont.systemFont(ofSize: 11, weight: .medium)
466
+    let sidebarIcon = NSFont.systemFont(ofSize: 12, weight: .medium)
467
+    let sidebarItem = NSFont.systemFont(ofSize: 16, weight: .medium)
468
+
469
+    let pageTitle = NSFont.systemFont(ofSize: 31, weight: .semibold)
470
+    let sectionTitle = NSFont.systemFont(ofSize: 23, weight: .semibold)
471
+    let sectionTitleBold = NSFont.systemFont(ofSize: 29, weight: .bold)
472
+    let dateHeading = NSFont.systemFont(ofSize: 21, weight: .medium)
473
+
474
+    let tabIcon = NSFont.systemFont(ofSize: 13, weight: .regular)
475
+    let tabTitle = NSFont.systemFont(ofSize: 31 / 2, weight: .semibold)
476
+
477
+    let fieldLabel = NSFont.systemFont(ofSize: 15, weight: .medium)
478
+    let inputPlaceholder = NSFont.systemFont(ofSize: 14, weight: .regular)
479
+    let buttonText = NSFont.systemFont(ofSize: 16, weight: .medium)
480
+    let filterText = NSFont.systemFont(ofSize: 15, weight: .regular)
481
+    let filterArrow = NSFont.systemFont(ofSize: 12, weight: .regular)
482
+    let iconButton = NSFont.systemFont(ofSize: 14, weight: .medium)
24 483
 
484
+    let cardIcon = NSFont.systemFont(ofSize: 10, weight: .bold)
485
+    let cardTitle = NSFont.systemFont(ofSize: 23, weight: .semibold)
486
+    let cardSubtitle = NSFont.systemFont(ofSize: 18, weight: .bold)
487
+    let cardTime = NSFont.systemFont(ofSize: 14, weight: .regular)
25 488
 }
26 489