Skip to content

Commit 4c7492c

Browse files
committed
fix(macos): simplify onboarding learning layout
Signed-off-by: xunzhuo <xunzhuo@vllm-semantic-router.ai>
1 parent deab9bf commit 4c7492c

2 files changed

Lines changed: 87 additions & 77 deletions

File tree

apps/macos/Sources/AppLocalization.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ enum AppText {
591591
case .providerNeedsDetails:
592592
return pick(language, en: "Provider and model ID are required; OpenAI Compatible also needs Base URL unless reusing an existing setup.", zh: "请选择模型服务并填写模型 ID;OpenAI Compatible 通常还需要 Base URL,除非复用已有配置。", fr: "Provider et ID de modèle sont requis ; OpenAI Compatible nécessite aussi une Base URL sauf si vous réutilisez une configuration.", de: "Provider und Modell-ID sind erforderlich; OpenAI Compatible braucht zusätzlich eine Base URL, außer du nutzt eine bestehende Konfiguration.")
593593
case .learningTitle:
594-
return pick(language, en: "Building your Personal Model deeply", zh: "深度构建你的个人模型中", fr: "Construction approfondie de votre Personal Model", de: "Dein Personal Model wird vertieft aufgebaut")
594+
return pick(language, en: "Building your Personal Model", zh: "正在深度构建你的个人模型", fr: "Construction de votre Personal Model", de: "Dein Personal Model wird aufgebaut")
595595
case .learningPreparing:
596596
return pick(language, en: "Preparing your first learning pass", zh: "准备第一次学习", fr: "Préparation du premier apprentissage", de: "Ersten Lernlauf vorbereiten")
597597
case .learningCreateModel:

apps/macos/Sources/Views.swift

Lines changed: 86 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -16796,7 +16796,7 @@ struct OnboardingFlow: View {
1679616796

1679716797
private var panelHeight: CGFloat {
1679816798
if isHerdDiscoveryStep { return 668 }
16799-
if model.onboardingStep == learnStep { return 574 }
16799+
if model.onboardingStep == learnStep { return 604 }
1680016800
if isDropdownProfileStep { return 592 }
1680116801
return usesExpandedPanel ? 568 : 486
1680216802
}
@@ -21408,8 +21408,7 @@ struct OnboardingLearningStep: View {
2140821408
job: model.onboardingLearningJob,
2140921409
statusText: model.onboardingFinalizationStatus.isEmpty ? model.text(.learningPreparing) : model.onboardingFinalizationStatus
2141021410
)
21411-
.frame(maxWidth: 640)
21412-
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
21411+
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
2141321412
.task {
2141421413
await model.startOnboardingFinalization()
2141521414
}
@@ -21425,12 +21424,12 @@ struct OnboardingLearningLivePanel: View {
2142521424
var body: some View {
2142621425
VStack(spacing: 12) {
2142721426
OnboardingLearningAnimation()
21428-
.frame(width: 62, height: 62)
21427+
.frame(width: 260, height: 156)
2142921428
.accessibilityHidden(true)
2143021429

21431-
VStack(spacing: 8) {
21430+
VStack(spacing: 6) {
2143221431
Text(model.text(.learningTitle))
21433-
.font(.system(size: 23, weight: .semibold))
21432+
.font(.system(size: 22, weight: .semibold))
2143421433
.foregroundStyle(ElephantTheme.ink)
2143521434
.multilineTextAlignment(.center)
2143621435
Text(panelSubtitle)
@@ -21439,7 +21438,7 @@ struct OnboardingLearningLivePanel: View {
2143921438
.multilineTextAlignment(.center)
2144021439
.lineLimit(2)
2144121440
.fixedSize(horizontal: false, vertical: true)
21442-
.frame(maxWidth: 500)
21441+
.frame(maxWidth: 520)
2144321442
}
2144421443

2144521444
HStack(alignment: .center, spacing: 14) {
@@ -21451,15 +21450,15 @@ struct OnboardingLearningLivePanel: View {
2145121450
.controlSize(.small)
2145221451
} else {
2145321452
Image(systemName: statusSymbol)
21454-
.font(.callout.weight(.bold))
21453+
.font(.caption.weight(.bold))
2145521454
.foregroundStyle(statusTint)
2145621455
}
2145721456
}
21458-
.frame(width: 42, height: 42)
21457+
.frame(width: 36, height: 36)
2145921458

2146021459
VStack(alignment: .leading, spacing: 4) {
2146121460
Text(stageTitle)
21462-
.font(.callout.weight(.semibold))
21461+
.font(.caption.weight(.semibold))
2146321462
.foregroundStyle(ElephantTheme.ink)
2146421463
.lineLimit(1)
2146521464
Text(stageDetail)
@@ -21471,40 +21470,21 @@ struct OnboardingLearningLivePanel: View {
2147121470
Spacer(minLength: 0)
2147221471
Pill(text: statusLabel, tint: statusTint)
2147321472
}
21474-
.padding(12)
21475-
.frame(maxWidth: 540, minHeight: 76, alignment: .leading)
21476-
.background(Color(nsColor: .textBackgroundColor).opacity(0.64), in: RoundedRectangle(cornerRadius: 10, style: .continuous))
21477-
.overlay(RoundedRectangle(cornerRadius: 10, style: .continuous).stroke(statusTint.opacity(0.20), lineWidth: 1))
21473+
.frame(maxWidth: 590, minHeight: 52, alignment: .leading)
21474+
21475+
Divider()
21476+
.frame(maxWidth: 590)
21477+
.opacity(0.55)
2147821478

2147921479
OnboardingLearningToolTimeline(job: job, statusText: statusText)
21480-
.frame(maxWidth: 540)
21481-
}
21482-
.padding(.horizontal, 26)
21483-
.padding(.vertical, 20)
21484-
.frame(maxWidth: .infinity, minHeight: 442, alignment: .center)
21485-
.background(learningPanelBackground)
21486-
.overlay(RoundedRectangle(cornerRadius: 18, style: .continuous).stroke(Color.white.opacity(0.50), lineWidth: 1))
21487-
.shadow(color: ElephantTheme.accent.opacity(0.10), radius: 30, x: 0, y: 18)
21480+
.frame(maxWidth: 590)
21481+
}
21482+
.padding(.horizontal, 18)
21483+
.padding(.vertical, 2)
21484+
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
2148821485
.animation(reduceMotion ? nil : .easeOut(duration: 0.18), value: activityKey)
2148921486
}
2149021487

21491-
private var learningPanelBackground: some View {
21492-
RoundedRectangle(cornerRadius: 18, style: .continuous)
21493-
.fill(.regularMaterial)
21494-
.overlay(
21495-
LinearGradient(
21496-
colors: [
21497-
ElephantTheme.accent.opacity(0.080),
21498-
ElephantTheme.green.opacity(0.045),
21499-
ElephantTheme.ember.opacity(0.035)
21500-
],
21501-
startPoint: .topLeading,
21502-
endPoint: .bottomTrailing
21503-
)
21504-
.clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous))
21505-
)
21506-
}
21507-
2150821488
private var activityKey: String {
2150921489
"\(job?.status ?? "")|\(job?.progressStage ?? "")|\(job?.progressDetail ?? "")|\(job?.modelProgress.text ?? "")|\(latestToolItem?.id ?? "")"
2151021490
}
@@ -21706,7 +21686,7 @@ struct OnboardingLearningToolTimeline: View {
2170621686
var statusText: String
2170721687

2170821688
var body: some View {
21709-
VStack(alignment: .leading, spacing: 8) {
21689+
VStack(alignment: .leading, spacing: 9) {
2171021690
HStack(alignment: .center, spacing: 9) {
2171121691
Image(systemName: "waveform.path.ecg")
2171221692
.font(.caption.weight(.bold))
@@ -21719,14 +21699,12 @@ struct OnboardingLearningToolTimeline: View {
2171921699
.truncationMode(.middle)
2172021700
Spacer(minLength: 0)
2172121701
}
21722-
.padding(.horizontal, 10)
21723-
.padding(.vertical, 7)
21724-
.background(statusTint.opacity(0.08), in: RoundedRectangle(cornerRadius: 10, style: .continuous))
21725-
.overlay(RoundedRectangle(cornerRadius: 10, style: .continuous).stroke(statusTint.opacity(0.18), lineWidth: 1))
21702+
.padding(.horizontal, 2)
21703+
.padding(.vertical, 2)
2172621704

2172721705
ScrollViewReader { proxy in
2172821706
ScrollView(.vertical) {
21729-
VStack(spacing: 8) {
21707+
VStack(spacing: 9) {
2173021708
if !modelProgress.isEmpty {
2173121709
OnboardingLearningModelLiveSlot(progress: modelProgress)
2173221710
.id("model")
@@ -21761,10 +21739,7 @@ struct OnboardingLearningToolTimeline: View {
2176121739
.frame(maxWidth: .infinity, alignment: .top)
2176221740
.clipped()
2176321741
}
21764-
.padding(11)
2176521742
.frame(maxWidth: .infinity, minHeight: timelineHeight, maxHeight: timelineHeight, alignment: .top)
21766-
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous))
21767-
.overlay(RoundedRectangle(cornerRadius: 12, style: .continuous).stroke(ElephantTheme.line.opacity(0.68), lineWidth: 1))
2176821743
.clipped()
2176921744
.animation(toolSlotAnimation, value: "\(latestToolItem?.id ?? "waiting")-\(modelProgress.text)")
2177021745
}
@@ -21780,9 +21755,9 @@ struct OnboardingLearningToolTimeline: View {
2178021755
return "\(title) · \(detail)"
2178121756
}
2178221757

21783-
private var liveContentHeight: CGFloat { 118 }
21758+
private var liveContentHeight: CGFloat { 120 }
2178421759

21785-
private var timelineHeight: CGFloat { 174 }
21760+
private var timelineHeight: CGFloat { 156 }
2178621761

2178721762
private var activityScrollKey: String {
2178821763
"\(modelProgress.text)|\(latestToolItem?.id ?? "")|\(job?.progressStage ?? "")|\(job?.progressDetail ?? "")"
@@ -22056,10 +22031,8 @@ struct OnboardingLearningToolTimelineEmpty: View {
2205622031
Spacer(minLength: 0)
2205722032
}
2205822033
.padding(.horizontal, 10)
22059-
.padding(.vertical, 9)
22060-
.frame(maxWidth: .infinity, minHeight: 76, maxHeight: 76, alignment: .leading)
22061-
.background(ElephantTheme.canvas.opacity(0.48), in: RoundedRectangle(cornerRadius: 10, style: .continuous))
22062-
.overlay(RoundedRectangle(cornerRadius: 10, style: .continuous).stroke(ElephantTheme.line.opacity(0.42), lineWidth: 1))
22034+
.padding(.vertical, 8)
22035+
.frame(maxWidth: .infinity, minHeight: 58, alignment: .leading)
2206322036
}
2206422037
}
2206522038

@@ -22101,10 +22074,8 @@ struct OnboardingLearningModelLiveSlot: View {
2210122074
.animation(reduceMotion ? nil : .easeOut(duration: 0.16), value: progress.text)
2210222075
}
2210322076
.padding(.horizontal, 12)
22104-
.padding(.vertical, 10)
22105-
.frame(maxWidth: .infinity, minHeight: 78, alignment: .topLeading)
22106-
.background(ElephantTheme.canvas.opacity(0.52), in: RoundedRectangle(cornerRadius: 10, style: .continuous))
22107-
.overlay(RoundedRectangle(cornerRadius: 10, style: .continuous).stroke(ElephantTheme.accent.opacity(0.18), lineWidth: 1))
22077+
.padding(.vertical, 6)
22078+
.frame(maxWidth: .infinity, minHeight: 70, alignment: .topLeading)
2210822079
.onAppear {
2210922080
guard !reduceMotion else { return }
2211022081
pulse = true
@@ -22158,9 +22129,7 @@ struct OnboardingLearningToolLiveSlot: View {
2215822129
.background(item.tint.opacity(0.10), in: Capsule())
2215922130
}
2216022131
.padding(.horizontal, 12)
22161-
.frame(maxWidth: .infinity, minHeight: 76, maxHeight: 76, alignment: .center)
22162-
.background(item.tint.opacity(0.07), in: RoundedRectangle(cornerRadius: 10, style: .continuous))
22163-
.overlay(RoundedRectangle(cornerRadius: 10, style: .continuous).stroke(item.tint.opacity(0.16), lineWidth: 1))
22132+
.frame(maxWidth: .infinity, minHeight: 62, alignment: .center)
2216422133
}
2216522134

2216622135
private var slotEyebrow: String {
@@ -22196,32 +22165,73 @@ struct OnboardingLearningAnimation: View {
2219622165
Canvas { context, size in
2219722166
let seconds = reduceMotion ? 0 : timeline.date.timeIntervalSinceReferenceDate
2219822167
let center = CGPoint(x: size.width / 2, y: size.height / 2)
22199-
let radius = min(size.width, size.height) * 0.34
22168+
let horizontalRadius = size.width * 0.38
22169+
let verticalRadius = size.height * 0.36
2220022170
let palette = [ElephantTheme.accent, ElephantTheme.green, ElephantTheme.ember]
2220122171

22202-
for index in 0..<3 {
22203-
let inset = CGFloat(index) * 18
22204-
let rect = CGRect(x: center.x - radius + inset / 2, y: center.y - radius + inset / 2, width: (radius * 2) - inset, height: (radius * 2) - inset)
22172+
let glowRect = CGRect(
22173+
x: center.x - horizontalRadius * 1.18,
22174+
y: center.y - verticalRadius * 1.18,
22175+
width: horizontalRadius * 2.36,
22176+
height: verticalRadius * 2.36
22177+
)
22178+
context.fill(
22179+
Path(ellipseIn: glowRect),
22180+
with: .color(ElephantTheme.accent.opacity(0.055))
22181+
)
22182+
22183+
var nodes: [CGPoint] = []
22184+
for index in 0..<9 {
22185+
let lane = CGFloat(index % 3)
22186+
let nodeRadius = 0.62 + lane * 0.18
22187+
let angle = seconds * (0.26 + Double(index % 4) * 0.035) + Double(index) * .pi * 2 / 9
22188+
let point = CGPoint(
22189+
x: center.x + CGFloat(cos(angle)) * horizontalRadius * nodeRadius,
22190+
y: center.y + CGFloat(sin(angle)) * verticalRadius * nodeRadius
22191+
)
22192+
nodes.append(point)
22193+
}
22194+
22195+
for index in nodes.indices {
22196+
let next = nodes[(index + 3) % nodes.count]
22197+
var path = Path()
22198+
path.move(to: nodes[index])
22199+
path.addLine(to: next)
2220522200
context.stroke(
22206-
Path(ellipseIn: rect),
22207-
with: .color(palette[index].opacity(0.26)),
22208-
style: StrokeStyle(lineWidth: 2, lineCap: .round, dash: [22, 16], dashPhase: CGFloat(seconds * 18 + Double(index) * 12))
22201+
path,
22202+
with: .color(ElephantTheme.accent.opacity(0.08)),
22203+
style: StrokeStyle(lineWidth: 1.2, lineCap: .round)
2220922204
)
2221022205
}
2221122206

22212-
for index in 0..<6 {
22213-
let angle = seconds * 0.42 + Double(index) * .pi / 3
22214-
let point = CGPoint(
22215-
x: center.x + CGFloat(cos(angle)) * (radius + 8),
22216-
y: center.y + CGFloat(sin(angle)) * (radius + 8)
22207+
for index in 0..<4 {
22208+
let inset = CGFloat(index) * 20
22209+
let rect = CGRect(
22210+
x: center.x - horizontalRadius + inset / 2,
22211+
y: center.y - verticalRadius + inset / 3,
22212+
width: (horizontalRadius * 2) - inset,
22213+
height: (verticalRadius * 2) - inset * 0.66
22214+
)
22215+
context.stroke(
22216+
Path(ellipseIn: rect),
22217+
with: .color(palette[index % palette.count].opacity(0.18)),
22218+
style: StrokeStyle(lineWidth: 1.8, lineCap: .round, dash: [24, 18], dashPhase: CGFloat(seconds * 16 + Double(index) * 12))
2221722219
)
22218-
let rect = CGRect(x: point.x - 4, y: point.y - 4, width: 8, height: 8)
22220+
}
22221+
22222+
for index in nodes.indices {
22223+
let pulse = 0.74 + 0.18 * sin(seconds * 1.25 + Double(index))
22224+
let size = CGFloat(6.5 + Double(index % 3) * 1.2) * CGFloat(pulse)
22225+
let rect = CGRect(x: nodes[index].x - size / 2, y: nodes[index].y - size / 2, width: size, height: size)
2221922226
context.fill(Path(ellipseIn: rect), with: .color(palette[index % palette.count].opacity(0.78)))
2222022227
}
2222122228

22222-
let core = CGRect(x: center.x - 8, y: center.y - 8, width: 16, height: 16)
22229+
let corePulse = CGFloat(1.0 + 0.06 * sin(seconds * 1.5))
22230+
let coreSize = CGFloat(24) * corePulse
22231+
let core = CGRect(x: center.x - coreSize / 2, y: center.y - coreSize / 2, width: coreSize, height: coreSize)
22232+
context.fill(Path(ellipseIn: core.insetBy(dx: -12, dy: -12)), with: .color(ElephantTheme.accent.opacity(0.08)))
2222322233
context.fill(Path(ellipseIn: core), with: .color(ElephantTheme.accent.opacity(0.22)))
22224-
context.fill(Path(ellipseIn: core.insetBy(dx: 4, dy: 4)), with: .color(ElephantTheme.accent.opacity(0.62)))
22234+
context.fill(Path(ellipseIn: core.insetBy(dx: 7, dy: 7)), with: .color(ElephantTheme.accent.opacity(0.62)))
2222522235
}
2222622236
}
2222722237
}

0 commit comments

Comments
 (0)