├── .tool-versions ├── .github └── FUNDING.yml ├── docs ├── docs │ ├── development │ │ ├── index.md │ │ ├── assets │ │ │ └── images │ │ │ │ ├── back.gif │ │ │ │ ├── cnt.gif │ │ │ │ ├── conv.gif │ │ │ │ ├── rest.gif │ │ │ │ ├── sync.gif │ │ │ │ ├── back@2x.gif │ │ │ │ ├── cnt@2x.gif │ │ │ │ ├── conv@2x.gif │ │ │ │ ├── install.gif │ │ │ │ ├── rest@2x.gif │ │ │ │ ├── sync@2x.gif │ │ │ │ └── install@2x.gif │ │ ├── debugging │ │ │ └── index.md │ │ └── architecture │ │ │ └── index.md │ ├── index.md │ ├── glossary │ │ └── index.md │ └── getting-started │ │ └── index.md ├── images │ ├── icons │ │ ├── drive.png │ │ ├── psion.png │ │ ├── install.png │ │ ├── preview.png │ │ └── sketch.png │ ├── icon_128x128.png │ ├── icon_128x128@2x.png │ ├── screenshot-default@2x.png │ └── screenshot-default-dark@2x.png ├── 404.md ├── privacy-policy │ └── index.md ├── assets │ └── css │ │ └── custom.css ├── Gemfile ├── index.md ├── license │ └── index.md └── _config.yml ├── images └── screenshot@2x.png ├── tools ├── ReconnectTools.sis └── reconnect-tools │ ├── screenshot.exe │ └── Reconnect Tools.pkg ├── Reconnect ├── Resources │ ├── cnt.gif │ ├── cnt@2x.gif │ ├── SYS$RPCS.IMG │ ├── install.gif │ └── install@2x.gif ├── Assets.xcassets │ ├── Contents.json │ ├── Data16.imageset │ │ ├── data.png │ │ └── Contents.json │ ├── OPL16.imageset │ │ ├── opl.png │ │ └── Contents.json │ ├── Word16.imageset │ │ ├── word.png │ │ └── Contents.json │ ├── Disk16.imageset │ │ ├── Disk32.png │ │ └── Contents.json │ ├── Sheet16.imageset │ │ ├── sheet.png │ │ └── Contents.json │ ├── Agenda16.imageset │ │ ├── agenda.png │ │ └── Contents.json │ ├── Drive16.imageset │ │ ├── Drive32.png │ │ └── Contents.json │ ├── Jotter16.imageset │ │ ├── jotter.png │ │ └── Contents.json │ ├── Psion16.imageset │ │ ├── Psion16.png │ │ └── Contents.json │ ├── Record16.imageset │ │ ├── record.png │ │ └── Contents.json │ ├── Sketch16.imageset │ │ ├── sketch.png │ │ └── Contents.json │ ├── Folder16.imageset │ │ ├── Folder32.png │ │ └── Contents.json │ ├── Icon.imageset │ │ ├── icon_512x512.png │ │ ├── icon_512x512@2x@2x.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── icon_16x16.png │ │ ├── icon_32x32.png │ │ ├── icon_128x128.png │ │ ├── icon_256x256.png │ │ ├── icon_512x512.png │ │ ├── icon_16x16@2x@2x.png │ │ ├── icon_32x32@2x@2x.png │ │ ├── icon_128x128@2x@2x.png │ │ ├── icon_256x256@2x@2x.png │ │ ├── icon_512x512@2x@2x.png │ │ └── Contents.json │ ├── Install16.imageset │ │ ├── Install16.png │ │ └── Contents.json │ ├── Installer.imageset │ │ ├── installer.png │ │ ├── installer@2x.png │ │ └── Contents.json │ ├── FileUnknown16.imageset │ │ ├── Unknown32.png │ │ └── Contents.json │ ├── UnknownAppIcon.imageset │ │ ├── question.png │ │ └── Contents.json │ ├── Disconnected.imageset │ │ ├── disconnected32.png │ │ ├── disconnected@2x.png │ │ └── Contents.json │ ├── Disconnected16.imageset │ │ ├── Disconnected16.png │ │ └── Contents.json │ └── AccentColor.colorset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Reconnect.entitlements ├── Model │ ├── SidebarItem.swift │ ├── Refreshable.swift │ ├── ParentNavigable.swift │ ├── CheckForUpdatesViewModel.swift │ ├── FileReference.swift │ ├── FileManageable.swift │ ├── ParentNavigableProxy.swift │ ├── RefreshableProxy.swift │ ├── EditableTextModel.swift │ └── BrowserSection.swift ├── Extensions │ ├── ShapeStyle.swift │ └── SisInstallError.swift ├── Views │ ├── DeviceModelProxy.swift │ ├── SectionLabel.swift │ ├── AnimagedImage.swift │ ├── ProgressAnimation.swift │ ├── DeviceDriveGroup.swift │ ├── PixelImage.swift │ ├── EditableText.swift │ ├── DeviceView.swift │ ├── ThumbnailView.swift │ ├── CheckForUpdatesView.swift │ ├── FileTypePopover.swift │ ├── Settings │ │ ├── SettingsButton.swift │ │ └── SerialDeviceEnableToggle.swift │ ├── DetailsGroup.swift │ └── Installer │ │ ├── ReplaceQueryInstallerPage.swift │ │ └── TextQueryInstallerPage.swift ├── Toolbars │ ├── ToolbarSpacer.swift │ ├── DeviceToolbar.swift │ ├── RefreshToolbar.swift │ └── FileToolbar.swift ├── Commands │ ├── SparkleCommands.swift │ ├── RefreshCommands.swift │ ├── DeviceCommands.swift │ └── NavigationCommands.swift ├── Environment │ └── WindowProxy.swift ├── Software Index │ ├── API │ │ ├── Kind.swift │ │ ├── ReferenceItem.swift │ │ ├── Image.swift │ │ ├── Version.swift │ │ ├── Collection.swift │ │ ├── Program.swift │ │ └── Release.swift │ ├── Model │ │ └── PsionSoftwareIndexError.swift │ ├── Views │ │ ├── IconView.swift │ │ └── ItemView.swift │ └── Styles │ │ └── UnadornedCircularProgressViewStyle.swift ├── Styles │ └── DetailsLabeledContentStyle.swift ├── Windows │ ├── TransfersWindow.swift │ └── NSInstallerWindow.swift ├── AppDelegate.swift └── Modifiers │ ├── OptionalNavigationSubtitle.swift │ └── ShowsDeviceProgress.swift ├── graphics ├── assets │ ├── opl │ │ └── opl.png │ ├── data │ │ └── data.png │ ├── word │ │ └── word.png │ ├── disk │ │ └── Disk32.png │ ├── sheet │ │ └── sheet.png │ ├── agenda │ │ └── agenda.png │ ├── drives │ │ └── Drive32.png │ ├── folder │ │ ├── Folder32.png │ │ ├── Directory16.png │ │ ├── Directory24.png │ │ ├── Directory32.acorn │ │ └── Directory32.png │ ├── jotter │ │ └── jotter.png │ ├── record │ │ └── record.png │ ├── sketch │ │ └── sketch.png │ ├── series-5 │ │ ├── series5.png │ │ └── series5large.png │ ├── unknown │ │ ├── Unknown32.png │ │ ├── Unknown32.acorn │ │ └── Unknown32_PsiWin.png │ ├── status │ │ ├── StatusConnected.png │ │ ├── StatusDisconnected.png │ │ ├── StatusConnectedDark.png │ │ └── StatusDisconnectedDark.png │ └── disconnected │ │ ├── disconnected32.png │ │ └── disconnected32@2x.png └── app-icon │ ├── Series 5 Icon.sketch │ └── SF Symbols Icon.symbolic ├── ReconnectMenu ├── Assets.xcassets │ ├── Contents.json │ ├── AppIcon.appiconset │ │ ├── icon_128x128.png │ │ ├── icon_16x16.png │ │ ├── icon_256x256.png │ │ ├── icon_32x32.png │ │ ├── icon_512x512.png │ │ ├── icon_16x16@2x@2x.png │ │ ├── icon_32x32@2x@2x.png │ │ ├── icon_128x128@2x@2x.png │ │ ├── icon_256x256@2x@2x.png │ │ ├── icon_512x512@2x@2x.png │ │ └── Contents.json │ ├── StatusUnknown.imageset │ │ ├── StatusMissing.png │ │ └── Contents.json │ ├── StatusConnected.imageset │ │ ├── StatusConnected.png │ │ ├── StatusConnectedDark.png │ │ └── Contents.json │ ├── StatusDisconnected.imageset │ │ ├── StatusDisconnected.png │ │ ├── StatusDisconnectedDark.png │ │ └── Contents.json │ └── AccentColor.colorset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Info.plist ├── ReconnectMenu.entitlements ├── ReconnectMenuApp.swift └── Views │ ├── StatusIcon.swift │ └── MainMenu.swift ├── .gitignore ├── profiles ├── Reconnect_Developer_ID_Profile.provisionprofile ├── Reconnect_Menu_Developer_ID_Profile.provisionprofile └── Reconnect_Previews_Developer_ID_Profile.provisionprofile ├── ReconnectCore ├── .gitignore ├── Tests │ └── ReconnectCoreTests │ │ └── ReconnectCoreTests.swift ├── Sources │ └── ReconnectCore │ │ ├── Extensions │ │ ├── Array.swift │ │ ├── UTType.swift │ │ ├── Sis.File.swift │ │ ├── rfsv.errs.swift │ │ ├── NSRunningApplication.swift │ │ ├── UInt32.swift │ │ ├── DriveInfo.swift │ │ ├── MachineType.swift │ │ ├── Locale.swift │ │ └── PsiLuaEnv.swift │ │ ├── Utilities │ │ └── Graphics.swift │ │ ├── XPC │ │ ├── DaemonClientInterface.swift │ │ ├── DaemonInterface.swift │ │ └── DaemonInfo.swift │ │ ├── PLP │ │ └── PsionClient.swift │ │ └── Model │ │ └── ReconnectError.swift └── Package.swift ├── scripts ├── release-notes.html ├── release-notes.md ├── release.sh ├── environment.sh ├── update-release-notes.sh ├── build-website.sh └── install-dependencies.sh ├── ReconnectPreviews ├── ReconnectPreviews.entitlements ├── Info.plist ├── Base.lproj │ └── PreviewViewController.xib ├── InstallerPreviewView.swift └── PreviewViewController.swift ├── reconnectd ├── uk.co.jbmorley.reconnect.apps.apple.reconnectd.plist ├── Info.plist └── main.swift ├── ExportOptions.plist ├── .gitmodules ├── ReconnectTests ├── ReconnectTests.swift └── WindowsPathTests.swift ├── README.md └── ReconnectUITests ├── ReconnectUITestsLaunchTests.swift └── ReconnectUITests.swift /.tool-versions: -------------------------------------------------------------------------------- 1 | python 3.12.1 2 | ruby 3.1.2 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [jbmorley] 2 | buy_me_a_coffee: jbmorley 3 | -------------------------------------------------------------------------------- /docs/docs/development/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Development 3 | --- 4 | -------------------------------------------------------------------------------- /images/screenshot@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/images/screenshot@2x.png -------------------------------------------------------------------------------- /tools/ReconnectTools.sis: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/tools/ReconnectTools.sis -------------------------------------------------------------------------------- /Reconnect/Resources/cnt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Resources/cnt.gif -------------------------------------------------------------------------------- /docs/images/icons/drive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icons/drive.png -------------------------------------------------------------------------------- /docs/images/icons/psion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icons/psion.png -------------------------------------------------------------------------------- /graphics/assets/opl/opl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/opl/opl.png -------------------------------------------------------------------------------- /Reconnect/Resources/cnt@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Resources/cnt@2x.gif -------------------------------------------------------------------------------- /docs/images/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icon_128x128.png -------------------------------------------------------------------------------- /docs/images/icons/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icons/install.png -------------------------------------------------------------------------------- /docs/images/icons/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icons/preview.png -------------------------------------------------------------------------------- /docs/images/icons/sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icons/sketch.png -------------------------------------------------------------------------------- /graphics/assets/data/data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/data/data.png -------------------------------------------------------------------------------- /graphics/assets/word/word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/word/word.png -------------------------------------------------------------------------------- /Reconnect/Resources/SYS$RPCS.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Resources/SYS$RPCS.IMG -------------------------------------------------------------------------------- /Reconnect/Resources/install.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Resources/install.gif -------------------------------------------------------------------------------- /docs/images/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icon_128x128@2x.png -------------------------------------------------------------------------------- /graphics/assets/disk/Disk32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/disk/Disk32.png -------------------------------------------------------------------------------- /graphics/assets/sheet/sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/sheet/sheet.png -------------------------------------------------------------------------------- /Reconnect/Resources/install@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Resources/install@2x.gif -------------------------------------------------------------------------------- /graphics/assets/agenda/agenda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/agenda/agenda.png -------------------------------------------------------------------------------- /graphics/assets/drives/Drive32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/drives/Drive32.png -------------------------------------------------------------------------------- /graphics/assets/folder/Folder32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/folder/Folder32.png -------------------------------------------------------------------------------- /graphics/assets/jotter/jotter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/jotter/jotter.png -------------------------------------------------------------------------------- /graphics/assets/record/record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/record/record.png -------------------------------------------------------------------------------- /graphics/assets/sketch/sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/sketch/sketch.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /docs/images/screenshot-default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/screenshot-default@2x.png -------------------------------------------------------------------------------- /graphics/assets/series-5/series5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/series-5/series5.png -------------------------------------------------------------------------------- /graphics/assets/unknown/Unknown32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/unknown/Unknown32.png -------------------------------------------------------------------------------- /tools/reconnect-tools/screenshot.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/tools/reconnect-tools/screenshot.exe -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /graphics/app-icon/Series 5 Icon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/app-icon/Series 5 Icon.sketch -------------------------------------------------------------------------------- /graphics/assets/folder/Directory16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/folder/Directory16.png -------------------------------------------------------------------------------- /graphics/assets/folder/Directory24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/folder/Directory24.png -------------------------------------------------------------------------------- /graphics/assets/folder/Directory32.acorn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/folder/Directory32.acorn -------------------------------------------------------------------------------- /graphics/assets/folder/Directory32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/folder/Directory32.png -------------------------------------------------------------------------------- /graphics/assets/unknown/Unknown32.acorn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/unknown/Unknown32.acorn -------------------------------------------------------------------------------- /docs/images/screenshot-default-dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/screenshot-default-dark@2x.png -------------------------------------------------------------------------------- /graphics/assets/series-5/series5large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/series-5/series5large.png -------------------------------------------------------------------------------- /graphics/assets/status/StatusConnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/status/StatusConnected.png -------------------------------------------------------------------------------- /docs/404.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Not Found 3 | layout: page 4 | --- 5 | 6 |

The page you requested could not be found.

7 | -------------------------------------------------------------------------------- /docs/docs/development/assets/images/back.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/back.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/cnt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/cnt.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/conv.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/conv.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/rest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/rest.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/sync.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/sync.gif -------------------------------------------------------------------------------- /graphics/assets/status/StatusDisconnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/status/StatusDisconnected.png -------------------------------------------------------------------------------- /graphics/assets/unknown/Unknown32_PsiWin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/unknown/Unknown32_PsiWin.png -------------------------------------------------------------------------------- /docs/docs/development/assets/images/back@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/back@2x.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/cnt@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/cnt@2x.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/conv@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/conv@2x.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/install.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/install.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/rest@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/rest@2x.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/sync@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/sync@2x.gif -------------------------------------------------------------------------------- /graphics/assets/disconnected/disconnected32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/disconnected/disconnected32.png -------------------------------------------------------------------------------- /graphics/assets/status/StatusConnectedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/status/StatusConnectedDark.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | .swiftpm 3 | /_site 4 | /.local 5 | /archives 6 | /build 7 | /docs/_site 8 | /docs/.jekyll-cache 9 | /docs/releases 10 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Data16.imageset/data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Data16.imageset/data.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/OPL16.imageset/opl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/OPL16.imageset/opl.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Word16.imageset/word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Word16.imageset/word.png -------------------------------------------------------------------------------- /Reconnect/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /docs/docs/development/assets/images/install@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/install@2x.gif -------------------------------------------------------------------------------- /graphics/assets/disconnected/disconnected32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/disconnected/disconnected32@2x.png -------------------------------------------------------------------------------- /graphics/assets/status/StatusDisconnectedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/status/StatusDisconnectedDark.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disk16.imageset/Disk32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Disk16.imageset/Disk32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Sheet16.imageset/sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Sheet16.imageset/sheet.png -------------------------------------------------------------------------------- /ReconnectMenu/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Agenda16.imageset/agenda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Agenda16.imageset/agenda.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Drive16.imageset/Drive32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Drive16.imageset/Drive32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Jotter16.imageset/jotter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Jotter16.imageset/jotter.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Psion16.imageset/Psion16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Psion16.imageset/Psion16.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Record16.imageset/record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Record16.imageset/record.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Sketch16.imageset/sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Sketch16.imageset/sketch.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Folder16.imageset/Folder32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Folder16.imageset/Folder32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Icon.imageset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Icon.imageset/icon_512x512.png -------------------------------------------------------------------------------- /profiles/Reconnect_Developer_ID_Profile.provisionprofile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/profiles/Reconnect_Developer_ID_Profile.provisionprofile -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Install16.imageset/Install16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Install16.imageset/Install16.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Installer.imageset/installer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Installer.imageset/installer.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/FileUnknown16.imageset/Unknown32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/FileUnknown16.imageset/Unknown32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Icon.imageset/icon_512x512@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Icon.imageset/icon_512x512@2x@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Installer.imageset/installer@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Installer.imageset/installer@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/UnknownAppIcon.imageset/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/UnknownAppIcon.imageset/question.png -------------------------------------------------------------------------------- /profiles/Reconnect_Menu_Developer_ID_Profile.provisionprofile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/profiles/Reconnect_Menu_Developer_ID_Profile.provisionprofile -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /profiles/Reconnect_Previews_Developer_ID_Profile.provisionprofile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/profiles/Reconnect_Previews_Developer_ID_Profile.provisionprofile -------------------------------------------------------------------------------- /tools/reconnect-tools/Reconnect Tools.pkg: -------------------------------------------------------------------------------- 1 | &EN 2 | #{"Reconnect Tools"},(0x100092cb),0,1,0 3 | "D:\Projects\reconnect-tools\screenshot.exe"-"C:\System\Reconnect\screenshot.exe" 4 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disconnected.imageset/disconnected32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Disconnected.imageset/disconnected32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disconnected.imageset/disconnected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Disconnected.imageset/disconnected@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disconnected16.imageset/Disconnected16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Disconnected16.imageset/Disconnected16.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusUnknown.imageset/StatusMissing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/StatusUnknown.imageset/StatusMissing.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusConnected.imageset/StatusConnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/StatusConnected.imageset/StatusConnected.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusConnected.imageset/StatusConnectedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/StatusConnected.imageset/StatusConnectedDark.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusDisconnected.imageset/StatusDisconnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/StatusDisconnected.imageset/StatusDisconnected.png -------------------------------------------------------------------------------- /ReconnectCore/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusDisconnected.imageset/StatusDisconnectedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/StatusDisconnected.imageset/StatusDisconnectedDark.png -------------------------------------------------------------------------------- /ReconnectMenu/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Documentation 3 | layout: documentation 4 | shows_title: false 5 | --- 6 | 7 | Check out the [Getting Started](/docs/getting-started) documentation if you're new to Psions and Reconnect. 8 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ReconnectMenu/ReconnectMenu.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/privacy-policy/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Privacy Policy 3 | --- 4 | 5 | Reconnect does not collect or store any personal data. 6 | 7 | Should this policy change in the future, we will make all reasonable efforts to inform you and provide mechanisms to opt-out. 8 | -------------------------------------------------------------------------------- /Reconnect/Reconnect.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.device.serial 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/docs/glossary/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Glossary 3 | --- 4 | 5 | # DSR/DTR 6 | 7 | Data Set Ready / Data Terminal Ready (RS232 hardware control signal) 8 | 9 | # PLP 10 | 11 | [Psion Link Protocol](https://thoukydides.github.io/riscos-psifs/plp.html) 12 | 13 | # RTS/CTS 14 | 15 | Request To Send / Clear To Send (RS232 hardware control signal) 16 | -------------------------------------------------------------------------------- /scripts/release-notes.html: -------------------------------------------------------------------------------- 1 | {% for release in releases -%} 2 | {{ release.version }} 3 | 8 | {% endfor %} 9 | -------------------------------------------------------------------------------- /ReconnectPreviews/ReconnectPreviews.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/assets/css/custom.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --brand-color: #ff9502; 3 | --showcase-color-high: #ff9502; 4 | --showcase-color-low: #ff9502; 5 | } 6 | 7 | @media (prefers-color-scheme: dark) { 8 | :root { 9 | --brand-color: #ff9502; 10 | --showcase-color-high: #5b3400; 11 | --showcase-color-low: #5b3400; 12 | } 13 | } 14 | 15 | img.icon { 16 | image-rendering: pixelated; 17 | scale: 1.5; 18 | } 19 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Data16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "data.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/OPL16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "opl.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Word16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "word.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Agenda16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "agenda.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disk16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "Disk32.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Drive16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "Drive32.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Jotter16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "jotter.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Psion16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "Psion16.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Record16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "record.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Sheet16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "sheet.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Sketch16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "sketch.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Folder16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "Folder32.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Install16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "Install16.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/FileUnknown16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "Unknown32.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/UnknownAppIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "question.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disconnected16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "Disconnected16.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusUnknown.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "StatusMissing.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Installer.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "installer.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "installer@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_512x512.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "icon_512x512@2x@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disconnected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "disconnected32.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "disconnected@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docs/docs/development/debugging/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Debugging 3 | --- 4 | 5 | Debugging Reconnect is a little more awkward than normal since plptools uses signals internally which are trapped by Xcode and lldb by default, causing the debugger to pause regularly. You can disable this automatic behavior by adding the following line to `~/.lldbinit-Xcode`: 6 | 7 | ``` 8 | process handle SIGUSR1 -n true -p true -s false 9 | ``` 10 | 11 | > [!WARNING] 12 | > Changing `~/.lldbinit-Xcode` will cause Xcode to ignore `SIGUSR1` for all projects. 13 | -------------------------------------------------------------------------------- /scripts/release-notes.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Releases 3 | --- 4 | 5 | {% for release in releases -%} 6 | ## {% if release.is_released %}{{ release.version }}{% else %}{{ release.version }} (Unreleased){% endif %} 7 | {% for section in release.sections -%} 8 | {% for change in section.changes | reverse -%} 9 | - {{ change.description | regex_replace("\\s+\\(#(\\d+)\\)$", "") }}{% if change.scope %}{{ change.scope }}{% endif %} 10 | {% endfor %}{% endfor %} 11 | {% endfor %} 12 | -------------------------------------------------------------------------------- /reconnectd/uk.co.jbmorley.reconnect.apps.apple.reconnectd.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | uk.co.jbmorley.reconnect.apps.apple.reconnectd 7 | BundleProgram 8 | Contents/Resources/reconnectd 9 | MachServices 10 | 11 | uk.co.jbmorley.reconnect.apps.apple.xpc.daemon 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "jekyll", "~> 4.2.0" 4 | gem "webrick", "~> 1.7" 5 | 6 | gem "theme", path: "_theme" 7 | 8 | group :jekyll_plugins do 9 | gem 'jekyll-environment-variables' 10 | gem "jekyll-feed", "~> 0.12" 11 | gem "jekyll-image-size", "~> 1.2" 12 | gem "jekyll-toc" 13 | gem 'jekyll-gfm-admonitions' 14 | end 15 | 16 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem 17 | # and associated library. 18 | platforms :mingw, :x64_mingw, :mswin, :jruby do 19 | gem "tzinfo", "~> 1.2" 20 | gem "tzinfo-data" 21 | end 22 | 23 | # Performance-booster for watching directories on Windows 24 | gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] 25 | -------------------------------------------------------------------------------- /reconnectd/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | $(PRODUCT_BUNDLE_IDENTIFIER) 7 | CFBundleName 8 | reconnectd 9 | CFBundleShortVersionString 10 | $(MARKETING_VERSION) 11 | CFBundleVersion 12 | $(CURRENT_PROJECT_VERSION) 13 | LSMinimumSystemVersion 14 | $(MACOSX_DEPLOYMENT_TARGET) 15 | NSHumanReadableCopyright 16 | Copyright © 2023-2025 Jason Morley 17 | 18 | 19 | -------------------------------------------------------------------------------- /graphics/app-icon/SF Symbols Icon.symbolic: -------------------------------------------------------------------------------- 1 | { 2 | "symbolColor" : { 3 | "red" : 0.9999999403953552, 4 | "green" : 0.9999999403953552, 5 | "blue" : 0.9999999403953552, 6 | "alpha" : 1 7 | }, 8 | "version" : 1, 9 | "topColor" : { 10 | "blue" : 0, 11 | "red" : 1, 12 | "alpha" : 1, 13 | "green" : 0.5763723254 14 | }, 15 | "shadowOpacity" : 0.27266666666666667, 16 | "symbol" : { 17 | "family" : "sf-symbols", 18 | "name" : "cable.connector" 19 | }, 20 | "id" : "24670896-9CE6-40DD-86CB-111B3E0DEB8D", 21 | "shadowHeight" : 0.3, 22 | "bottomColor" : { 23 | "green" : 0.5763723254, 24 | "blue" : 0, 25 | "red" : 1, 26 | "alpha" : 1 27 | }, 28 | "iconOffset" : [ 29 | 0.008556547619047672, 30 | 0 31 | ], 32 | "iconScale" : 0.8198560368342597 33 | } -------------------------------------------------------------------------------- /ReconnectPreviews/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSExtension 6 | 7 | NSExtensionAttributes 8 | 9 | QLIsDataBasedPreview 10 | 11 | QLSupportedContentTypes 12 | 13 | com.psion.sis 14 | org.opolua.sis 15 | 16 | QLSupportsSearchableItems 17 | 18 | 19 | NSExtensionPointIdentifier 20 | com.apple.quicklook.preview 21 | NSExtensionPrincipalClass 22 | $(PRODUCT_MODULE_NAME).PreviewViewController 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ExportOptions.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | destination 6 | export 7 | method 8 | developer-id 9 | provisioningProfiles 10 | 11 | uk.co.jbmorley.reconnect.apps.apple 12 | Reconnect Developer ID Profile 13 | uk.co.jbmorley.reconnect.apps.apple.menu 14 | Reconnect Menu Developer ID Profile 15 | uk.co.jbmorley.reconnect.apps.apple.previews 16 | Reconnect Previews Developer ID Profile 17 | 18 | signingCertificate 19 | Developer ID Application 20 | signingStyle 21 | manual 22 | teamID 23 | QS82QFHKWB 24 | 25 | 26 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "scripts/changes"] 2 | path = scripts/changes 3 | url = https://github.com/jbmorley/changes.git 4 | [submodule "scripts/build-tools"] 5 | path = scripts/build-tools 6 | url = https://github.com/jbmorley/build-tools.git 7 | [submodule "dependencies/diligence"] 8 | path = dependencies/diligence 9 | url = https://github.com/inseven/diligence.git 10 | [submodule "dependencies/plptools"] 11 | path = dependencies/plptools/plptools 12 | url = https://github.com/jbmorley/plptools.git 13 | [submodule "dependencies/interact"] 14 | path = dependencies/interact 15 | url = https://github.com/inseven/interact.git 16 | [submodule "scripts/Sparkle"] 17 | path = scripts/Sparkle 18 | url = https://github.com/sparkle-project/Sparkle 19 | [submodule "dependencies/opolua/opolua"] 20 | path = dependencies/opolua 21 | url = https://github.com/inseven/opolua.git 22 | [submodule "docs/_theme"] 23 | path = docs/_theme 24 | url = https://github.com/jbmorley/showcase-theme.git 25 | -------------------------------------------------------------------------------- /reconnectd/main.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | let daemon = Daemon() 22 | daemon.start() 23 | RunLoop.main.run() 24 | -------------------------------------------------------------------------------- /ReconnectTests/ReconnectTests.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import XCTest 20 | @testable import Reconnect 21 | 22 | final class ReconnectTests: XCTestCase { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /ReconnectCore/Tests/ReconnectCoreTests/ReconnectCoreTests.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import XCTest 20 | @testable import ReconnectCore 21 | 22 | final class ReconnectCoreTests: XCTestCase { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Reconnect/Model/SidebarItem.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct SidebarItem: Hashable, Identifiable { 22 | 23 | var id: Self { self } 24 | var section: BrowserSection 25 | var children: [SidebarItem]? = nil 26 | 27 | } 28 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Extensions/Array.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | public extension Array { 22 | 23 | func appending(_ element: Element) -> [Element] { 24 | return self + [element] 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Reconnect/Extensions/ShapeStyle.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | extension ShapeStyle where Self == Color { 22 | 23 | static var textBackgroundColor: Self { 24 | return Color(nsColor: .textBackgroundColor) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Reconnect/Views/DeviceModelProxy.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | class DeviceModelProxy: ObservableObject { 22 | 23 | let deviceModel: DeviceModel 24 | 25 | init(deviceModel: DeviceModel) { 26 | self.deviceModel = deviceModel 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Reconnect/Model/Refreshable.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | protocol Refreshable { 22 | 23 | @MainActor 24 | var canRefresh: Bool { get } 25 | 26 | @MainActor 27 | var isRefreshing: Bool { get } 28 | 29 | @MainActor 30 | func refresh() 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Reconnect/Toolbars/ToolbarSpacer.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct ToolbarSpacer: CustomizableToolbarContent { 22 | 23 | let id: String 24 | 25 | var body: some CustomizableToolbarContent { 26 | ToolbarItem(id: id) { 27 | Spacer() 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusConnected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "StatusConnected.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "StatusConnectedDark.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "appearances" : [ 25 | { 26 | "appearance" : "luminosity", 27 | "value" : "dark" 28 | } 29 | ], 30 | "idiom" : "universal", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "universal", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "appearances" : [ 39 | { 40 | "appearance" : "luminosity", 41 | "value" : "dark" 42 | } 43 | ], 44 | "idiom" : "universal", 45 | "scale" : "3x" 46 | } 47 | ], 48 | "info" : { 49 | "author" : "xcode", 50 | "version" : 1 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusDisconnected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "StatusDisconnected.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "StatusDisconnectedDark.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "appearances" : [ 25 | { 26 | "appearance" : "luminosity", 27 | "value" : "dark" 28 | } 29 | ], 30 | "idiom" : "universal", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "universal", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "appearances" : [ 39 | { 40 | "appearance" : "luminosity", 41 | "value" : "dark" 42 | } 43 | ], 44 | "idiom" : "universal", 45 | "scale" : "3x" 46 | } 47 | ], 48 | "info" : { 49 | "author" : "xcode", 50 | "version" : 1 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Reconnect/Views/SectionLabel.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct SectionLabel: View { 22 | 23 | let section: BrowserSection 24 | 25 | init(section: BrowserSection) { 26 | self.section = section 27 | } 28 | 29 | var body: some View { 30 | Label(section.title, image: section.image) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Reconnect -- Psion connectivity for macOS 4 | # 5 | # Copyright (C) 2024-2025 Jason Morley 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 | 21 | set -e 22 | set -o pipefail 23 | set -x 24 | 25 | # Actually make the release. 26 | gh release create "$CHANGES_TAG" --title "$CHANGES_QUALIFIED_TITLE" --notes-file "$CHANGES_NOTES_FILE" 27 | 28 | # Upload the attachments. 29 | for attachment in "$@" 30 | do 31 | gh release upload "$CHANGES_TAG" "$attachment" 32 | done 33 | -------------------------------------------------------------------------------- /Reconnect/Model/ParentNavigable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParentNavigable.swift 3 | // Reconnect 4 | // 5 | // Created by Jason Barrie Morley on 08/08/2025. 6 | // 7 | 8 | 9 | // Reconnect -- Psion connectivity for macOS 10 | // 11 | // Copyright (C) 2024-2025 Jason Morley 12 | // 13 | // This program is free software; you can redistribute it and/or modify 14 | // it under the terms of the GNU General Public License as published by 15 | // the Free Software Foundation; either version 2 of the License, or 16 | // (at your option) any later version. 17 | // 18 | // This program is distributed in the hope that it will be useful, 19 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | // GNU General Public License for more details. 22 | // 23 | // You should have received a copy of the GNU General Public License 24 | // along with this program; if not, write to the Free Software 25 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 26 | 27 | import Foundation 28 | 29 | protocol ParentNavigable { 30 | 31 | @MainActor 32 | var canNavigateToParent: Bool { get } 33 | 34 | @MainActor 35 | func navigateToParent() 36 | 37 | } 38 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: showcase 3 | --- 4 | 5 | 32 | -------------------------------------------------------------------------------- /docs/license/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: License 3 | --- 4 | 5 | Reconnect is licensed under the GNU General Public License (GPL) version 2 (see [LICENSE](https://github.com/inseven/reconnect/blob/main/LICENSE)). It depends on the following separately licensed third-party libraries and components: 6 | 7 | - [Diligence](https://github.com/inseven/diligence), MIT License 8 | - [Interact](https://github.com/inseven/interact), MIT License 9 | - [Licensable](https://github.com/inseven/licensable), MIT License 10 | - [Lua](https://www.lua.org), MIT License 11 | - [OpoLua](https://github.com/inseven/opolua), MIT License 12 | - [plptools](https://github.com/rrthomas/plptools), GPL 2.0 License 13 | - [PsionSoftwareIndexSwift](https://github.com/inseven/PsionSoftwareIndexSwift), MIT License 14 | - [Sparkle](https://github.com/sparkle-project/Sparkle), Sparkle License 15 | - [Swift Algorithms](https://github.com/apple/swift-algorithms), Apache 2.0 License 16 | - [Swift Argument Parser](https://github.com/apple/swift-argument-parser), Apache 2.0 License 17 | - [Swift Numerics](https://github.com/apple/swift-numerics), Apache 2.0 License 18 | 19 | Reconnect includes graphics (icons and animations) from the original Psion PsiWin and PsiMac software. These remain copyright Psion PLC. 20 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Extensions/UTType.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import UniformTypeIdentifiers 20 | 21 | extension UTType { 22 | 23 | public static var sis: Self { 24 | return UTType(exportedAs: "com.psion.sis", conformingTo: .data) 25 | } 26 | 27 | public static var sisOpoLua: Self { 28 | return UTType(exportedAs: "org.opolua.sis", conformingTo: .data) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: theme 2 | title: Reconnect 3 | author: Jason Morley 4 | email: support@jbmorley.co.uk 5 | description: Psion Connectivity for macOS 6 | url: "https://reconnect.jbmorley.co.uk" 7 | baseurl: "" 8 | repository: https://github.com/inseven/reconnect 9 | copyright: 2023-2025 Jason Morley 10 | 11 | navigation: 12 | - title: Reconnect 13 | href: / 14 | - title: Documentation 15 | href: /docs 16 | - title: Releases 17 | href: /releases 18 | - title: GitHub 19 | href: https://github.com/inseven/reconnect 20 | - title: Donate 21 | href: https://jbmorley.co.uk/support/ 22 | 23 | footer: 24 | - title: Privacy Policy 25 | href: /privacy-policy 26 | - title: License 27 | href: /license 28 | - title: More Software 29 | href: https://jbmorley.co.uk/software/ 30 | 31 | plugins: 32 | - jekyll-environment-variables 33 | - jekyll-feed 34 | - jekyll-gfm-admonitions 35 | - jekyll-image-size 36 | - jekyll-toc 37 | destination: ../_site 38 | 39 | toc: 40 | ordered_list: true 41 | 42 | defaults: 43 | - values: 44 | layout: "page" 45 | - scope: 46 | path: "docs" 47 | values: 48 | layout: "documentation" 49 | toc: true 50 | shows_title: true 51 | -------------------------------------------------------------------------------- /Reconnect/Commands/SparkleCommands.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | public struct SparkleCommands: Commands { 22 | 23 | let applicationModel: ApplicationModel 24 | 25 | public var body: some Commands { 26 | CommandGroup(before: .appSettings) { 27 | CheckForUpdatesView(updater: applicationModel.updaterController.updater) 28 | Divider() 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Reconnect/Environment/WindowProxy.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | class WindowProxy { 22 | 23 | weak var nsWindow: NSWindow? 24 | 25 | var title: String { 26 | get { 27 | return nsWindow?.title ?? "" 28 | } 29 | set { 30 | nsWindow?.title = newValue 31 | } 32 | } 33 | } 34 | 35 | extension EnvironmentValues { 36 | 37 | @Entry var window = WindowProxy() 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Reconnect/Model/CheckForUpdatesViewModel.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import Sparkle 22 | 23 | // This view model class publishes when new updates can be checked by the user 24 | final class CheckForUpdatesViewModel: ObservableObject { 25 | @Published var canCheckForUpdates = false 26 | 27 | init(updater: SPUUpdater) { 28 | updater.publisher(for: \.canCheckForUpdates) 29 | .assign(to: &$canCheckForUpdates) 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Reconnect/Software Index/API/Kind.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import Foundation 22 | 23 | public enum Kind: String, Codable { 24 | case installer 25 | case standalone 26 | } 27 | -------------------------------------------------------------------------------- /Reconnect/Views/AnimagedImage.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct AnimatedImage: NSViewRepresentable { 22 | 23 | var name: String 24 | 25 | init(named name: String) { 26 | self.name = name 27 | } 28 | 29 | func makeNSView(context: Context) -> NSImageView { 30 | let image = NSImage(named: name)! 31 | return NSImageView(image: image) 32 | } 33 | 34 | func updateNSView(_ uiView: NSImageView, context: Context) { 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Reconnect/Software Index/API/ReferenceItem.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import Foundation 22 | 23 | struct ReferenceItem: Codable, Hashable { 24 | 25 | let name: String 26 | let url: URL? 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Extensions/Sis.File.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | import OpoLua 22 | 23 | extension Sis.File: @retroactive Identifiable { 24 | 25 | public var id: String { "\(uid):\(version)" } 26 | 27 | public var localizedDisplayName: String { 28 | return (try? Locale.localize(name).text) ?? "Unknown Installer" 29 | } 30 | 31 | public var localizedDisplayNameAndVersion: String { 32 | return "\(localizedDisplayName) \(version)" 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Extensions/rfsv.errs.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | import ncp 22 | 23 | extension rfsv.errs { 24 | 25 | public func check() throws(PLPToolsError) { 26 | guard rawValue != 0 else { 27 | return 28 | } 29 | guard let error = PLPToolsError(rawValue: rawValue) else { 30 | print("Encountered unknown plptools error code (\(rawValue).") 31 | throw .unknown 32 | } 33 | throw error 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Reconnect/Model/FileReference.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | import ReconnectCore 22 | 23 | enum FileReference: Equatable { 24 | 25 | case local(URL) 26 | case remote(FileServer.DirectoryEntry) 27 | 28 | } 29 | 30 | extension FileReference { 31 | 32 | var name: String { 33 | switch self { 34 | case .local(let url): 35 | return url.lastPathComponent 36 | case .remote(let directoryEntry): 37 | return directoryEntry.name 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Reconnect/Software Index/API/Image.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import Foundation 22 | 23 | struct SoftwareIndexImage: Codable, Hashable { 24 | 25 | let width: Int 26 | let height: Int 27 | let path: String 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Reconnect/Views/ProgressAnimation.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct ProgressAnimation: View { 22 | 23 | struct LayoutMetrics { 24 | static let width: CGFloat = 240 25 | static let height: CGFloat = 70 26 | } 27 | 28 | private let name: String 29 | 30 | init(_ name: String) { 31 | self.name = name 32 | } 33 | 34 | var body: some View { 35 | AnimatedImage(named: name) 36 | .frame(width: LayoutMetrics.width, height: LayoutMetrics.height) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /ReconnectPreviews/Base.lproj/PreviewViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Reconnect/Styles/DetailsLabeledContentStyle.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct DetailsLabeledContentStyle: LabeledContentStyle { 22 | 23 | func makeBody(configuration: Configuration) -> some View { 24 | HStack { 25 | configuration.label 26 | .bold() 27 | configuration.content 28 | } 29 | } 30 | 31 | } 32 | 33 | extension LabeledContentStyle where Self == DetailsLabeledContentStyle { 34 | 35 | static var details: Self { 36 | return DetailsLabeledContentStyle() 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Reconnect/Model/FileManageable.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | protocol FileManageable { 22 | 23 | @MainActor 24 | var canOpenSelection: Bool { get } 25 | 26 | @MainActor 27 | func openSelection() 28 | 29 | @MainActor 30 | var canCreateNewFolder: Bool { get } 31 | 32 | @MainActor 33 | func createNewFolder() 34 | 35 | @MainActor 36 | var canDelete: Bool { get } 37 | 38 | @MainActor 39 | func delete() 40 | 41 | @MainActor 42 | var canDownload: Bool { get } 43 | 44 | @MainActor 45 | func download() 46 | 47 | } 48 | -------------------------------------------------------------------------------- /ReconnectMenu/ReconnectMenuApp.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import ReconnectCore 22 | 23 | @main @MainActor 24 | struct ReconnectMenuApp: App { 25 | 26 | @NSApplicationDelegateAdaptor private var appDelegate: AppDelegate 27 | 28 | @State var applicationModel = ApplicationModel() 29 | 30 | var body: some Scene { 31 | 32 | MenuBarExtra { 33 | MainMenu() 34 | .environment(applicationModel) 35 | } label: { 36 | StatusIcon() 37 | .environment(applicationModel) 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Reconnect/Software Index/API/Version.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import Foundation 22 | 23 | struct Version: Codable, Identifiable, Hashable { 24 | 25 | var id: String { 26 | return version 27 | } 28 | 29 | let version: String 30 | let variants: [Collection] 31 | 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reconnect 2 | 3 | [![build](https://github.com/inseven/PsiMac/actions/workflows/build.yaml/badge.svg)](https://github.com/inseven/PsiMac/actions/workflows/build.yaml) 4 | 5 | Psion connectivity for macOS. 6 | 7 | 8 | 9 | ## License 10 | 11 | Reconnect is licensed under the GNU General Public License (GPL) version 2 (see [LICENSE](LICENSE)). It depends on the following separately licensed third-party libraries and components: 12 | 13 | - [Diligence](https://github.com/inseven/diligence), MIT License 14 | - [Interact](https://github.com/inseven/interact), MIT License 15 | - [Licensable](https://github.com/inseven/licensable), MIT License 16 | - [Lua](https://www.lua.org), MIT License 17 | - [OpoLua](https://github.com/inseven/opolua), MIT License 18 | - [plptools](https://github.com/rrthomas/plptools), GPL 2.0 License 19 | - [PsionSoftwareIndexSwift](https://github.com/inseven/PsionSoftwareIndexSwift), MIT License 20 | - [Sparkle](https://github.com/sparkle-project/Sparkle), Sparkle License 21 | - [Swift Algorithms](https://github.com/apple/swift-algorithms), Apache 2.0 License 22 | - [Swift Argument Parser](https://github.com/apple/swift-argument-parser), Apache 2.0 License 23 | - [Swift Numerics](https://github.com/apple/swift-numerics), Apache 2.0 License 24 | 25 | Reconnect includes graphics (icons and animations) from the original Psion PsiWin and PsiMac software. These remain copyright Psion PLC. 26 | -------------------------------------------------------------------------------- /Reconnect/Software Index/API/Collection.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import Foundation 22 | 23 | struct Collection: Codable, Identifiable, Hashable { 24 | 25 | var id: String { 26 | return identifier 27 | } 28 | 29 | let identifier: String 30 | let items: [Release] 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Reconnect/Extensions/SisInstallError.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | import ReconnectCore 22 | import OpoLua 23 | 24 | extension Sis.InstallError: @retroactive LocalizedError { 25 | 26 | public var errorDescription: String? { 27 | switch self { 28 | case .userCancelled: 29 | return "User cancelled" 30 | case .epocError(let code, _): 31 | return LocalizedEpoc32ErrorCode(code) 32 | case .internalError(let message): 33 | return message 34 | case .isStub: 35 | return "Invalid SIS file" 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /scripts/environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Reconnect -- Psion connectivity for macOS 4 | # 5 | # Copyright (C) 2024-2025 Jason Morley 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 | 21 | ROOT_DIRECTORY="$( cd "$( dirname "$( dirname "${BASH_SOURCE[0]}" )" )" &> /dev/null && pwd )" 22 | SCRIPTS_DIRECTORY="$ROOT_DIRECTORY/scripts" 23 | 24 | export LOCAL_TOOLS_PATH="$ROOT_DIRECTORY/.local" 25 | 26 | export BIN_DIRECTORY="$ROOT_DIRECTORY/.local/bin" 27 | export PATH=$BIN_DIRECTORY:$PATH 28 | 29 | source "$LOCAL_TOOLS_PATH/python/bin/activate" 30 | 31 | export PIPENV_VENV_IN_PROJECT=1 32 | export PIPENV_IGNORE_VIRTUALENVS=1 33 | 34 | export PATH=$PATH:"$SCRIPTS_DIRECTORY/changes" 35 | export PATH=$PATH:"$SCRIPTS_DIRECTORY/build-tools" 36 | export PATH=$PATH:"$ROOT_DIRECTORY/dependencies/diligence/scripts" 37 | -------------------------------------------------------------------------------- /ReconnectMenu/Views/StatusIcon.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import ReconnectCore 22 | 23 | struct StatusIcon: View { 24 | 25 | @Environment(ApplicationModel.self) var applicationModel 26 | 27 | var body: some View { 28 | if !applicationModel.isDaemonConnected { 29 | Image("StatusUnknown") 30 | .interpolation(.none) 31 | 32 | } else if applicationModel.isDeviceConnected { 33 | Image("StatusConnected") 34 | .interpolation(.none) 35 | } else { 36 | Image("StatusDisconnected") 37 | .interpolation(.none) 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Reconnect/Windows/TransfersWindow.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import Interact 22 | 23 | struct TransfersWindow: Scene { 24 | 25 | static func reveal() { 26 | DispatchQueue.main.async { 27 | NSWorkspace.shared.open(.transfers) 28 | } 29 | } 30 | 31 | static let id = "transfers" 32 | 33 | @Environment(TransfersModel.self) private var transfersModel 34 | 35 | var body: some Scene { 36 | Window("Transfers", id: Self.id) { 37 | TransfersView(transfersModel: transfersModel) 38 | } 39 | .windowResizability(.contentSize) 40 | .handlesExternalEvents(matching: [.transfers]) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /ReconnectCore/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "ReconnectCore", 8 | platforms: [ 9 | .macOS(.v14), 10 | ], 11 | products: [ 12 | // Products define the executables and libraries a package produces, making them visible to other packages. 13 | .library( 14 | name: "ReconnectCore", 15 | targets: ["ReconnectCore"]), 16 | ], 17 | dependencies: [ 18 | .package(path: "../dependencies/diligence"), 19 | .package(path: "../dependencies/opolua"), 20 | .package(path: "../dependencies/plptools"), 21 | ], 22 | targets: [ 23 | // Targets are the basic building blocks of a package, defining a module or a test suite. 24 | // Targets can depend on other targets in this package and products from dependencies. 25 | .target( 26 | name: "ReconnectCore", 27 | dependencies: [ 28 | .product(name: "Diligence", package: "diligence"), 29 | .product(name: "OpoLua", package: "opolua"), 30 | .product(name: "ncp", package: "plptools"), 31 | ], 32 | swiftSettings: [ 33 | .interoperabilityMode(.Cxx) 34 | ] 35 | ), 36 | .testTarget( 37 | name: "ReconnectCoreTests", 38 | dependencies: ["ReconnectCore"]), 39 | ] 40 | ) 41 | -------------------------------------------------------------------------------- /Reconnect/Commands/RefreshCommands.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | public struct RefreshCommands: Commands { 22 | 23 | @FocusedObject private var refreshableProxy: RefreshableProxy? 24 | 25 | init() { 26 | } 27 | 28 | public var body: some Commands { 29 | 30 | CommandGroup(before: .newItem) { 31 | 32 | Button { 33 | refreshableProxy?.refresh() 34 | } label: { 35 | Label("Refresh", systemImage: "arrow.clockwise") 36 | } 37 | .keyboardShortcut("R") 38 | .disabled(!(refreshableProxy?.canRefresh ?? false)) 39 | 40 | Divider() 41 | } 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Reconnect/Model/ParentNavigableProxy.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | class ParentNavigableProxy: ObservableObject, ParentNavigable { 22 | 23 | var canNavigateToParent: Bool { 24 | return _canNavigateToParent() 25 | } 26 | 27 | let _canNavigateToParent: @MainActor () -> Bool 28 | let _navigateToParent: @MainActor () -> Void 29 | 30 | init(_ parentNavigable: ParentNavigable) { 31 | _canNavigateToParent = { 32 | return parentNavigable.canNavigateToParent 33 | } 34 | _navigateToParent = { 35 | parentNavigable.navigateToParent() 36 | } 37 | } 38 | 39 | func navigateToParent() { 40 | _navigateToParent() 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Reconnect/Toolbars/DeviceToolbar.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct DeviceToolbar: CustomizableToolbarContent { 22 | 23 | @FocusedObject private var deviceProxy: DeviceModelProxy? 24 | 25 | init() { 26 | } 27 | 28 | var body: some CustomizableToolbarContent { 29 | 30 | ToolbarItem(id: "screenshot") { 31 | Button { 32 | deviceProxy?.deviceModel.captureScreenshot() 33 | } label: { 34 | Label("Screenshot", systemImage: "camera.viewfinder") 35 | } 36 | .help("Capture a screenshot of your Psion") 37 | .disabled(deviceProxy?.deviceModel.isCapturingScreenshot ?? true) 38 | } 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /docs/docs/development/architecture/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Architecture 3 | --- 4 | 5 | # Background 6 | 7 | Psions communicate using the [Psion Link Protocol](https://thoukydides.github.io/riscos-psifs/plp.html) (PLP). Reconnect builds on top of [plptools](https://github.com/plptools/plptools), a tried-and-tested open source implementation of PLP for posix-friendly systems. 8 | 9 | plptools comprises the following components: 10 | 11 | - Service 12 | - `ncpd`---daemon that manages the connection to a single Psion on a serial port and exposes the different services to the local machine over TCP 13 | - Clients 14 | - `plpftp`---CLI offering an FTP-like interface for browsing and transferring files 15 | - `plpfuse`---exposes the connected Psion as local file system using [FUSE](https://en.wikipedia.org/wiki/Filesystem_in_Userspace) 16 | - `plpprint`---print-server allowing you to print-to-files from your Psion 17 | - `sisinstall`---CLI for installing SIS installer files on EPOC32 devices 18 | 19 | ```mermaid 20 | graph TD 21 | plpftp --> ncpd 22 | plpfuse --> ncpd 23 | plpprint --> ncpd 24 | sisinstall --> ncpd 25 | ``` 26 | 27 | Unfortunately, the evolving security architecture of macOS is increasingly hostile to services like FUSE (you need to [disable System Integrity Protection](https://developer.apple.com/documentation/security/disabling-and-enabling-system-integrity-protection) to install the required kernel extensions), and having to manually run services like `ncpd` to manage your Psion connection is far from user-friendly, necessitating a different approach on macOS. 28 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Extensions/NSRunningApplication.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import AppKit 20 | 21 | extension NSRunningApplication { 22 | 23 | public static func terminateRunningApplications(bundleIdentifier: String, waitForCompletion: Bool) { 24 | let runningApps = runningApplications(withBundleIdentifier: bundleIdentifier) 25 | for app in runningApps { 26 | app.terminate() 27 | } 28 | guard waitForCompletion else { 29 | return 30 | } 31 | for app in runningApps { 32 | while !app.isTerminated { 33 | RunLoop.current.run(until: Date().addingTimeInterval(0.1)) 34 | } 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /scripts/update-release-notes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Reconnect -- Psion connectivity for macOS 4 | # 5 | # Copyright (C) 2024-2025 Jason Morley 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 | 21 | set -e 22 | set -o pipefail 23 | set -x 24 | set -u 25 | 26 | SCRIPTS_DIRECTORY="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 27 | 28 | ROOT_DIRECTORY="$SCRIPTS_DIRECTORY/.." 29 | RELEASE_NOTES_TEMPLATE_PATH="$SCRIPTS_DIRECTORY/release-notes.md" 30 | RELEASE_NOTES_DIRECTORY="$ROOT_DIRECTORY/docs/releases" 31 | RELEASE_NOTES_PATH="$RELEASE_NOTES_DIRECTORY/index.md" 32 | 33 | source "$SCRIPTS_DIRECTORY/environment.sh" 34 | 35 | cd "$ROOT_DIRECTORY" 36 | 37 | if [ -d "$RELEASE_NOTES_DIRECTORY" ]; then 38 | rm -r "$RELEASE_NOTES_DIRECTORY" 39 | fi 40 | mkdir -p "$RELEASE_NOTES_DIRECTORY" 41 | changes notes --all --template "$RELEASE_NOTES_TEMPLATE_PATH" > "$RELEASE_NOTES_PATH" 42 | -------------------------------------------------------------------------------- /Reconnect/Windows/NSInstallerWindow.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | class NSInstallerWindow: NSWindow { 22 | 23 | var url: URL? 24 | 25 | convenience init(applicationModel: ApplicationModel, url: URL) { 26 | let windowProxy = WindowProxy() 27 | let rootView = InstallerView(applicationModel: applicationModel, url: url) 28 | .environment(\.window, windowProxy) 29 | self.init(contentViewController: NSHostingController(rootView: rootView)) 30 | self.url = url 31 | windowProxy.nsWindow = self 32 | title = url.displayName 33 | styleMask.remove([.resizable, .borderless, .fullSizeContentView]) 34 | setContentSize(CGSize(width: 800, height: 600)) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Reconnect/Software Index/Model/PsionSoftwareIndexError.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import Foundation 22 | 23 | enum PsionSoftwareIndexError: Error { 24 | case unknownDownloadFailure 25 | } 26 | 27 | extension PsionSoftwareIndexError: LocalizedError { 28 | 29 | public var errorDescription: String? { 30 | switch self { 31 | case .unknownDownloadFailure: 32 | return "Unknown download failure" 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_16x16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "icon_16x16@2x@2x.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "icon_32x32.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "icon_32x32@2x@2x.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "icon_128x128.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "icon_128x128@2x@2x.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "icon_256x256.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "icon_256x256@2x@2x.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "icon_512x512.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "icon_512x512@2x@2x.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_16x16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "icon_16x16@2x@2x.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "icon_32x32.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "icon_32x32@2x@2x.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "icon_128x128.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "icon_128x128@2x@2x.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "icon_256x256.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "icon_256x256@2x@2x.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "icon_512x512.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "icon_512x512@2x@2x.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Reconnect/Views/DeviceDriveGroup.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct DeviceDriveGroup: View { 22 | 23 | @State var isShowingDrives = true 24 | 25 | let deviceModel: DeviceModel 26 | 27 | var body: some View { 28 | DisclosureGroup(isExpanded: $isShowingDrives) { 29 | ForEach(deviceModel.drives) { driveInfo in 30 | Label(driveInfo.displayName, image: driveInfo.image) 31 | .tag(BrowserSection.drive(deviceModel.id, driveInfo)) 32 | .id(BrowserSection.drive(deviceModel.id, driveInfo)) 33 | } 34 | } label: { 35 | Label("My Psion", image: "Psion16") 36 | } 37 | .tag(BrowserSection.device(deviceModel.id)) 38 | .id(BrowserSection.device(deviceModel.id)) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Reconnect/Views/PixelImage.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import Interact 22 | 23 | struct PixelImage: View { 24 | 25 | enum Source { 26 | case name(String) 27 | case resource(ImageResource) 28 | } 29 | 30 | let source: Source 31 | 32 | init(_ name: String) { 33 | self.source = .name(name) 34 | } 35 | 36 | init(_ resource: ImageResource) { 37 | self.source = .resource(resource) 38 | } 39 | 40 | var image: Image { 41 | switch source { 42 | case .name(let string): 43 | Image(string) 44 | case .resource(let resource): 45 | Image(resource) 46 | } 47 | } 48 | 49 | var body: some View { 50 | image 51 | .interpolation(.none) 52 | .resizable() 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Reconnect/Views/EditableText.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | // This exists as a fairly gnarly workaround to turn SwiftUI's continuous table view text field editing back into 22 | // something that looks vaguely modal. It's possible we'd also get this for free by writing using an NSTextField 23 | // directly, but that's for another day; until then, debouncing edits will have to be sufficient. 24 | struct EditableText: View { 25 | 26 | @StateObject var model: EditableTextModel 27 | 28 | init(initialValue: String, completion: @escaping (String) -> Void) { 29 | _model = StateObject(wrappedValue: EditableTextModel(initialValue: initialValue, completion: completion)) 30 | } 31 | 32 | var body: some View { 33 | TextField("", text: $model.text) 34 | .runs(model) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Reconnect/Toolbars/RefreshToolbar.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct RefreshToolbar: CustomizableToolbarContent { 22 | 23 | @FocusedObject private var refreshableProxy: RefreshableProxy? 24 | 25 | var body: some CustomizableToolbarContent { 26 | 27 | if refreshableProxy?.isRefreshing ?? false { 28 | ToolbarItem(id: "refresh-indicator", placement: .navigation) { 29 | ProgressView() 30 | .controlSize(.small) 31 | } 32 | } 33 | 34 | ToolbarItem(id: "refresh") { 35 | Button { 36 | refreshableProxy?.refresh() 37 | } label: { 38 | Label("Refresh", systemImage: "arrow.clockwise") 39 | } 40 | .disabled(!(refreshableProxy?.canRefresh ?? false)) 41 | } 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Utilities/Graphics.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | import ImageIO 21 | import UniformTypeIdentifiers 22 | 23 | public func CGImageWrite(destinationURL: URL, images: [CGImage], type: UTType) throws { 24 | guard let destination = CGImageDestinationCreateWithURL(destinationURL as CFURL, 25 | type.identifier as CFString, 26 | images.count, 27 | nil) else { 28 | throw ReconnectError.imageSaveError 29 | } 30 | for image in images { 31 | CGImageDestinationAddImage(destination, image, nil) 32 | } 33 | guard CGImageDestinationFinalize(destination) else { 34 | throw ReconnectError.imageSaveError 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ReconnectUITests/ReconnectUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import XCTest 20 | 21 | final class ReconnectUITestsLaunchTests: XCTestCase { 22 | 23 | override class var runsForEachTargetApplicationUIConfiguration: Bool { 24 | true 25 | } 26 | 27 | override func setUpWithError() throws { 28 | continueAfterFailure = false 29 | } 30 | 31 | func testLaunch() throws { 32 | let app = XCUIApplication() 33 | app.launch() 34 | 35 | // Insert steps here to perform after app launch but before taking a screenshot, 36 | // such as logging into a test account or navigating somewhere in the app 37 | 38 | let attachment = XCTAttachment(screenshot: app.screenshot()) 39 | attachment.name = "Launch Screen" 40 | attachment.lifetime = .keepAlways 41 | add(attachment) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Reconnect/Model/RefreshableProxy.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | class RefreshableProxy: ObservableObject, Refreshable { 22 | 23 | var canRefresh: Bool { 24 | return _canRefresh() 25 | } 26 | 27 | var isRefreshing: Bool { 28 | return _isRefreshing() 29 | } 30 | 31 | private let _canRefresh: @MainActor () -> Bool 32 | private let _isRefreshing: @MainActor () -> Bool 33 | private let _refresh: @MainActor () -> Void 34 | 35 | init(_ refreshable: Refreshable) { 36 | self._canRefresh = { 37 | refreshable.canRefresh 38 | } 39 | self._isRefreshing = { 40 | refreshable.isRefreshing 41 | } 42 | self._refresh = { 43 | refreshable.refresh() 44 | } 45 | } 46 | 47 | func refresh() { 48 | _refresh() 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Reconnect/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | @MainActor 22 | final class AppDelegate: NSObject, NSApplicationDelegate { 23 | 24 | let applicationModel = ApplicationModel() 25 | 26 | func application(_ application: NSApplication, open urls: [URL]) { 27 | for url in urls { 28 | if url.isFileURL { 29 | applicationModel.showInstallerWindow(url: url) 30 | } else if url == .update { 31 | applicationModel.updaterController.updater.checkForUpdates() 32 | } else { 33 | print("Ignoring URL '\(url.absoluteString)'...") 34 | } 35 | } 36 | } 37 | 38 | func applicationWillTerminate(_ notification: Notification) { 39 | if !applicationModel.openAtLogin { 40 | applicationModel.terminateRunningMenuApplications() 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/XPC/DaemonClientInterface.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | @objc 22 | public protocol DaemonClientInterface { 23 | 24 | func keepalive(count: Int) 25 | func setSerialDevices(_ devices: [SerialDevice]) 26 | func setIsConnected(_ isConnected: Bool) 27 | 28 | } 29 | 30 | extension NSXPCInterface { 31 | 32 | static var daemonClientInterface: NSXPCInterface { 33 | let interface = NSXPCInterface(with: DaemonClientInterface.self) 34 | let allowedClasses = [NSArray.self, SerialDevice.self, SerialDeviceConfiguration.self] as NSSet as Set 35 | interface.setClasses(allowedClasses, 36 | for: #selector(DaemonClientInterface.setSerialDevices(_:)), 37 | argumentIndex: 0, 38 | ofReply: false) 39 | return interface 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Reconnect/Software Index/API/Program.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import Foundation 22 | 23 | struct Program: Codable, Identifiable, Hashable { 24 | 25 | let id: String 26 | let name: String 27 | let icon: SoftwareIndexImage? 28 | let versions: [Version] 29 | let subtitle: String? 30 | let description: String? 31 | let tags: [String] 32 | var screenshots: [SoftwareIndexImage]? 33 | 34 | var iconURL: URL? { 35 | guard let icon else { 36 | return nil 37 | } 38 | return URL.softwareIndexAPIV1.appendingPathComponent(icon.path) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Reconnect/Software Index/Views/IconView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import SwiftUI 22 | 23 | struct IconView: View { 24 | 25 | let url: URL? 26 | 27 | var body: some View { 28 | if let url { 29 | AsyncImage(url: url) { image in 30 | image 31 | .interpolation(.none) 32 | } placeholder: { 33 | Image(.unknownAppIcon) 34 | .interpolation(.none) 35 | } 36 | } else { 37 | SwiftUI.Image(.unknownAppIcon) 38 | .interpolation(.none) 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Reconnect/Software Index/Styles/UnadornedCircularProgressViewStyle.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import SwiftUI 22 | 23 | struct UnadornedCircularProgressViewStyle: ProgressViewStyle { 24 | 25 | func makeBody(configuration: Configuration) -> some View { 26 | ProgressView(value: configuration.fractionCompleted) 27 | .progressViewStyle(.circular) 28 | } 29 | 30 | } 31 | 32 | extension ProgressViewStyle where Self == UnadornedCircularProgressViewStyle { 33 | 34 | static var unadornedCircular: UnadornedCircularProgressViewStyle { 35 | return UnadornedCircularProgressViewStyle() 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Reconnect/Modifiers/OptionalNavigationSubtitle.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import SwiftUI 22 | 23 | struct OptionalNavigationSubtitle: ViewModifier { 24 | 25 | let subtitle: String? 26 | 27 | func body(content: Content) -> some View { 28 | if let subtitle { 29 | content 30 | .navigationSubtitle(subtitle) 31 | } else { 32 | content 33 | } 34 | } 35 | 36 | } 37 | 38 | extension View { 39 | 40 | func optionalNavigationSubtitle(_ subtitle: String?) -> some View { 41 | return self.modifier(OptionalNavigationSubtitle(subtitle: subtitle)) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Reconnect/Views/DeviceView.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct DeviceView: View { 22 | 23 | @Environment(DeviceModel.self) private var deviceModel 24 | 25 | var body: some View { 26 | ScrollView { 27 | VStack(alignment: .leading, spacing: 32) { 28 | 29 | MachineDetailsGroup(machineInfo: deviceModel.machineInfo) 30 | 31 | DetailsGroup("Installed Programs") { 32 | ProgramManagerView(deviceModel: deviceModel) 33 | .frame(height: 300) 34 | .border(.quaternary) 35 | } 36 | 37 | } 38 | .padding() 39 | .frame(maxWidth: .infinity, maxHeight: .infinity) 40 | .scenePadding() 41 | } 42 | .background(.textBackgroundColor) 43 | .navigationTitle("My Psion") 44 | .showsDeviceProgress() 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Extensions/UInt32.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | public extension UInt32 { 22 | 23 | static let none: Self = 0x00000000 24 | 25 | // UID1 26 | static let directFileStore: Self = 0x10000037 27 | static let permanentFileStoreLayout: Self = 0x10000050 // Database 28 | static let multiBitmapRomImage: Self = 0x10000041 29 | static let dynamicLibraryUid: Self = 0x10000079 // Native app 30 | 31 | // UID2 32 | static let appDllDoc: Self = 0x1000006D 33 | static let mbm: Self = 0x10000042 34 | 35 | // UID3 36 | static let word: Self = 0x1000007F 37 | static let sheet: Self = 0x10000088 38 | static let record: Self = 0x1000007E 39 | static let opl: Self = 0x10000085 40 | static let data: Self = 0x10000086 41 | static let agenda: Self = 0x10000084 42 | static let sketch: Self = 0x1000007D 43 | static let jotter: Self = 0x10000CEA 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Reconnect/Views/ThumbnailView.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | import QuickLookThumbnailing 21 | 22 | import Interact 23 | 24 | @MainActor 25 | struct ThumbnailView: View { 26 | 27 | let url: URL 28 | let size: CGSize 29 | 30 | @State var image: NSImage? = nil 31 | 32 | var body: some View { 33 | Image(nsImage: image ?? NSWorkspace.shared.icon(forFile: url.path)) 34 | .resizable() 35 | .task { 36 | let thumbnail = try? await QLThumbnailGenerator.shared.thumbnailRepresentation(fileAt: url, 37 | size: size, 38 | scale: 2.0, 39 | iconMode: true) 40 | image = thumbnail?.nsImage 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Reconnect/Views/CheckForUpdatesView.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import Sparkle 22 | 23 | // This is the view for the Check for Updates menu item 24 | // Note this intermediate view is necessary for the disabled state on the menu item to work properly before Monterey. 25 | // See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more info 26 | struct CheckForUpdatesView: View { 27 | @ObservedObject private var checkForUpdatesViewModel: CheckForUpdatesViewModel 28 | private let updater: SPUUpdater 29 | 30 | init(updater: SPUUpdater) { 31 | self.updater = updater 32 | 33 | // Create our view model for our CheckForUpdatesView 34 | self.checkForUpdatesViewModel = CheckForUpdatesViewModel(updater: updater) 35 | } 36 | 37 | var body: some View { 38 | Button("Check for Updates…", action: updater.checkForUpdates) 39 | .disabled(!checkForUpdatesViewModel.canCheckForUpdates) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Extensions/DriveInfo.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | extension FileServer.DriveInfo { 22 | 23 | public var displayName: String { 24 | if let name { 25 | return "\(name) (\(drive):)" 26 | } else { 27 | return "\(drive):" 28 | } 29 | } 30 | 31 | public var image: String { 32 | switch mediaType { 33 | case .notPresent: 34 | return "Drive16" 35 | case .unknown: 36 | return "Drive16" 37 | case .floppy: 38 | return "Drive16" 39 | case .disk: 40 | return "Disk16" 41 | case .compactDisc: 42 | return "Drive16" 43 | case .ram: 44 | return "Drive16" 45 | case .flashDisk: 46 | return "Drive16" 47 | case .rom: 48 | return "Drive16" 49 | case .remote: 50 | return "Drive16" 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Extensions/MachineType.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | extension RemoteCommandServicesClient.MachineType { 22 | 23 | public var localizedNameKey: LocalizedStringKey { 24 | switch self { 25 | case .unknown: 26 | return "Unknown" 27 | case .pc: 28 | return "PC" 29 | case .mc: 30 | return "MC" 31 | case .hc: 32 | return "HC" 33 | case .series3: 34 | return "Series 3" 35 | case .series3acmx: 36 | return "Series 3a / Series 3c / Series 3mx" 37 | case .workabout: 38 | return "Workabout" 39 | case .siena: 40 | return "Siena" 41 | case .series3c: 42 | return "Series 3c" 43 | case .series5: 44 | return "Series 5 / Series 5mx / Series 7 / netBook" 45 | case .winC: 46 | return "WinC" 47 | } 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Reconnect/Commands/DeviceCommands.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | public struct DeviceCommands: Commands { 22 | 23 | @Environment(ApplicationModel.self) private var applicationModel: ApplicationModel 24 | 25 | @FocusedObject private var deviceProxy: DeviceModelProxy? 26 | 27 | public var body: some Commands { 28 | 29 | CommandMenu("Device") { 30 | 31 | Button { 32 | deviceProxy?.deviceModel.captureScreenshot() 33 | } label: { 34 | Label("Capture Screenshot", systemImage: "camera.viewfinder") 35 | } 36 | .keyboardShortcut("S", modifiers: [.command, .shift]) 37 | .disabled(deviceProxy?.deviceModel.isCapturingScreenshot ?? true) 38 | 39 | Divider() 40 | 41 | Button("Install Reconnect Tools...") { 42 | applicationModel.installGuestTools() 43 | } 44 | .disabled(deviceProxy == nil) 45 | 46 | } 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /docs/docs/getting-started/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | layout: documentation 4 | priority: 100 5 | --- 6 | 7 | # Hardware 8 | 9 | In order to connect your Psion to your Mac, you'll need a couple of bits of hardware: an RS232 adapter, and a suitable link cable. 10 | 11 | ## RS232 Adapter 12 | 13 | RS232 adapters using an [FTDI](https://ftdichip.com) chipset are the most reliable on macOS and are recommended. [This one](https://www.amazon.com/dp/B09WJS26WX) from Amazon US seems to work well. 14 | 15 | > [!IMPORTANT] 16 | > The RS232 adapter you select _must_ support the DSR/DTR hardware control signals to work with Reconnect and plptools. [Prolific](https://www.prolific.com.tw/) adapters can be made to work, but the default drivers that ship by default with macOS don't handle these correctly so you'll need to install the [official drivers](https://apps.apple.com/gb/app/pl2303-serial/id1624835354). 17 | 18 | [Alex Brown](https://oldbytes.space/@thelastpsion) has done some pretty rigorous testing of different adapters which you can find on his [Hackaday blog](https://hackaday.io/project/161291-the-last-psion/log/222358-usb-rs232-shenanigans-updated-2025-05-28). 19 | 20 | ## Link Cable 21 | 22 | The link cable you need depends on the type of Psion you have. Unfortunately these can be a little expensive now, but [Psionex](https://psionex.co.uk) still have stock at relatively competitive prices. 23 | 24 | ### Series 3 and 3a 25 | 26 | - [3Link for Psion Series 3, and 3a](https://psionex.co.uk/en/product/pda/series3/adapters-cables-modems/c-3l-complete.html) 27 | 28 | ### Series 3c, 3mx, 5, 5mx, 7, and netBook 29 | 30 | - [Link cable Psion Series 3c, 3mx, 5, 5mx, 7, and netBook](https://psionex.co.uk/en/product/pda/netbook/adapters-cables-modems/s5mx-rs232-link.html) 31 | 32 | > [!NOTE] 33 | > These are commonly known as 'Honda' cables as Honda manufactured the original connectors. 34 | -------------------------------------------------------------------------------- /Reconnect/Views/FileTypePopover.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import ReconnectCore 22 | 23 | struct FileTypePopover: View { 24 | 25 | @State var isPresented: Bool = false 26 | 27 | let file: FileServer.DirectoryEntry 28 | 29 | var body: some View { 30 | Button { 31 | isPresented = true 32 | } label: { 33 | Text(file.fileType.name) 34 | } 35 | .popover(isPresented: $isPresented) { 36 | Grid { 37 | GridRow { 38 | Text("UID1") 39 | Text(String(format: "0x%08X", file.uid1)) 40 | } 41 | GridRow { 42 | Text("UID2") 43 | Text(String(format: "0x%08X", file.uid2)) 44 | } 45 | GridRow { 46 | Text("UID3") 47 | Text(String(format: "0x%08X", file.uid3)) 48 | } 49 | } 50 | .padding() 51 | .textSelection(.enabled) 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/XPC/DaemonInterface.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | @objc 22 | public protocol DaemonInterface { 23 | 24 | func connect(reply: @escaping (DaemonInfo) -> Void) 25 | func configureSerialDevice(path: String, configuration: SerialDeviceConfiguration) 26 | 27 | } 28 | 29 | extension NSXPCInterface { 30 | 31 | static var daemonInterface: NSXPCInterface { 32 | let interface = NSXPCInterface(with: DaemonInterface.self) 33 | 34 | interface.setClasses([DaemonInfo.self] as NSSet as Set, 35 | for: #selector(DaemonInterface.connect(reply:)), 36 | argumentIndex: 0, 37 | ofReply: true) 38 | interface.setClasses([SerialDeviceConfiguration.self] as NSSet as Set, 39 | for: #selector(DaemonInterface.configureSerialDevice(path:configuration:)), 40 | argumentIndex: 1, 41 | ofReply: false) 42 | 43 | return interface 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Reconnect/Views/Settings/SettingsButton.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import Diligence 22 | import Interact 23 | 24 | struct SettingsButton: View { 25 | 26 | @Environment(ApplicationModel.self) private var applicationModel 27 | 28 | @Environment(\.openSettings) private var openSettings 29 | 30 | let label: Label 31 | let section: SettingsView.SettingsSection 32 | 33 | init(section: SettingsView.SettingsSection = .general, @ViewBuilder label: () -> Label) { 34 | self.label = label() 35 | self.section = section 36 | } 37 | 38 | var body: some View { 39 | Button { 40 | applicationModel.activeSettingsSection = section 41 | openSettings() 42 | } label: { 43 | label 44 | } 45 | } 46 | 47 | } 48 | 49 | extension SettingsButton where Label == Text { 50 | 51 | init(_ title: LocalizedStringKey = "Open Settings...", section: SettingsView.SettingsSection = .general) { 52 | self.init(section: section) { 53 | Text(title) 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Reconnect/Model/EditableTextModel.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Combine 20 | import SwiftUI 21 | 22 | import Interact 23 | 24 | class EditableTextModel: ObservableObject, Runnable { 25 | 26 | @Published var text: String = "" 27 | 28 | private let initialValue: String 29 | private let completion: (String) -> Void 30 | 31 | private var cancellables: Set = [] 32 | 33 | init(initialValue: String, completion: @escaping (String) -> Void) { 34 | self.initialValue = initialValue 35 | self.completion = completion 36 | self.text = initialValue 37 | } 38 | 39 | func start() { 40 | $text 41 | .debounce(for: 1.0, scheduler: DispatchQueue.main) 42 | .sink { text in 43 | dispatchPrecondition(condition: .onQueue(.main)) 44 | guard text != self.initialValue else { 45 | return 46 | } 47 | self.completion(text) 48 | } 49 | .store(in: &cancellables) 50 | } 51 | 52 | func stop() { 53 | cancellables.removeAll() 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /ReconnectPreviews/InstallerPreviewView.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import OpoLua 22 | import ReconnectCore 23 | 24 | struct InstallerPreviewView: View { 25 | 26 | let file: Sis.File 27 | 28 | var body: some View { 29 | VStack(alignment: .center) { 30 | Text(file.localizedDisplayName) 31 | .multilineTextAlignment(.center) 32 | .font(.title) 33 | Text("\(file.version)") 34 | .font(.title2) 35 | .frame(maxWidth: .infinity) 36 | Text(String(format: "0x%08X", file.uid)) 37 | .foregroundStyle(.secondary) 38 | 39 | Divider() 40 | 41 | Text("Languages") 42 | .foregroundStyle(.secondary) 43 | ForEach(file.languages, id: \.self) { language in 44 | Text(NSLocalizedString(language, comment: language)) 45 | } 46 | 47 | Spacer() 48 | } 49 | .padding() 50 | .background(Color(nsColor: .textBackgroundColor)) 51 | .frame(maxWidth: .infinity, maxHeight: .infinity) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ReconnectMenu/Views/MainMenu.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import Interact 22 | 23 | import ReconnectCore 24 | 25 | struct MainMenu: View { 26 | 27 | @Environment(\.openURL) private var openURL 28 | 29 | @Environment(ApplicationModel.self) var applicationModel 30 | 31 | @ObservedObject var application = Application.shared 32 | 33 | var body: some View { 34 | @Bindable var applicationModel = applicationModel 35 | Button { 36 | applicationModel.openReconnect(.browser) 37 | } label: { 38 | Text("My Psion...") 39 | } 40 | Divider() 41 | Button { 42 | applicationModel.openReconnect(.about) 43 | } label: { 44 | Text("About...") 45 | } 46 | Button("Settings...") { 47 | applicationModel.openReconnect(.settings) 48 | } 49 | Divider() 50 | Button("Check for Updates...") { 51 | applicationModel.openReconnect(.update) 52 | } 53 | Divider() 54 | Button("Quit") { 55 | applicationModel.quit() 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/PLP/PsionClient.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | public class PsionClient { 22 | 23 | let fileServer = FileServer() 24 | let remoteCommandServices = RemoteCommandServicesClient() 25 | 26 | public init() { 27 | 28 | } 29 | 30 | public func runProgram(path: String) async throws { 31 | let attributes = try await fileServer.getExtendedAttributes(path: path) 32 | if attributes.uid1 == .dynamicLibraryUid { 33 | try remoteCommandServices.execProgram(program: path) 34 | } else { 35 | try remoteCommandServices.execProgram(program: "Z:\\System\\Apps\\OPL\\OPL.app", args: "A" + path) 36 | } 37 | } 38 | 39 | public func runProgram(path: String) throws { 40 | let attributes = try fileServer.getExtendedAttributesSync(path: path) 41 | if attributes.uid1 == .dynamicLibraryUid { 42 | try remoteCommandServices.execProgram(program: path) 43 | } else { 44 | try remoteCommandServices.execProgram(program: "Z:\\System\\Apps\\OPL\\OPL.app", args: "A" + path) 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Extensions/Locale.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | public struct Localization { 22 | 23 | public let language: String 24 | public let text: String 25 | 26 | } 27 | 28 | extension Locale { 29 | 30 | public static func selectLanguage(_ candidates: any Collection) throws -> String { 31 | let candidates = Set(candidates) 32 | for language in preferredLanguages { 33 | let language = language.replacingOccurrences(of: "-", with: "_") // Convert to the OpoLua language identifier format. 34 | if candidates.contains(language) { 35 | return language 36 | } 37 | } 38 | guard let language = candidates.sorted().first else { 39 | throw ReconnectError.invalidLocalization 40 | } 41 | return language 42 | } 43 | 44 | public static func localize(_ localizations: [String: String]) throws -> Localization { 45 | let language = try selectLanguage(localizations.keys) 46 | return Localization(language: language, text: localizations[language]!) 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /scripts/build-website.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Reconnect -- Psion connectivity for macOS 4 | # 5 | # Copyright (C) 2024-2025 Jason Morley 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 | 21 | set -e 22 | set -o pipefail 23 | set -x 24 | set -u 25 | 26 | SCRIPTS_DIRECTORY="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 27 | 28 | ROOT_DIRECTORY="$SCRIPTS_DIRECTORY/.." 29 | WEBSITE_DIRECTORY="$ROOT_DIRECTORY/docs" 30 | 31 | source "$SCRIPTS_DIRECTORY/environment.sh" 32 | 33 | cd "$ROOT_DIRECTORY" 34 | 35 | # Update the release notes. 36 | "$SCRIPTS_DIRECTORY/update-release-notes.sh" 37 | 38 | # Install the Jekyll dependencies. 39 | export GEM_HOME="$ROOT_DIRECTORY/.local/ruby" 40 | mkdir -p "$GEM_HOME" 41 | export PATH="$GEM_HOME/bin":$PATH 42 | gem install bundler 43 | cd "$WEBSITE_DIRECTORY" 44 | bundle install 45 | 46 | # Get the latest release URL. 47 | if ! DOWNLOAD_URL=$(build-tools latest-github-release inseven reconnect "Reconnect-*.zip"); then 48 | echo >&2 failed 49 | exit 1 50 | fi 51 | # Belt-and-braces check that we managed to get the download URL. 52 | if [[ -z "$DOWNLOAD_URL" ]]; then 53 | echo "Failed to get release download URL." 54 | exit 1 55 | fi 56 | export DOWNLOAD_URL 57 | 58 | # Build the website. 59 | cd "$WEBSITE_DIRECTORY" 60 | bundle exec jekyll build 61 | -------------------------------------------------------------------------------- /Reconnect/Modifiers/ShowsDeviceProgress.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct ShowsDeviceProgress: ViewModifier { 22 | 23 | @Environment(DeviceModel.self) private var deviceModel: DeviceModel 24 | 25 | func body(content: Content) -> some View { 26 | content 27 | .safeAreaInset(edge: .bottom) { 28 | if deviceModel.isCapturingScreenshot { 29 | VStack(spacing: 0) { 30 | Divider() 31 | HStack { 32 | ProgressView() 33 | .progressViewStyle(.circular) 34 | .controlSize(.small) 35 | Text("Capturing screenshot...") 36 | } 37 | .font(.callout) 38 | .foregroundStyle(.secondary) 39 | .padding() 40 | } 41 | .background(Color(nsColor: .textBackgroundColor)) 42 | } 43 | } 44 | } 45 | 46 | } 47 | 48 | extension View { 49 | 50 | func showsDeviceProgress() -> some View { 51 | return modifier(ShowsDeviceProgress()) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Extensions/PsiLuaEnv.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import CoreGraphics 20 | import Foundation 21 | import UniformTypeIdentifiers 22 | 23 | import OpoLua 24 | 25 | extension PsiLuaEnv { 26 | 27 | // TODO: Rename source and destination 28 | public func convertMultiBitmap(at url: URL, to: URL, type: UTType = .tiff) throws { 29 | let bitmaps = PsiLuaEnv().getMbmBitmaps(path: url.path) ?? [] 30 | let images = bitmaps.map { bitmap in 31 | return CGImage.from(bitmap: bitmap) 32 | } 33 | try CGImageWrite(destinationURL: to, images: images, type: type) 34 | } 35 | 36 | public func loadSisFile(url: URL) throws -> Sis.File { 37 | let info = getFileInfo(path: url.path) 38 | guard case let .sis(sis) = info else { 39 | throw ReconnectError.invalidSisFile 40 | } 41 | return sis 42 | } 43 | 44 | public func loadSisFile(data: Data) throws -> Sis.File { 45 | let info = getFileInfo(data: data) 46 | guard case let .sis(sis) = info else { 47 | throw ReconnectError.invalidSisFile 48 | } 49 | return sis 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /scripts/install-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Reconnect -- Psion connectivity for macOS 4 | # 5 | # Copyright (C) 2024-2025 Jason Morley 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 | 21 | set -e 22 | set -o pipefail 23 | set -x 24 | set -u 25 | 26 | ROOT_DIRECTORY="$( cd "$( dirname "$( dirname "${BASH_SOURCE[0]}" )" )" &> /dev/null && pwd )" 27 | SCRIPTS_DIRECTORY="$ROOT_DIRECTORY/scripts" 28 | 29 | LOCAL_TOOLS_PATH="$ROOT_DIRECTORY/.local" 30 | CHANGES_DIRECTORY="$SCRIPTS_DIRECTORY/changes" 31 | BUILD_TOOLS_DIRECTORY="$SCRIPTS_DIRECTORY/build-tools" 32 | 33 | # Install tools defined in `.tool-versions`. 34 | cd "$ROOT_DIRECTORY" 35 | mise install 36 | 37 | # Clean up and recreate the local tools directory. 38 | if [ -d "$LOCAL_TOOLS_PATH" ] ; then 39 | rm -r "$LOCAL_TOOLS_PATH" 40 | fi 41 | mkdir -p "$LOCAL_TOOLS_PATH" 42 | 43 | # Set up a Python venv to bootstrap our python dependency on `pipenv`. 44 | python -m venv "$LOCAL_TOOLS_PATH/python" 45 | 46 | # Source `environment.sh` to ensure the remainder of our paths are set up correctly. 47 | source "$SCRIPTS_DIRECTORY/environment.sh" 48 | 49 | # Install the Python dependencies. 50 | pip install --upgrade pip pipenv wheel certifi 51 | PIPENV_PIPFILE="$CHANGES_DIRECTORY/Pipfile" pipenv install 52 | PIPENV_PIPFILE="$BUILD_TOOLS_DIRECTORY/Pipfile" pipenv install 53 | -------------------------------------------------------------------------------- /Reconnect/Views/DetailsGroup.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct DetailsGroup: View { 22 | 23 | let content: Content 24 | let label: Label 25 | 26 | init(@ViewBuilder content: () -> Content, @ViewBuilder label: () -> Label) { 27 | self.content = content() 28 | self.label = label() 29 | } 30 | 31 | var body: some View { 32 | VStack(alignment: .leading) { 33 | label 34 | .font(.title) 35 | VStack { 36 | content 37 | } 38 | .padding() 39 | .frame(maxWidth: .infinity) 40 | .background(.quinary) 41 | .border(.quaternary) 42 | } 43 | } 44 | 45 | } 46 | 47 | extension DetailsGroup where Label == Text { 48 | 49 | init(_ title: LocalizedStringKey, @ViewBuilder content: () -> Content) { 50 | self.init(content: content) { 51 | Text(title) 52 | } 53 | } 54 | 55 | @_disfavoredOverload 56 | init(_ title: String, @ViewBuilder content: () -> Content) { 57 | self.init(content: content) { 58 | Text(title) 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /ReconnectPreviews/PreviewViewController.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Cocoa 20 | import SwiftUI 21 | import Quartz 22 | 23 | import OpoLua 24 | import ReconnectCore 25 | 26 | class PreviewViewController: NSViewController, QLPreviewingController { 27 | 28 | override var nibName: NSNib.Name? { 29 | return NSNib.Name("PreviewViewController") 30 | } 31 | 32 | override func loadView() { 33 | super.loadView() 34 | } 35 | 36 | func preparePreviewOfFile(at url: URL) async throws { 37 | let file = try PsiLuaEnv().loadSisFile(url: url) 38 | let previewView = NSHostingView(rootView: InstallerPreviewView(file: file)) 39 | self.view.addSubview(previewView) 40 | self.view.translatesAutoresizingMaskIntoConstraints = false 41 | previewView.translatesAutoresizingMaskIntoConstraints = false 42 | NSLayoutConstraint.activate([ 43 | previewView.leadingAnchor.constraint(equalTo: view.leadingAnchor), 44 | previewView.trailingAnchor.constraint(equalTo: view.trailingAnchor), 45 | previewView.topAnchor.constraint(equalTo: view.topAnchor), 46 | previewView.bottomAnchor.constraint(equalTo: view.bottomAnchor), 47 | ]) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Reconnect/Toolbars/FileToolbar.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | struct FileToolbar: CustomizableToolbarContent { 22 | 23 | @FocusedObject private var fileManageableProxy: FileManageableProxy? 24 | 25 | var body: some CustomizableToolbarContent { 26 | 27 | ToolbarItem(id: "new-folder") { 28 | Button { 29 | fileManageableProxy?.createNewFolder() 30 | } label: { 31 | Label("New Folder", systemImage: "folder.badge.plus") 32 | } 33 | .disabled(!(fileManageableProxy?.canCreateNewFolder ?? false)) 34 | } 35 | 36 | ToolbarItem(id: "download") { 37 | Button { 38 | fileManageableProxy?.download() 39 | } label: { 40 | Label("Download", systemImage: "display.and.arrow.down") 41 | } 42 | .disabled(!(fileManageableProxy?.canDownload ?? false)) 43 | } 44 | 45 | ToolbarItem(id: "delete") { 46 | Button { 47 | fileManageableProxy?.delete() 48 | } label: { 49 | Label("Delete", systemImage: "trash") 50 | } 51 | .disabled(!(fileManageableProxy?.canDelete ?? false)) 52 | } 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Reconnect/Software Index/Views/ItemView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import SwiftUI 22 | 23 | struct ItemView: View { 24 | 25 | let imageURL: URL? 26 | let title: String 27 | let subtitle: String? 28 | 29 | init(imageURL: URL?, title: String, subtitle: String? = nil) { 30 | self.imageURL = imageURL 31 | self.title = title 32 | self.subtitle = subtitle 33 | } 34 | 35 | var body: some View { 36 | HStack { 37 | IconView(url: imageURL) 38 | .padding() 39 | VStack(alignment: .leading) { 40 | Spacer() 41 | Text(title) 42 | if let subtitle { 43 | Text(subtitle) 44 | .foregroundStyle(.secondary) 45 | .font(.caption) 46 | } 47 | Spacer() 48 | Divider() 49 | } 50 | } 51 | .contentShape(Rectangle()) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Reconnect/Views/Installer/ReplaceQueryInstallerPage.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import OpoLua 22 | 23 | @MainActor 24 | struct ReplaceQueryInstallerPage: View { 25 | 26 | let query: InstallerModel.ReplaceQuery 27 | 28 | init(query: InstallerModel.ReplaceQuery) { 29 | self.query = query 30 | } 31 | 32 | var body: some View { 33 | VStack(spacing: 0) { 34 | InstallerPage("Replace '\(query.replacing.name)'?") { 35 | HStack(spacing: 0) { 36 | Text("'\(query.replacing.name) - \(query.replacing.version)' is already installed on drive \(query.replacing.drive). Do you want to replace it with version \(query.sis.version)?") 37 | .multilineTextAlignment(.leading) 38 | Spacer() 39 | } 40 | .padding() 41 | .frame(maxWidth: InstallerView.LayoutMetrics.maximumContentWidth) 42 | } actions: { 43 | Button("No", role: .destructive) { 44 | query.resume(false) 45 | } 46 | Button("Yes") { 47 | query.resume(true) 48 | } 49 | .keyboardShortcut(.defaultAction) 50 | } 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/XPC/DaemonInfo.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | public class DaemonInfo: NSObject, NSSecureCoding { 22 | 23 | enum CodingKeys: String { 24 | case version 25 | case buildNumber 26 | } 27 | 28 | public static var supportsSecureCoding = true 29 | 30 | public override var description: String { 31 | debugDescription 32 | } 33 | 34 | public override var debugDescription: String { 35 | return "{version = \(version ?? "?"), buildNumber = \(buildNumber ?? "?")'}" 36 | } 37 | 38 | public let version: String? 39 | public let buildNumber: String? 40 | 41 | public init(version: String?, buildNumber: String?) { 42 | self.version = version 43 | self.buildNumber = buildNumber 44 | } 45 | 46 | public required init?(coder: NSCoder) { 47 | self.version = coder.decodeObject(of: NSString.self, forKey: CodingKeys.version.rawValue) as? String 48 | self.buildNumber = coder.decodeObject(of: NSString.self, forKey: CodingKeys.buildNumber.rawValue) as? String 49 | } 50 | 51 | public func encode(with coder: NSCoder) { 52 | coder.encode(version, forKey: CodingKeys.version.rawValue) 53 | coder.encode(buildNumber, forKey: CodingKeys.buildNumber.rawValue) 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Reconnect/Views/Settings/SerialDeviceEnableToggle.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import Interact 22 | 23 | import ReconnectCore 24 | 25 | struct SerialDeviceEnableToggle: View { 26 | 27 | @Environment(ApplicationModel.self) private var applicationModel 28 | 29 | @State var error: Error? = nil 30 | 31 | let device: SerialDevice 32 | 33 | init(device: SerialDevice) { 34 | self.device = device 35 | } 36 | 37 | func binding() -> Binding { 38 | return Binding { 39 | return device.configuration.isEnabled 40 | } set: { enabled in 41 | let configuration = device 42 | .configuration 43 | .setting(isEnabled: enabled) 44 | applicationModel.daemonClient.configureSerialDevice(path: device.path, 45 | configuration: configuration) { result in 46 | guard case .failure(let error) = result else { 47 | return 48 | } 49 | DispatchQueue.main.async { 50 | self.error = error 51 | } 52 | } 53 | } 54 | } 55 | 56 | var body: some View { 57 | Toggle(isOn: binding()) { 58 | EmptyView() 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Reconnect/Views/Installer/TextQueryInstallerPage.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | @MainActor 22 | struct TextQueryInstallerPage: View { 23 | 24 | let query: InstallerModel.TextQuery 25 | 26 | var body: some View { 27 | InstallerPage(query.sis.localizedDisplayNameAndVersion) { 28 | ScrollView { 29 | Text(query.text) 30 | .frame(maxWidth: .infinity, alignment: .leading) 31 | .padding() 32 | } 33 | .background(Color(nsColor: .textBackgroundColor)) 34 | } actions: { 35 | switch query.type { 36 | case .continue: 37 | Button("Continue") { 38 | query.resume(true) 39 | } 40 | .keyboardShortcut(.defaultAction) 41 | case .skip, .abort: 42 | Button("No") { 43 | query.resume(false) 44 | } 45 | Button("Yes") { 46 | query.resume(true) 47 | } 48 | .keyboardShortcut(.defaultAction) 49 | case .exit: 50 | Button("Exit") { 51 | query.resume(true) 52 | } 53 | .keyboardShortcut(.defaultAction) 54 | } 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Reconnect/Software Index/API/Release.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024-2025 Jason Morley 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import Foundation 22 | 23 | public struct Release: Codable, Hashable { 24 | 25 | public var uniqueId: String { 26 | return id + referenceString 27 | } 28 | 29 | public let id: String 30 | public let uid: String? 31 | public let kind: Kind 32 | public let name: String 33 | let icon: SoftwareIndexImage? 34 | let filename: String 35 | let size: Int 36 | let sha256: String 37 | let reference: [ReferenceItem] 38 | public let tags: [String] 39 | 40 | var iconURL: URL? { 41 | guard let icon else { 42 | return nil 43 | } 44 | return URL.softwareIndexAPIV1.appendingPathComponent(icon.path) 45 | } 46 | 47 | var referenceString: String { 48 | return reference 49 | .map { $0.name } 50 | .joined(separator: " - ") 51 | } 52 | 53 | var downloadURL: URL { 54 | return URL(string: "https://software.psion.community/files/\(sha256)")! 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /ReconnectTests/WindowsPathTests.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import XCTest 20 | @testable import Reconnect 21 | 22 | final class WindowsPathTests: XCTestCase { 23 | 24 | func testDeletingLastWindowsPathComponent() { 25 | XCTAssertEqual("C:\\Foo\\Bar".deletingLastWindowsPathComponent, "C:\\Foo") 26 | XCTAssertEqual("C:\\Foo\\".deletingLastWindowsPathComponent, "C:") 27 | } 28 | 29 | func testAppendingWindowsPathComponent() { 30 | XCTAssertEqual("C:\\Foo".appendingWindowsPathComponent("Bar"), "C:\\Foo\\Bar") 31 | XCTAssertEqual("C:\\Foo".appendingWindowsPathComponent("Bar", isDirectory: true), "C:\\Foo\\Bar\\") 32 | } 33 | 34 | func testEnsuringTrailingWindowsPathSeparator() { 35 | 36 | XCTAssertEqual("C:\\Foo".ensuringTrailingWindowsPathSeparator(), "C:\\Foo\\") 37 | XCTAssertEqual("C:\\Foo".ensuringTrailingWindowsPathSeparator(isPresent: true), "C:\\Foo\\") 38 | 39 | XCTAssertEqual("C:\\Foo".ensuringTrailingWindowsPathSeparator(isPresent: false), "C:\\Foo") 40 | 41 | XCTAssertEqual("C:\\Foo\\".ensuringTrailingWindowsPathSeparator(), "C:\\Foo\\") 42 | XCTAssertEqual("C:\\Foo\\".ensuringTrailingWindowsPathSeparator(isPresent: true), "C:\\Foo\\") 43 | 44 | XCTAssertEqual("C:\\Foo\\".ensuringTrailingWindowsPathSeparator(isPresent: false), "C:\\Foo") 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /ReconnectCore/Sources/ReconnectCore/Model/ReconnectError.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import Foundation 20 | 21 | import ncp 22 | 23 | public enum ReconnectError: Error { 24 | case unknown 25 | case invalidFilePath 26 | case unknownFileSize 27 | case imageSaveError 28 | case invalidLocalization 29 | case invalidSisFile 30 | case invalidFileReference 31 | case missingTools 32 | case invalidDaemonProxy 33 | } 34 | 35 | extension ReconnectError: LocalizedError { 36 | 37 | public var errorDescription: String? { 38 | switch self { 39 | case .unknown: 40 | return "Unknown error." 41 | case .invalidFilePath: 42 | return "Invalid file path." 43 | case .unknownFileSize: 44 | return "Unknown file size." 45 | case .imageSaveError: 46 | return "Failed to save image." 47 | case .invalidLocalization: 48 | return "Badly formatted localized text." 49 | case .invalidSisFile: 50 | return "Invalid SIS file." 51 | case .invalidFileReference: 52 | return "Invalid file reference." 53 | case .missingTools: 54 | return "The Reconnect Tools are not installed on your Psion." 55 | case .invalidDaemonProxy: 56 | return "Failed to get daemon proxy." 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Reconnect/Commands/NavigationCommands.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | public struct NavigationCommands: Commands { 22 | 23 | @Environment(NavigationHistory.self) private var navigationHistory 24 | 25 | @FocusedObject private var parentNavigableProxy: ParentNavigableProxy? 26 | 27 | public var body: some Commands { 28 | 29 | CommandMenu("Go") { 30 | 31 | Button { 32 | navigationHistory.back() 33 | } label: { 34 | Label("Back", systemImage: "chevron.backward") 35 | } 36 | .keyboardShortcut("[", modifiers: [.command]) 37 | .disabled(!navigationHistory.canGoBack()) 38 | 39 | Button { 40 | navigationHistory.forward() 41 | } label: { 42 | Label("Forward", systemImage: "chevron.forward") 43 | } 44 | .keyboardShortcut("]", modifiers: [.command]) 45 | .disabled(!navigationHistory.canGoForward()) 46 | 47 | Button { 48 | parentNavigableProxy?.navigateToParent() 49 | } label: { 50 | Label("Enclosing Folder", systemImage: "arrow.turn.left.up") 51 | } 52 | .keyboardShortcut(.upArrow, modifiers: [.command]) 53 | .disabled(!(parentNavigableProxy?.canNavigateToParent ?? false)) 54 | } 55 | 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Reconnect/Model/BrowserSection.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import SwiftUI 20 | 21 | import ReconnectCore 22 | 23 | enum BrowserSection: Hashable { 24 | case connecting 25 | case drive(UUID, FileServer.DriveInfo) 26 | case directory(UUID, FileServer.DriveInfo, String) 27 | case device(UUID) 28 | case softwareIndex 29 | case program(Program) 30 | } 31 | 32 | extension BrowserSection { 33 | 34 | var title: String { 35 | switch self { 36 | case .connecting: 37 | return "Connecting..." 38 | case .drive(_, let driveInfo): 39 | return driveInfo.displayName 40 | case .directory(_, _, let path): 41 | return path.lastWindowsPathComponent 42 | case .device: 43 | return "My Psion" 44 | case .softwareIndex: 45 | return "Software Index" 46 | case .program(let program): 47 | return program.name 48 | } 49 | } 50 | 51 | var image: String { 52 | switch self { 53 | case .connecting: 54 | return "Disconnected16" 55 | case .drive(_, let driveInfo): 56 | return driveInfo.image 57 | case .directory: 58 | return "Folder16" 59 | case .device: 60 | return "Psion16" 61 | case .softwareIndex: 62 | return "Install16" 63 | case .program: 64 | return "FileUnknown16" 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /ReconnectUITests/ReconnectUITests.swift: -------------------------------------------------------------------------------- 1 | // Reconnect -- Psion connectivity for macOS 2 | // 3 | // Copyright (C) 2024-2025 Jason Morley 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | import XCTest 20 | 21 | final class ReconnectUITests: XCTestCase { 22 | 23 | override func setUpWithError() throws { 24 | // Put setup code here. This method is called before the invocation of each test method in the class. 25 | 26 | // In UI tests it is usually best to stop immediately when a failure occurs. 27 | continueAfterFailure = false 28 | 29 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 30 | } 31 | 32 | override func tearDownWithError() throws { 33 | // Put teardown code here. This method is called after the invocation of each test method in the class. 34 | } 35 | 36 | func testExample() throws { 37 | // UI tests must launch the application that they test. 38 | let app = XCUIApplication() 39 | app.launch() 40 | 41 | // Use XCTAssert and related functions to verify your tests produce the correct results. 42 | } 43 | 44 | func testLaunchPerformance() throws { 45 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 46 | // This measures how long it takes to launch your application. 47 | measure(metrics: [XCTApplicationLaunchMetric()]) { 48 | XCUIApplication().launch() 49 | } 50 | } 51 | } 52 | } 53 | --------------------------------------------------------------------------------