Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
92982a4
feat(mac): revert to default accent color
sgschantz Aug 5, 2026
2806b0c
Merge remote-tracking branch 'origin/epic/mac-config' into feat/mac/c…
sgschantz Aug 6, 2026
9441c08
Resolved merge conflict
geschantz Aug 3, 2026
8e895e8
feat(mac): refactored alert modifier bound to the
geschantz Jul 20, 2026
c363a21
feat(mac): created the keyboard info view
geschantz Jul 21, 2026
9bb1c0b
feat(mac): added remove installed package support
geschantz Jul 21, 2026
cf80c88
feat(mac): removed unnecessary complexity
geschantz Jul 21, 2026
cbcbdab
feat(mac): layout changes to the package info view
geschantz Jul 23, 2026
3f0ede2
feat(mac): clicking row toggles disclosure group
geschantz Jul 23, 2026
f7b7e62
feat(mac): refactored the icon button view
geschantz Jul 24, 2026
c5ded45
feat(mac): layout changes to the package info view
geschantz Jul 24, 2026
20c9966
feat(mac): extracted label button view and binding
geschantz Jul 27, 2026
47aa88b
feat(mac): toggle button and code reformatting
geschantz Jul 27, 2026
0cceb76
feat(mac): multi keyboard package views
geschantz Jul 29, 2026
a2940f3
feat(mac): disclosure groups share state variable
geschantz Jul 30, 2026
f0fa17f
feat(mac): fixed package info animation
geschantz Jul 30, 2026
5c59da8
feat(mac): resolved changes from applying stash
geschantz Jul 30, 2026
2b2fc15
feat(mac): added tabview and webview
geschantz Aug 3, 2026
be3e3fc
feat(mac): created new installation views in Configuration app
ejschantz Jul 4, 2026
5d0e3a6
feat(mac): Added notification support to installer
ejschantz Jul 21, 2026
62f72c5
feat(mac): Added input method error screen and restart view
ejschantz Jul 23, 2026
fd5726f
feat(mac): First draft of integrated installer
ejschantz Jul 25, 2026
3d90feb
feat(mac): Added loading screen and reformated files
ejschantz Jul 27, 2026
7b531fd
feat(mac): Added an initial repair view
ejschantz Jul 28, 2026
cfea0a4
feat(mac): Fixed minor issues with installation flow
ejschantz Jul 29, 2026
47aa28b
feat(mac): Added Form view layout to a few installation pages
ejschantz Aug 6, 2026
3a19387
Update mac/Config/Config/PackageRowView.swift
geschantz Aug 11, 2026
f01050c
Update mac/Config/Config/PackageRowView.swift
geschantz Aug 11, 2026
c9915dd
feat(mac): responded to review comments
geschantz Aug 11, 2026
960bf45
Merge pull request #16299 from geschantz/feat/mac/config-window
sgschantz Aug 11, 2026
cfb1bed
feat(mac): Responding to code review
ejschantz Aug 12, 2026
5d70b5a
Merge branch 'feat/mac/config-ui' into feat/mac/install-window
sgschantz Aug 13, 2026
aba9ecc
Merge pull request #16252 from ejschantz/feat/mac/install-window
sgschantz Aug 13, 2026
477f5c0
feat(mac): make Config Window the main window
sgschantz Aug 13, 2026
25d6426
Merge remote-tracking branch 'origin/epic/mac-config' into feat/mac/c…
sgschantz Aug 13, 2026
6b59fd6
feat(mac): cache qrcode image and lazily load
sgschantz Aug 13, 2026
00b6712
feat(mac): clean up downloaded package when install fails
sgschantz Aug 13, 2026
895151d
feat(mac): refactor to allow package removal after install
sgschantz Aug 14, 2026
beba554
feat(mac): load help and readme if not listed in options
sgschantz Aug 14, 2026
04aa433
feat(mac): show confirm restart view correctly for state
sgschantz Aug 14, 2026
7ba1081
feat(mac): remove repeated loadURL call
sgschantz Aug 14, 2026
234ebe5
Merge branch 'epic/mac-config' into feat/mac/config-ui
sgschantz Aug 15, 2026
f262ae9
feat(mac): clarify comment
sgschantz Aug 15, 2026
4b6d52a
feat(mac): replaced scaled multiple images with pdfs
sgschantz Aug 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions mac/Config/Config.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,23 @@
D88F03DD2F50ED5100C02A31 /* ConfigUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ConfigUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */

/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
375D21122FF2F21800FCD24A /* Exceptions for "Config" folder in "Config" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
ConfigTests/ConfigTests.swift,
);
target = D88F03C52F50ED5000C02A31 /* Config */;
};
375D21132FF2F21800FCD24A /* Exceptions for "Config" folder in "ConfigTests" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
ConfigTests/ConfigTests.swift,
);
target = D88F03D22F50ED5100C02A31 /* ConfigTests */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */

/* Begin PBXFileSystemSynchronizedRootGroup section */
D87D6F492FAF95400083A95E /* Installation */ = {
isa = PBXFileSystemSynchronizedRootGroup;
Expand All @@ -41,14 +58,13 @@
};
D88F03C82F50ED5000C02A31 /* Config */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
375D21122FF2F21800FCD24A /* Exceptions for "Config" folder in "Config" target */,
375D21132FF2F21800FCD24A /* Exceptions for "Config" folder in "ConfigTests" target */,
);
path = Config;
sourceTree = "<group>";
};
D88F03D62F50ED5100C02A31 /* ConfigTests */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = ConfigTests;
sourceTree = "<group>";
};
D88F03E02F50ED5100C02A31 /* ConfigUITests */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = ConfigUITests;
Expand Down Expand Up @@ -87,7 +103,6 @@
children = (
D87D6F492FAF95400083A95E /* Installation */,
D88F03C82F50ED5000C02A31 /* Config */,
D88F03D62F50ED5100C02A31 /* ConfigTests */,
D88F03E02F50ED5100C02A31 /* ConfigUITests */,
D88F04022F512FE800C02A31 /* Frameworks */,
D88F03C72F50ED5000C02A31 /* Products */,
Expand Down Expand Up @@ -151,9 +166,6 @@
dependencies = (
D88F03D52F50ED5100C02A31 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
D88F03D62F50ED5100C02A31 /* ConfigTests */,
);
name = ConfigTests;
packageProductDependencies = (
);
Expand Down Expand Up @@ -416,7 +428,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "";
CODE_SIGN_ENTITLEMENTS = Config/ConfigDebug.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer";
CODE_SIGN_STYLE = Manual;
Expand Down Expand Up @@ -455,7 +467,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "";
CODE_SIGN_ENTITLEMENTS = Config/ConfigRelease.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
CODE_SIGN_STYLE = Manual;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
"size" : "128x128"
},
{
"filename" : "Keyman-256.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
Expand All @@ -47,6 +46,7 @@
"size" : "512x512"
},
{
"filename" : "keyman-1024.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
Expand Down
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"images" : [
{
"filename" : "accessibility-permission-light.pdf",
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "accessibility-permission-dark.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"images" : [
{
"filename" : "enable-keyman-light.pdf",
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "enable-keyman-dark.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"images" : [
{
"filename" : "keyman-logo-light.png",
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "keyman-logo-dark.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions mac/Config/Config/ConfigApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,23 @@ struct ConfigApp: App {
@Environment(\.openWindow) private var openWindow

var body: some Scene {
Window("Configuration", id: "main-config") {
MainConfigView()
.environmentObject(settings)
.task {
if !installation.getHasDisplayedInstallationComplete() {
openWindow(id: "install")
}
}
.onReceive(NotificationCenter.default.publisher(for: .installationRepairStarted)) { notification in openWindow(id: "install")
}
}
Window("Installation", id: "install") {
MainInstallView()
.environmentObject(installation)
}
.windowResizability(.contentSize)
.defaultSize(width: 600, height: 500)
Window("Config Test", id: "config-debug") {
ConfigDebugView()
.environmentObject(settings)
Expand Down
19 changes: 0 additions & 19 deletions mac/Config/Config/ConfigDebugView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ struct ConfigDebugView: View {
.foregroundColor(.accentColor)
Text("multiple keyboard package count = \(settings.multiKeyboardPackages.count)")
Text("single keyboard package count = \(settings.singleKeyboardPackages.count)")
Button("debug") {
settings.debug()
}
Button("log defaults") {
settings.logUserDefaults()
}
Expand All @@ -50,22 +47,6 @@ struct ConfigDebugView: View {
ForEach(Array(settings.singleKeyboardPackages.enumerated()), id: \.offset) { index, package in
VStack {
HStack(alignment: .center, spacing: 10) {
VStack(spacing: 16) {
Text("Scan to visit website:")
.font(.headline)

if let qrImage = package.generateSharePackageQRCode(size: 200) {
Image(nsImage: qrImage)
.interpolation(.none) // important: keeps the QR edges sharp
.resizable()
.frame(width: 200, height: 200)
.background(Color.white) // ensures good scanning contrast
} else {
Text("Failed to generate QR Code")
.foregroundColor(.red)
}
}
.padding()
Text(package.packageName)
.font(.headline)
Text(package.packageVersion)
Expand Down
34 changes: 34 additions & 0 deletions mac/Config/Config/HelpView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Created by Gabriel Schantz on 2026-08-03
*
* Webview used to show help for Keyman keyboards
*/
import Foundation

import SwiftUI
import WebKit
import KeymanSettings

public struct HelpView: NSViewRepresentable {
let helpFileURL: URL

// create the AppKit view instance
public func makeNSView(context: Context) -> WKWebView {
let webView = WKWebView()
return webView
}

// update the view when SwiftUI state changes
public func updateNSView(_ nsView: WKWebView, context: Context) {
let request = URLRequest(url: helpFileURL)

// only load the request if it's not already loading/loaded to prevent infinite loops
if nsView.url != helpFileURL {
if let helpUrl = request.url {
nsView.loadFileURL(helpUrl, allowingReadAccessTo: helpUrl.deletingLastPathComponent())
}
}
}
}
46 changes: 46 additions & 0 deletions mac/Config/Config/IconButtonView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Created by Gabriel Schantz on 2026-07-03
*
* The view used for image-only buttons
*/

import SwiftUI

public struct IconButtonView: View {
let action: () -> Void
let systemImage: String
let font: Font
let helpText: String

public var body: some View {

Button {
action()
} label: {
Image(systemName: systemImage)
.font(font)
}
.buttonStyle(.plain)
.accessibilityLabel(helpText)
.help(helpText)

}
}

// the view for buttons with a label
public struct LabelButtonView: View {
let action: () -> Void
let label: String
let systemImage: String
let font: Font

public var body: some View {
Button(action: action) {
Label(label, systemImage: systemImage)
.font(font)
}
.buttonStyle(.bordered)
}
}
44 changes: 44 additions & 0 deletions mac/Config/Config/InstallationViews/CompletedInstallView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Created by Eli Schantz on 2026-07-01
*
* View used for notifying the user that installation is complete.
*/

import SwiftUI

struct CompletedInstallView: View {
@EnvironmentObject var installation: InstallationContainer
let namespace: Namespace.ID

var versionText: String {
if let version = installation.installationState?.keymanVersion {
return "Version: \(version)"
Comment thread
sgschantz marked this conversation as resolved.
} else {
return "No version to display"
Comment thread
sgschantz marked this conversation as resolved.
}
}

var body: some View {
VStack {

Spacer()

KeymanLogo(namespace: namespace)
Text(versionText)
.foregroundStyle(.secondary)

Spacer()

GradientDivider(namespace: namespace)
.padding(.bottom, 8)
HStack {
Text("Installation complete")
.font(.title2)
.frame(maxWidth: .infinity, alignment: .leading)
NavigationButton(action: .dismiss)
}
}
}
}
Loading