diff --git a/.reuse/dep5 b/.reuse/dep5 index 28052de..5ef34a0 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -13,3 +13,15 @@ License: Apache-2.0 Files: docs/architecture/simble-architecture.qd Copyright: 2026 Nirapod Labs License: Apache-2.0 + +Files: assets/*.png assets/github/*.png assets/squircles/*.png assets/menubar/*.png +Copyright: 2026 Nirapod Labs +License: Apache-2.0 + +Files: assets/simble.icon/icon.json assets/simble-dev.icon/icon.json assets/simble.icon/Assets/*.png assets/simble-dev.icon/Assets/*.png +Copyright: 2026 Nirapod Labs +License: Apache-2.0 + +Files: apps/helper/Resources/fonts/DMSans-Medium.ttf apps/helper/Resources/fonts/DMSans-Bold.ttf apps/helper/Resources/fonts/OFL.txt +Copyright: 2014 The DM Sans Project Authors +License: OFL-1.1 diff --git a/README.md b/README.md index 6470c7b..3560bde 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,10 @@ SPDX-FileCopyrightText: 2026 Nirapod Labs -->

- SimBLE + + + SimBLE +

diff --git a/apps/helper/Resources/fonts/DMSans-Bold.ttf b/apps/helper/Resources/fonts/DMSans-Bold.ttf new file mode 100644 index 0000000..db8d71a Binary files /dev/null and b/apps/helper/Resources/fonts/DMSans-Bold.ttf differ diff --git a/apps/helper/Resources/fonts/DMSans-Medium.ttf b/apps/helper/Resources/fonts/DMSans-Medium.ttf new file mode 100644 index 0000000..39ddfe4 Binary files /dev/null and b/apps/helper/Resources/fonts/DMSans-Medium.ttf differ diff --git a/apps/helper/Resources/fonts/OFL.txt b/apps/helper/Resources/fonts/OFL.txt new file mode 100644 index 0000000..441171d --- /dev/null +++ b/apps/helper/Resources/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2014 The DM Sans Project Authors (https://github.com/googlefonts/dm-fonts) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/apps/helper/Sources/simble-menubar/App.swift b/apps/helper/Sources/simble-menubar/App.swift index 9a5d011..9eb529f 100644 --- a/apps/helper/Sources/simble-menubar/App.swift +++ b/apps/helper/Sources/simble-menubar/App.swift @@ -33,7 +33,7 @@ struct SimBLEMenubarApp: App { MenuBarExtra { MenubarView(model: model) } label: { - Image(systemName: model.iconName) + menuBarLabel } .menuBarExtraStyle(.window) @@ -41,4 +41,26 @@ struct SimBLEMenubarApp: App { SettingsView(model: model) } } + + /// The status-bar glyph: the SimBLE mark, a template image the menu bar tints to the bar, dimmed + /// while the bridge is stopped so the on/off state reads at a glance. Bluetooth that is not + /// powered on keeps the warning triangle, and an unbundled `swift run` with no copied resource + /// falls back to the symbol. + @ViewBuilder + private var menuBarLabel: some View { + if model.bluetooth != .poweredOn { + Image(systemName: "exclamationmark.triangle") + } else if let icon = Self.markIcon { + Image(nsImage: icon).opacity(model.running ? 1 : 0.45) + } else { + Image(systemName: model.iconName) + } + } + + /// The menu-bar mark, loaded once from the bundle and marked as a template so AppKit tints it. + private static let markIcon: NSImage? = { + guard let image = NSImage(named: "MenuBarIcon") else { return nil } + image.isTemplate = true + return image + }() } diff --git a/apps/helper/Sources/simble-menubar/SimBLEWordmark.swift b/apps/helper/Sources/simble-menubar/SimBLEWordmark.swift index 35b7088..4a45865 100644 --- a/apps/helper/Sources/simble-menubar/SimBLEWordmark.swift +++ b/apps/helper/Sources/simble-menubar/SimBLEWordmark.swift @@ -3,15 +3,42 @@ import SwiftUI -/// The SimBLE wordmark as a system-font lockup: "Sim" in a muted medium weight and "BLE" in bold. -/// A wordmark asset under /assets can replace it. +/// The SimBLE wordmark, drawn as live text in the brand face. +/// +/// "Sim" is set in DM Sans Medium in the muted grey, "BLE" in DM Sans Bold in the ink colour, on +/// one baseline with the brand's negative tracking. Both colours track the light and dark +/// appearance. +/// +/// DM Sans ships in the packaged app's `Resources` and is registered at launch through +/// `ATSApplicationFontsPath` (see `scripts/build-menubar-app.sh`). Outside that bundle, for example +/// under `swift run`, the face is not registered and `Font.custom` falls back to the system font. @available(macOS 14, *) struct SimBLEWordmark: View { + /// Point size of the wordmark. var size: CGFloat = 17 + @Environment(\.colorScheme) private var scheme + + /// The "Sim" grey: `#86868B` in light, `#8E95A3` in dark. + private var simColor: Color { + scheme == .dark + ? Color(red: 0.557, green: 0.584, blue: 0.639) + : Color(red: 0.525, green: 0.525, blue: 0.545) + } + + /// The "BLE" ink: `#1D1D1F` in light, `#F5F5F7` in dark. + private var inkColor: Color { + scheme == .dark + ? Color(red: 0.961, green: 0.961, blue: 0.969) + : Color(red: 0.114, green: 0.114, blue: 0.122) + } + var body: some View { - (Text("Sim").font(.system(size: size, weight: .medium)).foregroundStyle(.secondary) - + Text("BLE").font(.system(size: size, weight: .bold)).foregroundStyle(.primary)) + (Text("Sim").font(.custom("DMSans-Medium", size: size)).foregroundColor(simColor) + + Text("BLE").font(.custom("DMSans-Bold", size: size)).foregroundColor(inkColor)) + // The brand tracking is -0.035em, applied in points to both parts. + .tracking(-0.035 * size) + .fixedSize() .accessibilityLabel("SimBLE") } } diff --git a/assets/README.md b/assets/README.md deleted file mode 100644 index e6cc906..0000000 --- a/assets/README.md +++ /dev/null @@ -1,8 +0,0 @@ - - -# Assets - -Placeholder. App icons, menu bar assets, and release artwork are not added yet. diff --git a/assets/app-icon-1024.png b/assets/app-icon-1024.png new file mode 100644 index 0000000..8722118 Binary files /dev/null and b/assets/app-icon-1024.png differ diff --git a/assets/app-icon-dev-1024.png b/assets/app-icon-dev-1024.png new file mode 100644 index 0000000..c6903cb Binary files /dev/null and b/assets/app-icon-dev-1024.png differ diff --git a/assets/github/github-banner-dark.png b/assets/github/github-banner-dark.png new file mode 100644 index 0000000..9238b2c Binary files /dev/null and b/assets/github/github-banner-dark.png differ diff --git a/assets/github/github-banner-light.png b/assets/github/github-banner-light.png new file mode 100644 index 0000000..bc49066 Binary files /dev/null and b/assets/github/github-banner-light.png differ diff --git a/assets/github/social-preview.png b/assets/github/social-preview.png new file mode 100644 index 0000000..080159e Binary files /dev/null and b/assets/github/social-preview.png differ diff --git a/assets/mark-dark.png b/assets/mark-dark.png new file mode 100644 index 0000000..992956a Binary files /dev/null and b/assets/mark-dark.png differ diff --git a/assets/mark-light.png b/assets/mark-light.png new file mode 100644 index 0000000..a3d3519 Binary files /dev/null and b/assets/mark-light.png differ diff --git a/assets/menubar/menubar-iconTemplate-18.png b/assets/menubar/menubar-iconTemplate-18.png new file mode 100644 index 0000000..22d72cd Binary files /dev/null and b/assets/menubar/menubar-iconTemplate-18.png differ diff --git a/assets/menubar/menubar-iconTemplate-36-2x.png b/assets/menubar/menubar-iconTemplate-36-2x.png new file mode 100644 index 0000000..e3a00e4 Binary files /dev/null and b/assets/menubar/menubar-iconTemplate-36-2x.png differ diff --git a/assets/menubar/menubar-white-16.png b/assets/menubar/menubar-white-16.png new file mode 100644 index 0000000..16fe59c Binary files /dev/null and b/assets/menubar/menubar-white-16.png differ diff --git a/assets/menubar/menubar-white-18.png b/assets/menubar/menubar-white-18.png new file mode 100644 index 0000000..0d53a2f Binary files /dev/null and b/assets/menubar/menubar-white-18.png differ diff --git a/assets/menubar/menubar-white-32-2x.png b/assets/menubar/menubar-white-32-2x.png new file mode 100644 index 0000000..37b2db4 Binary files /dev/null and b/assets/menubar/menubar-white-32-2x.png differ diff --git a/assets/menubar/menubar-white-36-2x.png b/assets/menubar/menubar-white-36-2x.png new file mode 100644 index 0000000..0d2483e Binary files /dev/null and b/assets/menubar/menubar-white-36-2x.png differ diff --git a/assets/menubar/preview-in-menubar.png b/assets/menubar/preview-in-menubar.png new file mode 100644 index 0000000..0809b95 Binary files /dev/null and b/assets/menubar/preview-in-menubar.png differ diff --git a/assets/simble-dev.icon/Assets/app-icon-dev-1024.png b/assets/simble-dev.icon/Assets/app-icon-dev-1024.png new file mode 100644 index 0000000..c6903cb Binary files /dev/null and b/assets/simble-dev.icon/Assets/app-icon-dev-1024.png differ diff --git a/assets/simble-dev.icon/icon.json b/assets/simble-dev.icon/icon.json new file mode 100644 index 0000000..01547ca --- /dev/null +++ b/assets/simble-dev.icon/icon.json @@ -0,0 +1,25 @@ +{ + "fill": "automatic", + "groups": [ + { + "layers": [ + { + "image-name": "app-icon-dev-1024.png", + "name": "app-icon-dev-1024" + } + ], + "shadow": { + "kind": "neutral", + "opacity": 0.5 + }, + "translucency": { + "enabled": true, + "value": 0.5 + } + } + ], + "supported-platforms": { + "circles": ["watchOS"], + "squares": "shared" + } +} diff --git a/assets/simble.icon/Assets/app-icon-1024.png b/assets/simble.icon/Assets/app-icon-1024.png new file mode 100644 index 0000000..8722118 Binary files /dev/null and b/assets/simble.icon/Assets/app-icon-1024.png differ diff --git a/assets/simble.icon/icon.json b/assets/simble.icon/icon.json new file mode 100644 index 0000000..f892f36 --- /dev/null +++ b/assets/simble.icon/icon.json @@ -0,0 +1,35 @@ +{ + "fill": "automatic", + "groups": [ + { + "layers": [ + { + "blend-mode": "normal", + "fill": "none", + "glass": true, + "hidden": false, + "image-name": "app-icon-1024.png", + "name": "app-icon-1024", + "opacity": 1, + "position": { + "scale": 1, + "translation-in-points": [0, 0] + } + } + ], + "name": "Regular", + "shadow": { + "kind": "neutral", + "opacity": 0.5 + }, + "translucency": { + "enabled": true, + "value": 0.5 + } + } + ], + "supported-platforms": { + "circles": ["watchOS"], + "squares": "shared" + } +} diff --git a/assets/squircles/app-icon-dev-squircle.png b/assets/squircles/app-icon-dev-squircle.png new file mode 100644 index 0000000..f23e6c8 Binary files /dev/null and b/assets/squircles/app-icon-dev-squircle.png differ diff --git a/assets/squircles/app-icon-squircle.png b/assets/squircles/app-icon-squircle.png new file mode 100644 index 0000000..64ed16a Binary files /dev/null and b/assets/squircles/app-icon-squircle.png differ diff --git a/assets/wordmark-dark.png b/assets/wordmark-dark.png new file mode 100644 index 0000000..0c7c343 Binary files /dev/null and b/assets/wordmark-dark.png differ diff --git a/assets/wordmark-light.png b/assets/wordmark-light.png new file mode 100644 index 0000000..fc5cdc4 Binary files /dev/null and b/assets/wordmark-light.png differ diff --git a/docs/development.md b/docs/development.md index 5d9d011..64d4100 100644 --- a/docs/development.md +++ b/docs/development.md @@ -37,3 +37,17 @@ CoreBluetooth calls through the helper to the Mac's radio. They are operator-run, not CI gates: each needs Xcode, a booted or bootable simulator, Bluetooth granted to the helper, and (for the central lanes) a BLE peer in range. + +## App icons + +Two Icon Composer bundles live in [`assets/`](../assets). `simble.icon` is the +regular mark the menubar helper wears in Finder and its about window; +`simble-dev.icon` is the dimmed dev variant the example apps wear, so a dev +build never passes for the real one. `build-menubar-app.sh` compiles the +regular bundle into the helper; the example project compiles the dev bundle. + +

+ SimBLE helper icon +     + SimBLE example-app icon +

diff --git a/examples/native/project.yml b/examples/native/project.yml index 1f79e32..29fdb49 100644 --- a/examples/native/project.yml +++ b/examples/native/project.yml @@ -22,9 +22,15 @@ targets: platform: iOS sources: - Sources + # The dev variant of the app icon: the Icon Composer bundle in /assets. actool compiles it + # and the build merges its CFBundleIconName into the generated plist, so a dev build never + # passes for a shipping app on the home screen. + - path: ../../assets/simble-dev.icon + buildPhase: resources settings: base: PRODUCT_BUNDLE_IDENTIFIER: dev.simble.SimBLEExample + ASSETCATALOG_COMPILER_APPICON_NAME: simble-dev GENERATE_INFOPLIST_FILE: "YES" INFOPLIST_KEY_CFBundleDisplayName: SimBLE INFOPLIST_KEY_UILaunchScreen_Generation: "YES" diff --git a/scripts/build-menubar-app.sh b/scripts/build-menubar-app.sh index e7aa279..3f0c3f9 100755 --- a/scripts/build-menubar-app.sh +++ b/scripts/build-menubar-app.sh @@ -34,6 +34,8 @@ cat > "$APP/Contents/Info.plist" <CFBundleIdentifierdev.simble.menubar CFBundleNameSimBLE CFBundleDisplayNameSimBLE + CFBundleIconFilesimble + CFBundleIconNamesimble CFBundleExecutablesimble-menubar CFBundleInfoDictionaryVersion6.0 CFBundlePackageTypeAPPL @@ -41,11 +43,33 @@ cat > "$APP/Contents/Info.plist" <CFBundleVersion1 LSMinimumSystemVersion14.0 LSUIElement + ATSApplicationFontsPath. NSBluetoothAlwaysUsageDescriptionSimBLE bridges the iOS and watchOS Simulators to this Mac's Bluetooth radio. PLIST +# Compile the app icon (the Icon Composer .icon) into the bundle. actool emits the Assets.car the +# modern renderer reads, the simble.icns the Dock and Finder fall back to, and a partial plist whose +# icon-name keys already sit in the Info.plist above. +ICON_PARTIAL="$(mktemp)" +xcrun actool "$REPO/assets/simble.icon" --compile "$APP/Contents/Resources" \ + --app-icon simble --platform macosx --minimum-deployment-target 14.0 \ + --output-partial-info-plist "$ICON_PARTIAL" >/dev/null 2>&1 \ + || { echo "actool failed to compile the app icon"; rm -f "$ICON_PARTIAL"; exit 1; } +rm -f "$ICON_PARTIAL" + +# The menu-bar status icon: the Template variants AppKit tints to the bar, black on a light bar and +# white on a dark one. NSImage(named:) resolves @1x/@2x from these two files. +cp "$REPO/assets/menubar/menubar-iconTemplate-18.png" "$APP/Contents/Resources/MenuBarIcon.png" +cp "$REPO/assets/menubar/menubar-iconTemplate-36-2x.png" "$APP/Contents/Resources/MenuBarIcon@2x.png" + +# The brand face. ATSApplicationFontsPath (set in the Info.plist above) registers any font in +# Resources at launch, so the wordmark renders in DM Sans. OFL.txt rides along as the license requires. +cp "$REPO/apps/helper/Resources/fonts/DMSans-Medium.ttf" "$APP/Contents/Resources/DMSans-Medium.ttf" +cp "$REPO/apps/helper/Resources/fonts/DMSans-Bold.ttf" "$APP/Contents/Resources/DMSans-Bold.ttf" +cp "$REPO/apps/helper/Resources/fonts/OFL.txt" "$APP/Contents/Resources/DMSans-OFL.txt" + # Bundle the interposers the helper injects, one per simulator platform. Each is a simulator-slice # binary the fence keeps out of any shipped app; the helper carries them and arms a booted simulator # with the slice matching its platform. Without these the installed helper finds no slice and arms