├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md └── workflows │ └── ci.yml ├── .gitignore ├── Frameworks └── .gitkeep ├── Hamster.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ ├── WorkspaceSettings.xcsettings │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcschemes │ ├── Hamster.xcscheme │ └── HamsterKeyboard.xcscheme ├── Hamster ├── AppDelegete.swift ├── Assets │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-iTunes 1.png │ │ │ ├── Icon-App-iTunes.png │ │ │ ├── Icon_114×114@3x.png │ │ │ ├── Icon_120×120@2x 1.png │ │ │ ├── Icon_120×120@2x.png │ │ │ ├── Icon_120×120@3x 1.png │ │ │ ├── Icon_120×120@3x.png │ │ │ ├── Icon_128×128@2x.png │ │ │ ├── Icon_136×136@2x.png │ │ │ ├── Icon_152×152@2x.png │ │ │ ├── Icon_167x167@2x.png │ │ │ ├── Icon_180×180@3x 1.png │ │ │ ├── Icon_180×180@3x.png │ │ │ ├── Icon_192×192@3x.png │ │ │ ├── Icon_40×40@2x 1.png │ │ │ ├── Icon_40×40@2x.png │ │ │ ├── Icon_58×58@2x 1.png │ │ │ ├── Icon_58×58@2x.png │ │ │ ├── Icon_60×60@3x 1.png │ │ │ ├── Icon_60×60@3x.png │ │ │ ├── Icon_76×76@2x.png │ │ │ ├── Icon_80×80@2x 1.png │ │ │ ├── Icon_80×80@2x.png │ │ │ ├── Icon_87×87@3x 1.png │ │ │ └── Icon_87×87@3x.png │ │ ├── Contents.json │ │ └── Hamster.imageset │ │ │ ├── Contents.json │ │ │ ├── Hamster.png │ │ │ └── HamsterWhite.png │ ├── LaunchScreen.storyboard │ └── Preview Content │ │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Hamster.entitlements ├── HamsterDebug.entitlements ├── Info.plist ├── SceneDelegate.swift └── Shortcuts │ ├── Actions │ ├── RimeDeployBackgroundIntent.swift │ ├── RimeDeployIntent.swift │ └── RimeSyncIntent.swift │ └── IntentProvider.swift ├── HamsterKeyboard ├── HamsterKeyboard.entitlements ├── HamsterKeyboardInputViewController.swift ├── Info.plist ├── en.lproj │ └── InfoPlist.strings ├── zh-HK.lproj │ └── InfoPlist.strings ├── zh-Hans.lproj │ └── InfoPlist.strings ├── zh-Hant.lproj │ └── InfoPlist.strings └── zh.lproj │ └── InfoPlist.strings ├── InputSchemaBuild.sh ├── LICENSE.txt ├── Makefile ├── Packages ├── HamsterFileServer │ ├── .gitignore │ ├── Front │ │ ├── .eslintrc.json │ │ ├── .prettierignore │ │ ├── .prettierrc.json │ │ ├── index.html │ │ ├── jsconfig.json │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── postcss.config.cjs │ │ ├── public │ │ │ ├── .gitkeep │ │ │ ├── img │ │ │ │ └── logo.svg │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ └── themes │ │ │ │ └── dark.css │ │ ├── src │ │ │ ├── App.vue │ │ │ ├── api │ │ │ │ ├── commands.js │ │ │ │ ├── files.js │ │ │ │ ├── index.js │ │ │ │ ├── pub.js │ │ │ │ ├── search.js │ │ │ │ ├── settings.js │ │ │ │ ├── share.js │ │ │ │ ├── tus.js │ │ │ │ ├── users.js │ │ │ │ └── utils.js │ │ │ ├── assets │ │ │ │ └── fonts │ │ │ │ │ └── roboto │ │ │ │ │ ├── bold-cyrillic-ext.woff2 │ │ │ │ │ ├── bold-cyrillic.woff2 │ │ │ │ │ ├── bold-greek-ext.woff2 │ │ │ │ │ ├── bold-greek.woff2 │ │ │ │ │ ├── bold-latin-ext.woff2 │ │ │ │ │ ├── bold-latin.woff2 │ │ │ │ │ ├── bold-vietnamese.woff2 │ │ │ │ │ ├── medium-cyrillic-ext.woff2 │ │ │ │ │ ├── medium-cyrillic.woff2 │ │ │ │ │ ├── medium-greek-ext.woff2 │ │ │ │ │ ├── medium-greek.woff2 │ │ │ │ │ ├── medium-latin-ext.woff2 │ │ │ │ │ ├── medium-latin.woff2 │ │ │ │ │ ├── medium-vietnamese.woff2 │ │ │ │ │ ├── normal-cyrillic-ext.woff2 │ │ │ │ │ ├── normal-cyrillic.woff2 │ │ │ │ │ ├── normal-greek-ext.woff2 │ │ │ │ │ ├── normal-greek.woff2 │ │ │ │ │ ├── normal-latin-ext.woff2 │ │ │ │ │ ├── normal-latin.woff2 │ │ │ │ │ └── normal-vietnamese.woff2 │ │ │ ├── components │ │ │ │ ├── Breadcrumbs.vue │ │ │ │ ├── Search.vue │ │ │ │ ├── Shell.vue │ │ │ │ ├── Sidebar.vue │ │ │ │ ├── files │ │ │ │ │ ├── ExtendedImage.vue │ │ │ │ │ └── ListingItem.vue │ │ │ │ ├── header │ │ │ │ │ ├── Action.vue │ │ │ │ │ └── HeaderBar.vue │ │ │ │ ├── prompts │ │ │ │ │ ├── Copy.vue │ │ │ │ │ ├── Delete.vue │ │ │ │ │ ├── Download.vue │ │ │ │ │ ├── FileList.vue │ │ │ │ │ ├── Help.vue │ │ │ │ │ ├── Info.vue │ │ │ │ │ ├── Move.vue │ │ │ │ │ ├── NewDir.vue │ │ │ │ │ ├── NewFile.vue │ │ │ │ │ ├── Prompts.vue │ │ │ │ │ ├── Rename.vue │ │ │ │ │ ├── Replace.vue │ │ │ │ │ ├── ReplaceRename.vue │ │ │ │ │ ├── Share.vue │ │ │ │ │ ├── ShareDelete.vue │ │ │ │ │ ├── Upload.vue │ │ │ │ │ └── UploadFiles.vue │ │ │ │ └── settings │ │ │ │ │ ├── Commands.vue │ │ │ │ │ ├── Languages.vue │ │ │ │ │ ├── Permissions.vue │ │ │ │ │ ├── Rules.vue │ │ │ │ │ ├── Themes.vue │ │ │ │ │ └── UserForm.vue │ │ │ ├── css │ │ │ │ ├── _buttons.css │ │ │ │ ├── _inputs.css │ │ │ │ ├── _share.css │ │ │ │ ├── _shell.css │ │ │ │ ├── _variables.css │ │ │ │ ├── base.css │ │ │ │ ├── dashboard.css │ │ │ │ ├── fonts.css │ │ │ │ ├── header.css │ │ │ │ ├── listing-icons.css │ │ │ │ ├── listing.css │ │ │ │ ├── login.css │ │ │ │ ├── mobile.css │ │ │ │ ├── styles.css │ │ │ │ └── upload-files.css │ │ │ ├── i18n │ │ │ │ ├── ar.json │ │ │ │ ├── de.json │ │ │ │ ├── el.json │ │ │ │ ├── en.json │ │ │ │ ├── es.json │ │ │ │ ├── fr.json │ │ │ │ ├── he.json │ │ │ │ ├── hu.json │ │ │ │ ├── index.js │ │ │ │ ├── is.json │ │ │ │ ├── it.json │ │ │ │ ├── ja.json │ │ │ │ ├── ko.json │ │ │ │ ├── nl-be.json │ │ │ │ ├── pl.json │ │ │ │ ├── pt-br.json │ │ │ │ ├── pt.json │ │ │ │ ├── ro.json │ │ │ │ ├── ru.json │ │ │ │ ├── sk.json │ │ │ │ ├── sv-se.json │ │ │ │ ├── tr.json │ │ │ │ ├── ua.json │ │ │ │ ├── zh-cn.json │ │ │ │ └── zh-tw.json │ │ │ ├── main.js │ │ │ ├── router │ │ │ │ └── index.js │ │ │ ├── store │ │ │ │ ├── getters.js │ │ │ │ ├── index.js │ │ │ │ ├── modules │ │ │ │ │ └── upload.js │ │ │ │ └── mutations.js │ │ │ ├── utils │ │ │ │ ├── auth.js │ │ │ │ ├── buttons.js │ │ │ │ ├── constants.js │ │ │ │ ├── cookie.js │ │ │ │ ├── css.js │ │ │ │ ├── index.js │ │ │ │ ├── upload.js │ │ │ │ ├── url.js │ │ │ │ └── vue.js │ │ │ └── views │ │ │ │ ├── Errors.vue │ │ │ │ ├── Files.vue │ │ │ │ ├── Layout.vue │ │ │ │ ├── Login.vue │ │ │ │ ├── Settings.vue │ │ │ │ ├── Share.vue │ │ │ │ ├── files │ │ │ │ ├── Editor.vue │ │ │ │ ├── Listing.vue │ │ │ │ └── Preview.vue │ │ │ │ └── settings │ │ │ │ ├── Global.vue │ │ │ │ ├── Profile.vue │ │ │ │ ├── Shares.vue │ │ │ │ ├── User.vue │ │ │ │ └── Users.vue │ │ └── vite.config.js │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ ├── FileServer.swift │ │ └── Resources │ │ │ └── FileServer.bundle │ │ │ ├── assets │ │ │ ├── Editor-7123e63a.js │ │ │ ├── Editor-legacy-01841bf0.js │ │ │ ├── bold-cyrillic-c72dc79d.woff2 │ │ │ ├── bold-cyrillic-ext-d2a7562e.woff2 │ │ │ ├── bold-greek-20b1d460.woff2 │ │ │ ├── bold-latin-827bd1a8.woff2 │ │ │ ├── bold-latin-ext-2d83ee25.woff2 │ │ │ ├── bold-vietnamese-2096d809.woff2 │ │ │ ├── index-30449ddb.css │ │ │ ├── index-64f5c6a7.js │ │ │ ├── index-legacy-5b3f3e02.js │ │ │ ├── material-icons-8265f647.woff2 │ │ │ ├── material-icons-fd84f88b.woff │ │ │ ├── medium-cyrillic-ef372eb9.woff2 │ │ │ ├── medium-cyrillic-ext-2d290fe6.woff2 │ │ │ ├── medium-greek-1320e5dd.woff2 │ │ │ ├── medium-latin-01a44f86.woff2 │ │ │ ├── medium-latin-ext-b4dae108.woff2 │ │ │ ├── medium-vietnamese-20d9ffa9.woff2 │ │ │ ├── normal-cyrillic-ext-457c1e0d.woff2 │ │ │ ├── normal-cyrillic-fb0297aa.woff2 │ │ │ ├── normal-greek-adc0b6a1.woff2 │ │ │ ├── normal-latin-ext-55f25e8b.woff2 │ │ │ ├── normal-latin-f7bbc846.woff2 │ │ │ ├── normal-vietnamese-0af602fe.woff2 │ │ │ └── polyfills-legacy-d03a0163.js │ │ │ ├── img │ │ │ └── logo.svg │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ └── themes │ │ │ └── dark.css │ └── Tests │ │ └── HamsterFileServerTests │ │ └── HamsterFileServerTests.swift ├── HamsterKeyboardKit │ ├── .gitignore │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ ├── Configuration │ │ │ ├── Models │ │ │ │ ├── CustomizeKeyboardsConfiguration.swift │ │ │ │ ├── GeneralConfiguration.swift │ │ │ │ ├── HamsterConfiguration.swift │ │ │ │ ├── HamsterKeyboardColor.swift │ │ │ │ ├── HamsterPatchConfiguration.swift │ │ │ │ ├── HapticIntensity.swift │ │ │ │ ├── KeyboardColorSchema.swift │ │ │ │ ├── KeyboardConfiguration.swift │ │ │ │ ├── KeyboardSwipeConfiguration.swift │ │ │ │ ├── KeyboardToolbarConfiguration.swift │ │ │ │ └── RimeConfiguration.swift │ │ │ └── Repositories │ │ │ │ └── HamsterConfigurationRepositories.swift │ │ ├── Controller │ │ │ ├── KeyboardController.swift │ │ │ ├── KeyboardInputViewController+.swift │ │ │ └── KeyboardInputViewController.swift │ │ ├── Customize │ │ │ ├── Yaml │ │ │ │ └── Models.swift │ │ │ └── 自定义键盘.md │ │ ├── KeyboardKit │ │ │ ├── Actions │ │ │ │ ├── DeleteBackwardRange.swift │ │ │ │ ├── KeyboardAction+Actions.swift │ │ │ │ ├── KeyboardAction+Autocomplete.swift │ │ │ │ ├── KeyboardAction+Button.swift │ │ │ │ ├── KeyboardAction+InputCallout.swift │ │ │ │ ├── KeyboardAction.swift │ │ │ │ ├── KeyboardActionHandler.swift │ │ │ │ ├── KeyboardActionRows.swift │ │ │ │ ├── KeyboardActions.swift │ │ │ │ ├── ShortcutCommand.swift │ │ │ │ └── StandardKeyboardActionHandler.swift │ │ │ ├── Appearance │ │ │ │ ├── AutocompleteToolbarItemBackgroundStyle.swift │ │ │ │ ├── AutocompleteToolbarItemStyle.swift │ │ │ │ ├── AutocompleteToolbarSeparatorStyle.swift │ │ │ │ ├── AutocompleteToolbarStyle.swift │ │ │ │ ├── CandidateBarStyle.swift │ │ │ │ ├── KeyboardActionCalloutStyle.swift │ │ │ │ ├── KeyboardAppearance.swift │ │ │ │ ├── KeyboardBackgroundStyle.swift │ │ │ │ ├── KeyboardButtonBorderStyle.swift │ │ │ │ ├── KeyboardButtonShadowStyle.swift │ │ │ │ ├── KeyboardButtonStyle.swift │ │ │ │ ├── KeyboardCalloutStyle.swift │ │ │ │ ├── KeyboardFont.swift │ │ │ │ ├── KeyboardFontWeight.swift │ │ │ │ ├── KeyboardInputCalloutStyle.swift │ │ │ │ ├── NonStandardKeyboardStyle.swift │ │ │ │ ├── StandardKeyboardAppearance.swift │ │ │ │ └── keyboardFontType.swift │ │ │ ├── Autocomplete │ │ │ │ ├── AutocompleteContext.swift │ │ │ │ ├── AutocompleteProvider.swift │ │ │ │ ├── AutocompleteSpaceState.swift │ │ │ │ ├── AutocompleteSuggestion.swift │ │ │ │ ├── DisabledAutocompleteProvider.swift │ │ │ │ └── PrefersAutocompleteResolver.swift │ │ │ ├── Bundle │ │ │ │ └── Bundle+.swift │ │ │ ├── Callouts │ │ │ │ ├── ActionCalloutContext.swift │ │ │ │ ├── CalloutActionProvider.swift │ │ │ │ ├── InputCalloutContext.swift │ │ │ │ ├── KeyboardCalloutContext.swift │ │ │ │ └── Providers │ │ │ │ │ ├── BaseCalloutActionProvider.swift │ │ │ │ │ ├── DisabledCalloutActionProvider.swift │ │ │ │ │ ├── EnglishCalloutActionProvider.swift │ │ │ │ │ └── StandardCalloutActionProvider.swift │ │ │ ├── Casing │ │ │ │ ├── KeyboardCase+Button.swift │ │ │ │ └── KeyboardCase.swift │ │ │ ├── Color │ │ │ │ ├── KeyboardColor.swift │ │ │ │ └── KeyboardColorReader.swift │ │ │ ├── Device │ │ │ │ ├── DeviceType.swift │ │ │ │ ├── InterfaceOrientation.swift │ │ │ │ └── InterfaceOrientationResolver.swift │ │ │ ├── Emojis │ │ │ │ ├── Emoji.swift │ │ │ │ ├── EmojiAnalyzer.swift │ │ │ │ ├── EmojiCategory.swift │ │ │ │ ├── EmojiProvider.swift │ │ │ │ ├── FrequentEmojiProvider.swift │ │ │ │ └── MostRecentEmojiProvider.swift │ │ │ ├── Extensions │ │ │ │ ├── CGSize+.swift │ │ │ │ ├── Locale+.swift │ │ │ │ ├── String+.swift │ │ │ │ ├── UIEdgeInsets.swift │ │ │ │ └── UIReturnKeyType+.swift │ │ │ ├── Feedback │ │ │ │ ├── AudioFeedback.swift │ │ │ │ ├── AudioFeedbackConfiguration.swift │ │ │ │ ├── AudioFeedbackEngine.swift │ │ │ │ ├── HapticFeedback.swift │ │ │ │ ├── HapticFeedbackConfiguration.swift │ │ │ │ ├── HapticFeedbackEngine.swift │ │ │ │ ├── HapticIntensity+.swift │ │ │ │ ├── KeyboardFeedbackHandler.swift │ │ │ │ ├── KeyboardFeedbackSettings.swift │ │ │ │ ├── StandardAudioFeedbackEngine.swift │ │ │ │ ├── StandardHapticFeedbackEngine.swift │ │ │ │ └── StandardKeyboardFeedbackHandler.swift │ │ │ ├── Gestures │ │ │ │ ├── DragGestureHandler.swift │ │ │ │ ├── GestureButtonDefaults.swift │ │ │ │ ├── KeyboardGesture.swift │ │ │ │ ├── RepeatGestureTimer.swift │ │ │ │ ├── SpaceCursorDragGestureHandler.swift │ │ │ │ ├── SpaceDragSensitivity.swift │ │ │ │ └── SpaceLongPressBehavior.swift │ │ │ ├── Images │ │ │ │ └── KeyboardImageReader.swift │ │ │ ├── Input │ │ │ │ ├── InputSet.swift │ │ │ │ ├── InputSetItem.swift │ │ │ │ ├── InputSetProvider.swift │ │ │ │ ├── InputSetProviderBased.swift │ │ │ │ ├── InputSetProviderProxy.swift │ │ │ │ ├── InputSetRow.swift │ │ │ │ ├── InputSetRows.swift │ │ │ │ ├── Providers │ │ │ │ │ ├── ChineseInputSetProvider.swift │ │ │ │ │ └── EnglishInputSetProvider.swift │ │ │ │ └── StandardInputSetProvider.swift │ │ │ ├── Keyboard │ │ │ │ ├── KeyboardAutocapitalizationType.swift │ │ │ │ ├── KeyboardBehavior.swift │ │ │ │ ├── KeyboardContext+KeyboardType.swift │ │ │ │ ├── KeyboardContext.swift │ │ │ │ ├── KeyboardReturnKeyType.swift │ │ │ │ ├── KeyboardTextContext.swift │ │ │ │ ├── KeyboardType+Button.swift │ │ │ │ ├── KeyboardType.swift │ │ │ │ ├── NextKeyboardController.swift │ │ │ │ └── StandardKeyboardBehavior.swift │ │ │ ├── LICENSE │ │ │ ├── Layout │ │ │ │ ├── KeyboardLayout.swift │ │ │ │ ├── KeyboardLayoutConfiguration.swift │ │ │ │ ├── KeyboardLayoutItem.swift │ │ │ │ ├── KeyboardLayoutItemSize.swift │ │ │ │ ├── KeyboardLayoutItemWidth.swift │ │ │ │ ├── KeyboardLayoutProvider.swift │ │ │ │ ├── KeyboardLayoutProviderProxy.swift │ │ │ │ ├── KeyboardRowItem.swift │ │ │ │ ├── Providers │ │ │ │ │ ├── ChineseKeyboardLayoutProvider.swift │ │ │ │ │ ├── ChineseNineGridLayoutProvider.swift │ │ │ │ │ ├── CustomizeKeyboardProvider.swift │ │ │ │ │ ├── EnglishKeyboardLayoutProvider.swift │ │ │ │ │ ├── NumericNineGridKeyboardLayoutProvider.swift │ │ │ │ │ ├── SystemKeyboardLayoutProvider.swift │ │ │ │ │ ├── iPadChineseKeyboardLayoutProvider.swift │ │ │ │ │ ├── iPadKeyboardLayoutProvider.swift │ │ │ │ │ ├── iPhoneChineseKeyboardLayoutProvider.swift │ │ │ │ │ └── iPhoneKeyboardLayoutProvider.swift │ │ │ │ └── StandardKeyboardLayoutProvider.swift │ │ │ ├── Localization │ │ │ │ ├── KKL10n.swift │ │ │ │ └── KeyboardLocale.swift │ │ │ ├── Navigation │ │ │ │ └── KeyboardUrlOpener.swift │ │ │ ├── Previews │ │ │ │ ├── Appearance+Preview.swift │ │ │ │ ├── ButtonStyle+Preview.swift │ │ │ │ ├── Keyboard+Preview.swift │ │ │ │ ├── KeyboardActionHandler+Preview.swift │ │ │ │ └── UITextDocumentProxy+Preview.swift │ │ │ ├── Proxy │ │ │ │ └── UITextDocumentProxy+.swift │ │ │ ├── README.md │ │ │ ├── Routing │ │ │ │ └── TextInputProxy.swift │ │ │ ├── Settings │ │ │ │ └── KeyboardSettingsUrlProvider.swift │ │ │ ├── Symbol │ │ │ │ ├── FrequentSymbolProvider.swift │ │ │ │ ├── MostRecentSymbolProvider.swift │ │ │ │ ├── Symbol.swift │ │ │ │ ├── SymbolCategory.swift │ │ │ │ └── SymbolProvider.swift │ │ │ └── Text │ │ │ │ └── WordAnalyzer.swift │ │ ├── Preview Helpers │ │ │ ├── UIViewControllerPreview.swift │ │ │ └── UIViewPreview.swift │ │ ├── Resources │ │ │ ├── Colors.xcassets │ │ │ │ ├── Contents.json │ │ │ │ ├── standardButtonBackground.colorset │ │ │ │ │ └── Contents.json │ │ │ │ ├── standardButtonBackgroundForColorSchemeBug.colorset │ │ │ │ │ └── Contents.json │ │ │ │ ├── standardButtonBackgroundForDarkAppearance.colorset │ │ │ │ │ └── Contents.json │ │ │ │ ├── standardButtonForeground.colorset │ │ │ │ │ └── Contents.json │ │ │ │ ├── standardButtonForegroundForDarkAppearance.colorset │ │ │ │ │ └── Contents.json │ │ │ │ ├── standardButtonShadow.colorset │ │ │ │ │ └── Contents.json │ │ │ │ ├── standardDarkButtonBackground.colorset │ │ │ │ │ └── Contents.json │ │ │ │ ├── standardDarkButtonBackgroundForColorSchemeBug.colorset │ │ │ │ │ └── Contents.json │ │ │ │ ├── standardDarkButtonBackgroundForDarkAppearance.colorset │ │ │ │ │ └── Contents.json │ │ │ │ ├── standardKeyboardBackground.colorset │ │ │ │ │ └── Contents.json │ │ │ │ └── standardKeyboardBackgroundForDarkAppearance.colorset │ │ │ │ │ └── Contents.json │ │ │ ├── Images.xcassets │ │ │ │ ├── Contents.json │ │ │ │ ├── chineseState.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── cn.pdf │ │ │ │ │ └── cnDark.pdf │ │ │ │ ├── englishState.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── en.pdf │ │ │ │ │ └── enDark.pdf │ │ │ │ └── keyboardEmoji.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── keyboard-emoji-dark.pdf │ │ │ │ │ └── keyboard-emoji.pdf │ │ │ ├── en.lproj │ │ │ │ └── Localizable.strings │ │ │ ├── zh-Hans.lproj │ │ │ │ └── Localizable.strings │ │ │ └── zh-Hant.lproj │ │ │ │ └── Localizable.strings │ │ ├── RimeContext │ │ │ ├── CandidateSuggestion.swift │ │ │ └── RimeContext.swift │ │ └── View │ │ │ ├── CandidateBarView.swift │ │ │ ├── Compoents │ │ │ └── CandidateWords │ │ │ │ ├── AlignedCollectionViewFlowLayout.swift │ │ │ │ ├── CandidateWordCell.swift │ │ │ │ ├── CandidateWordsCollectionView.swift │ │ │ │ ├── CandidatesPagingView.swift │ │ │ │ └── SeparatorCollectionViewFlowLayout.swift │ │ │ ├── KeyboardButton │ │ │ ├── ButtonContentView │ │ │ │ ├── ImageContentView.swift │ │ │ │ ├── SpaceContentView.swift │ │ │ │ └── TextContentView.swift │ │ │ ├── InputCallout │ │ │ │ └── InputCalloutView.swift │ │ │ ├── KeyboardButton+Touch.swift │ │ │ ├── KeyboardButton.swift │ │ │ └── KeyboardButtonContent.swift │ │ │ ├── KeyboardRootView.swift │ │ │ ├── KeyboardToolbarView.swift │ │ │ ├── KeyboardTouchView.swift │ │ │ ├── ShapeView.swift │ │ │ └── StandarKeyboard │ │ │ ├── ChineseNineGridKeyboard.swift │ │ │ ├── ClassifySymbolic │ │ │ ├── BottomRowView.swift │ │ │ ├── ClassifySymbolCell.swift │ │ │ ├── ClassifyView.swift │ │ │ ├── SymbolsView.swift │ │ │ └── ViewModel │ │ │ │ └── ClassifySymbolicViewModel.swift │ │ │ ├── ClassifySymbolicKeyboard.swift │ │ │ ├── EmojisKeyboard.swift │ │ │ ├── NumericNineGridKeyboard.swift │ │ │ ├── StanderSystemKeyboard.swift │ │ │ └── SymbolList │ │ │ ├── SymbolCell.swift │ │ │ └── SymbolsVerticalView.swift │ └── Tests │ │ ├── Configuration │ │ ├── Repositories │ │ │ └── HamsterConfigurationRepositoriesTest.swift │ │ └── Sample │ │ │ ├── HamsterConfiguration.swift │ │ │ └── HamsterPatchConfiguration.swift │ │ ├── HamsterKeyboardTests │ │ ├── Customize │ │ │ └── CustomizeKeyboardTest.swift │ │ └── Extensions │ │ │ ├── CGFloat+.swift │ │ │ ├── String+.swift │ │ │ └── UILabel+.swift │ │ └── KeyboardKit │ │ └── Appearance │ │ └── StanderKeyboardAppearanceTest.swift ├── HamsterKit │ ├── .gitignore │ ├── Package.resolved │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ ├── Algo │ │ │ └── Trie.swift │ │ ├── Constants │ │ │ └── HamsterConstants.swift │ │ ├── Context │ │ │ └── KeyboardContext.swift │ │ ├── Extensions │ │ │ ├── DateFormatter+.swift │ │ │ ├── FileManager+.swift │ │ │ ├── String+.swift │ │ │ ├── UIDevice+.swift │ │ │ ├── URL+.swift │ │ │ └── UserDefaults+.swift │ │ ├── Model │ │ │ ├── CandidateWord.swift │ │ │ └── RimeSchema.swift │ │ ├── Persistent │ │ │ └── PersistentController.swift │ │ ├── Shoutcat │ │ │ └── ShortcutItemType.swift │ │ ├── T9 │ │ │ └── T9Constants.swift │ │ └── Utilities │ │ │ ├── AppInfo.swift │ │ │ ├── Logger.swift │ │ │ ├── MetadataProvider.swift │ │ │ ├── OSXKeyCode.swift │ │ │ └── keysymdef.swift │ └── Tests │ │ ├── Algo │ │ └── T9Test.swift │ │ └── Extensions │ │ └── StringTest.swift ├── HamsterUIKit │ ├── .gitignore │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ ├── Model │ │ │ ├── Conform.swift │ │ │ └── ErrorMessage.swift │ │ └── UIKIt │ │ │ ├── Extensions │ │ │ └── UIView+.swift │ │ │ ├── NibLessCompotents │ │ │ ├── NibLessCollectionView.swift │ │ │ ├── NibLessNavigationController.swift │ │ │ ├── NibLessTableViewCell.swift │ │ │ ├── NibLessView.swift │ │ │ └── NibLessViewController.swift │ │ │ ├── StepSlider │ │ │ └── StepSlider.swift │ │ │ ├── UIViewController+ErrorPresentation.swift │ │ │ └── customKeyboardLayoutGuide.swift │ └── Tests │ │ └── HamsterUIKitTests │ │ └── HamsterUIKitTests.swift ├── HamsteriOS │ ├── .gitignore │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ ├── CloudKit │ │ │ └── CloudKitHelper.swift │ │ ├── Extensions │ │ │ ├── UICellConfigurationState+.swift │ │ │ └── UserDefault+.swift │ │ ├── HamsterAppDependencyContainer.swift │ │ ├── Model │ │ │ ├── About │ │ │ │ ├── AboutCellInfo.swift │ │ │ │ └── AboutCellType.swift │ │ │ ├── Backup │ │ │ │ └── BackupSwipeAction.swift │ │ │ ├── FavoriteButton.swift │ │ │ ├── FileInfo.swift │ │ │ ├── Keyboard │ │ │ │ └── ToolbarStepperModel.swift │ │ │ ├── OpenSource │ │ │ │ └── OpenSourceInfo.swift │ │ │ ├── SettingItemModel.swift │ │ │ ├── SettingSectionModel.swift │ │ │ ├── SettingType.swift │ │ │ └── SettingsSubView.swift │ │ ├── Resources │ │ │ └── Images.xcassets │ │ │ │ └── Contents.json │ │ ├── UILayer │ │ │ ├── About │ │ │ │ ├── AboutRootView.swift │ │ │ │ ├── AboutViewController.swift │ │ │ │ └── OpenSource │ │ │ │ │ ├── OpenSourceRootView.swift │ │ │ │ │ ├── OpenSourceTableViewCell.swift │ │ │ │ │ └── OpenSourceViewController.swift │ │ │ ├── Backup │ │ │ │ ├── BackupRootView.swift │ │ │ │ └── BackupViewController.swift │ │ │ ├── Compotents │ │ │ │ ├── ButtonTableViewCell.swift │ │ │ │ ├── FinderViewCell.swift │ │ │ │ ├── PullDownMenuCell.swift │ │ │ │ ├── SettingsTableViewCell.swift │ │ │ │ ├── StepperTableViewCell.swift │ │ │ │ ├── TableFooterView.swift │ │ │ │ ├── TextFieldTableViewCell.swift │ │ │ │ └── ToggleTableViewCell.swift │ │ │ ├── Feedback │ │ │ │ ├── HapticFeedbackTableViewCell.swift │ │ │ │ ├── KeyboardFeedbackRootView.swift │ │ │ │ └── KeyboardFeedbackViewController.swift │ │ │ ├── Finder │ │ │ │ ├── FileBrowserView.swift │ │ │ │ ├── FinderRootView.swift │ │ │ │ ├── FinderSettingsView.swift │ │ │ │ ├── FinderViewController.swift │ │ │ │ └── SubView │ │ │ │ │ └── TextEditorViewController.swift │ │ │ ├── InputSchema │ │ │ │ ├── CloudInputSchema │ │ │ │ │ ├── CloudInputSchemaCell.swift │ │ │ │ │ ├── CloudInputSchemaInfoView.swift │ │ │ │ │ ├── CloudInputSchemaInfoViewController.swift │ │ │ │ │ ├── CloudInputSchemaRootView.swift │ │ │ │ │ ├── CloudInputSchemaViewController.swift │ │ │ │ │ ├── CreateInputSchemaRootView.swift │ │ │ │ │ └── CreateInputSchemaViewController.swift │ │ │ │ ├── InputSchemaRootView.swift │ │ │ │ └── InputSchemaViewController.swift │ │ │ ├── Keyboard │ │ │ │ ├── KeyboardSettingsRootView.swift │ │ │ │ ├── KeyboardSettingsViewController.swift │ │ │ │ └── SubViews │ │ │ │ │ ├── Compotents │ │ │ │ │ └── SymbolEditorView.swift │ │ │ │ │ ├── KeyboardLayout │ │ │ │ │ ├── Components │ │ │ │ │ │ ├── KeySwipeSettingsCell.swift │ │ │ │ │ │ └── SwipeListView.swift │ │ │ │ │ ├── KeySwipe │ │ │ │ │ │ ├── KeySwipeSettingsView.swift │ │ │ │ │ │ ├── KeySwipeSettingsViewController.swift │ │ │ │ │ │ └── add │ │ │ │ │ │ │ ├── AddKeySwipeRootView.swift │ │ │ │ │ │ │ └── AddKeySwipeViewController.swift │ │ │ │ │ ├── KeyboardLayoutCell.swift │ │ │ │ │ ├── KeyboardLayoutRootView.swift │ │ │ │ │ ├── KeyboardLayoutViewController.swift │ │ │ │ │ └── LayoutSettings │ │ │ │ │ │ ├── ChineseNineGridKeyboardSettingsView.swift │ │ │ │ │ │ ├── ChineseStanderSystemKeyboardSettingsView.swift │ │ │ │ │ │ ├── ChineseStanderSystemKeyboardSwipeSettingsView.swift │ │ │ │ │ │ ├── CustomKeyboardSettingsView.swift │ │ │ │ │ │ └── LayoutSettingsViewController.swift │ │ │ │ │ ├── NumberNineGridSettings │ │ │ │ │ ├── NumberNineGridSettingsRootView.swift │ │ │ │ │ ├── NumberNineGridSettingsView.swift │ │ │ │ │ └── NumberNineGridSettingsViewController.swift │ │ │ │ │ ├── SpaceSettings │ │ │ │ │ ├── SpaceSettingsRootView.swift │ │ │ │ │ └── SpaceSettingsViewController.swift │ │ │ │ │ ├── SymbolKeyboardSettings │ │ │ │ │ ├── SymbolKeyboardSettingViewController.swift │ │ │ │ │ └── SymbolKeyboardSettingsRootView.swift │ │ │ │ │ ├── SymbolSettings │ │ │ │ │ ├── SymbolSettingsRootView.swift │ │ │ │ │ └── SymbolSettingsViewController.swift │ │ │ │ │ └── Toolbar │ │ │ │ │ ├── ToolbarSettingsRootView.swift │ │ │ │ │ └── ToolbarSettingsViewController.swift │ │ │ ├── KeyboardColor │ │ │ │ ├── KeyboardColorRootView.swift │ │ │ │ ├── KeyboardColorTableViewCell.swift │ │ │ │ ├── KeyboardColorView.swift │ │ │ │ └── KeyboardColorViewController.swift │ │ │ ├── Main │ │ │ │ └── MainViewController.swift │ │ │ ├── RIME │ │ │ │ ├── Logger │ │ │ │ │ └── RimeLoggerViewController.swift │ │ │ │ ├── RimeRootView.swift │ │ │ │ └── RimeViewController.swift │ │ │ ├── Settings │ │ │ │ ├── SettingsRootView.swift │ │ │ │ └── SettingsViewController.swift │ │ │ ├── UploadInputSchema │ │ │ │ ├── UploadInputSchemaRootView.swift │ │ │ │ └── UploadInputSchemaViewController.swift │ │ │ └── iCloud │ │ │ │ ├── AppleCloudRootView.swift │ │ │ │ └── AppleCloudViewController.swift │ │ └── ViewModel │ │ │ ├── About │ │ │ └── AboutViewModel.swift │ │ │ ├── Backup │ │ │ └── BackupViewModel.swift │ │ │ ├── ColorSettings │ │ │ └── ColorSchemaViewModel.swift │ │ │ ├── Feedback │ │ │ └── KeyboardFeedbackViewModel.swift │ │ │ ├── Finder │ │ │ ├── FileBrowserViewModel.swift │ │ │ └── FinderViewModel.swift │ │ │ ├── InputSchema │ │ │ └── InputSchemaViewModel.swift │ │ │ ├── Keyboard │ │ │ └── KeyboardSettingsViewModel.swift │ │ │ ├── Main │ │ │ └── MainViewModel.swift │ │ │ ├── OpenSource │ │ │ └── OpenSourceViewModel.swift │ │ │ ├── RIME │ │ │ └── RimeViewModel.swift │ │ │ ├── Settings │ │ │ └── SettingsViewModel.swift │ │ │ ├── UploadInputSchema │ │ │ └── UploadInputSchemaViewModel.swift │ │ │ └── iCloud │ │ │ └── AppleCloudViewModel.swift │ └── Tests │ │ └── CloudKit │ │ └── CloudKitHelperTest.swift └── RimeKit │ ├── .gitignore │ ├── Package.swift │ ├── README.md │ ├── Sources │ ├── C │ │ ├── rime_api.h │ │ └── rime_levers_api.h │ ├── ObjC │ │ ├── include │ │ │ ├── irime_api.h │ │ │ └── irime_entity.h │ │ └── irime_api.m │ └── Swift │ │ └── Rime.swift │ └── Tests │ └── RimeKitTests │ └── RimeKitTests.swift ├── README.md ├── Resources ├── SharedSupport │ └── hamster.yaml ├── en.lproj │ └── InfoPlist.strings ├── zh-HK.lproj │ └── InfoPlist.strings ├── zh-Hans.lproj │ └── InfoPlist.strings ├── zh-Hant.lproj │ └── InfoPlist.strings └── zh.lproj │ └── InfoPlist.strings ├── SbxlmKeyboard ├── Info.plist ├── KeyboardViewController.swift ├── SbxlmKeyboard.entitlements ├── en.lproj │ └── InfoPlist.strings ├── zh-HK.lproj │ └── InfoPlist.strings ├── zh-Hans.lproj │ └── InfoPlist.strings ├── zh-Hant.lproj │ └── InfoPlist.strings └── zh.lproj │ └── InfoPlist.strings ├── ci_scripts └── ci_post_clone.sh └── librimeFramework.sh /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: bug report 3 | about: 创建一个 Bug 的反馈,帮助我来改进「仓输入法」 4 | title: "「Bug」请填 Bug 的标题" 5 | labels: '' 6 | assignees: imfuxiao 7 | 8 | --- 9 | 10 | **Bug的详细描述** 11 | 简明扼要地描述这个 Bug 是什么,如 12 | 13 | **Bug 的复现过程** 14 | 重现此 Bug 的步骤: 15 | 1. 打开 '...' 16 | 2. 点击 '....' 17 | 3. 看到 ... 异常。 18 | 19 | **您的预期行为** 20 | 简明扼要地描述您期望得到的结果。 21 | 22 | **屏幕截图/视频** 23 | 如果可能,请添加截图或视频以帮助解释您的问题。 24 | 25 | **设备信息(请填写以下信息):** 26 | - iOS 版本:如 17.0.3 27 | - 仓输入法版本:商店版 2.4.1 或 TF 3.0.0(122) 28 | 29 | **其他** 30 | 31 | 在此添加有关该 bug 的任何其他信息。 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: feature request 3 | about: 为此项目提出建议 4 | title: "「Feature」" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **您的功能请求是否与某些问题有关?请描述** 11 | 简明扼要地描述问题所在。 12 | 13 | **描述您想要的解决方案** 14 | 简明扼要地描述您希望发生的结果。 15 | 16 | **描述您考虑过的替代方案** 17 | 简明扼要地描述您考虑过的任何替代解决方案或功能。 18 | 19 | **其他** 20 | 在此处添加有关功能请求的任何其他上下文或截图或视频等等。 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build Test 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | build: 12 | runs-on: macos-13 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | with: 17 | submodules: true 18 | 19 | - name: Download Frameworks 20 | uses: robinraju/release-downloader@v1.7 21 | with: 22 | repository: "imfuxiao/LibrimeKit" 23 | latest: true 24 | fileName: "Frameworks.tgz" 25 | 26 | - name: Un-tar Frameworks 27 | run: | 28 | tar -xzf Frameworks.tgz -C Frameworks/.. 29 | 30 | - name: Build Schemas 31 | run: | 32 | make schema 33 | 34 | - name: Select Xcode 35 | run: sudo xcode-select -s "/Applications/Xcode_15.0.1.app" 36 | 37 | - name: Compile 38 | run: | 39 | xcodebuild archive -archivePath "Hamster" -scheme "Hamster" -sdk "iphoneos" -arch arm64 -configuration Release CODE_SIGNING_ALLOWED=NO 40 | BUILT_PATH=$(find Hamster.xcarchive -name '*.app' -type d | head -1) 41 | find "$BUILT_PATH" -type d -path '*/Frameworks/*.dylib' -exec codesign --force --sign - --timestamp=none \{\} \; 42 | codesign --force --sign - --entitlements "Hamster/Hamster.entitlements" --timestamp=none "$BUILT_PATH" 43 | tar -acf Hamster.xcarchive.tgz Hamster.xcarchive 44 | 45 | - name: Upload xcarchive 46 | uses: actions/upload-artifact@v3 47 | with: 48 | name: Hamster-ios-arm64-xcarchive 49 | path: Hamster.xcarchive.tgz 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .build 3 | .swiftpm 4 | .netrc 5 | /lib 6 | .vscode 7 | .plum 8 | .deps 9 | .HamsterInputSchemas 10 | Resources/SharedSupport/*.zip 11 | *.zip 12 | *.tgz 13 | 14 | 15 | # xcode gitignore 16 | # https://github.com/github/gitignore/blob/main/Global/Xcode.gitignore 17 | ## User settings 18 | xcuserdata/ 19 | ## Xcode 8 and earlier 20 | *.xcscmblueprint 21 | *.xccheckout 22 | .history 23 | .HamsterInputSchemas/ 24 | .tmp 25 | package/ 26 | *.xcframework 27 | 28 | # VueJS 29 | /Packages/HamsterFileServer/Front/node_modules 30 | /Packages/HamsterFileServer/Front/dist/* -------------------------------------------------------------------------------- /Frameworks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Frameworks/.gitkeep -------------------------------------------------------------------------------- /Hamster.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Hamster.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Hamster.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Hamster/AppDelegete.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegete.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/5. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 13 | // Override point for customization after application launch. 14 | return true 15 | } 16 | 17 | // MARK: UISceneSession Lifecycle 18 | 19 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 20 | // Called when a new scene session is being created. 21 | // Use this method to select a configuration to create the new scene with. 22 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 23 | } 24 | 25 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 26 | // Called when the user discards a scene session. 27 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 28 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Hamster/Assets/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 | -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon-App-iTunes 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon-App-iTunes 1.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon-App-iTunes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon-App-iTunes.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_114×114@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_114×114@3x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_120×120@2x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_120×120@2x 1.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_120×120@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_120×120@2x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_120×120@3x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_120×120@3x 1.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_120×120@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_120×120@3x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_128×128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_128×128@2x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_136×136@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_136×136@2x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_152×152@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_152×152@2x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_167x167@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_167x167@2x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_180×180@3x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_180×180@3x 1.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_180×180@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_180×180@3x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_192×192@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_192×192@3x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_40×40@2x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_40×40@2x 1.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_40×40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_40×40@2x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_58×58@2x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_58×58@2x 1.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_58×58@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_58×58@2x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_60×60@3x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_60×60@3x 1.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_60×60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_60×60@3x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_76×76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_76×76@2x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_80×80@2x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_80×80@2x 1.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_80×80@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_80×80@2x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_87×87@3x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_87×87@3x 1.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_87×87@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/AppIcon.appiconset/Icon_87×87@3x.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/Hamster.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Hamster.png", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "filename" : "HamsterWhite.png", 15 | "idiom" : "universal" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/Hamster.imageset/Hamster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/Hamster.imageset/Hamster.png -------------------------------------------------------------------------------- /Hamster/Assets/Assets.xcassets/Hamster.imageset/HamsterWhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Hamster/Assets/Assets.xcassets/Hamster.imageset/HamsterWhite.png -------------------------------------------------------------------------------- /Hamster/Assets/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Hamster/Hamster.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.icloud-container-environment 6 | Production 7 | aps-environment 8 | production 9 | com.apple.developer.icloud-container-identifiers 10 | 11 | iCloud.dev.fuxiao.app.hamsterapp 12 | 13 | com.apple.developer.icloud-services 14 | 15 | CloudDocuments 16 | CloudKit 17 | 18 | com.apple.developer.ubiquity-container-identifiers 19 | 20 | iCloud.dev.fuxiao.app.hamsterapp 21 | 22 | com.apple.security.application-groups 23 | 24 | group.dev.fuxiao.app.Hamster 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Hamster/HamsterDebug.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.icloud-container-environment 8 | Development 9 | com.apple.developer.icloud-container-identifiers 10 | 11 | iCloud.dev.fuxiao.app.hamsterapp 12 | 13 | com.apple.developer.icloud-services 14 | 15 | CloudDocuments 16 | CloudKit 17 | 18 | com.apple.developer.ubiquity-container-identifiers 19 | 20 | iCloud.dev.fuxiao.app.hamsterapp 21 | 22 | com.apple.security.application-groups 23 | 24 | group.dev.fuxiao.app.Hamster 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Hamster/Shortcuts/Actions/RimeDeployBackgroundIntent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RimeDeployBackgroundIntent.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/11/8. 6 | // 7 | 8 | import AppIntents 9 | import HamsteriOS 10 | import HamsterKeyboardKit 11 | import HamsterKit 12 | import OSLog 13 | 14 | @available(iOS 16.0, *) 15 | struct RimeDeployBackgroundIntent: AppIntent { 16 | static var title: LocalizedStringResource = "RIME 重新部署(静默)" 17 | 18 | static var openAppWhenRun: Bool { 19 | return false 20 | } 21 | 22 | static var authenticationPolicy: IntentAuthenticationPolicy { 23 | .alwaysAllowed 24 | } 25 | 26 | static var description = IntentDescription("仓输入法 - RIME 重新部署,后台静默运行,不会打开应用,但可能会有超时的异常。") 27 | 28 | @MainActor 29 | func perform() async throws -> some ReturnsValue { 30 | var hamsterConfiguration = HamsterAppDependencyContainer.shared.configuration 31 | do { 32 | try HamsterAppDependencyContainer.shared.rimeContext.deployment(configuration: &hamsterConfiguration) 33 | HamsterAppDependencyContainer.shared.configuration = hamsterConfiguration 34 | return .result(value: true) 35 | } catch { 36 | return .result(value: false) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Hamster/Shortcuts/Actions/RimeDeployIntent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RimeDeployIntent.swift 3 | // Hamster 4 | // 5 | // Created by morse on 22/6/2023. 6 | // 7 | 8 | import AppIntents 9 | import HamsteriOS 10 | import HamsterKeyboardKit 11 | import HamsterKit 12 | import OSLog 13 | 14 | @available(iOS 16.0, *) 15 | struct RimeDeployIntent: AppIntent { 16 | static var title: LocalizedStringResource = "RIME 重新部署" 17 | 18 | static var openAppWhenRun: Bool { 19 | return true 20 | } 21 | 22 | static var authenticationPolicy: IntentAuthenticationPolicy { 23 | .requiresAuthentication 24 | } 25 | 26 | static var description = IntentDescription("仓输入法 - RIME 重新部署") 27 | 28 | @MainActor 29 | func perform() async throws -> some ReturnsValue { 30 | HamsterAppDependencyContainer.shared.mainViewModel.navigationToRIME() 31 | HamsterAppDependencyContainer.shared.mainViewModel.execShortcutCommand(.rimeDeploy) 32 | return .result() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Hamster/Shortcuts/IntentProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntentProvider.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/9/25. 6 | // 7 | 8 | import AppIntents 9 | 10 | @available(iOS 16.0, *) 11 | struct IntentProvider: AppShortcutsProvider { 12 | static var appShortcuts: [AppShortcut] { 13 | return [ 14 | AppShortcut(intent: RimeSyncIntent(), phrases: ["RIME Sync", "RIME 同步"]), 15 | AppShortcut(intent: RimeDeployIntent(), phrases: ["RIME Deploy", "RIME 重新部署"]), 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /HamsterKeyboard/HamsterKeyboard.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | group.dev.fuxiao.app.Hamster 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /HamsterKeyboard/HamsterKeyboardInputViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardInputViewController.swift 3 | // HamsterKeyboard 4 | // 5 | // Created by morse on 11/1/2023. 6 | // 7 | 8 | import HamsterKeyboardKit 9 | import UIKit 10 | 11 | public class HamsterKeyboardInputViewController: KeyboardInputViewController {} 12 | -------------------------------------------------------------------------------- /HamsterKeyboard/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSExtension 6 | 7 | NSExtensionAttributes 8 | 9 | IsASCIICapable 10 | 11 | PrefersRightToLeft 12 | 13 | PrimaryLanguage 14 | zh-Hans 15 | RequestsOpenAccess 16 | 17 | 18 | NSExtensionPointIdentifier 19 | com.apple.keyboard-service 20 | NSExtensionPrincipalClass 21 | $(PRODUCT_MODULE_NAME).HamsterKeyboardInputViewController 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /HamsterKeyboard/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 2023/11/16. 6 | 7 | */ 8 | "CFBundleDisplayName" = "仓输入法"; 9 | -------------------------------------------------------------------------------- /HamsterKeyboard/zh-HK.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 2023/11/16. 6 | 7 | */ 8 | "CFBundleDisplayName" = "仓输入法"; 9 | -------------------------------------------------------------------------------- /HamsterKeyboard/zh-Hans.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 2023/11/16. 6 | 7 | */ 8 | "CFBundleDisplayName" = "仓输入法"; 9 | -------------------------------------------------------------------------------- /HamsterKeyboard/zh-Hant.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 2023/11/16. 6 | 7 | */ 8 | "CFBundleDisplayName" = "仓输入法"; 9 | -------------------------------------------------------------------------------- /HamsterKeyboard/zh.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 2023/11/16. 6 | 7 | */ 8 | "CFBundleDisplayName" = "仓输入法"; 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 xiao.fu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: framework cleanFramework scheme cleanScheme 2 | 3 | mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) 4 | mkfile_dir := $(dir $(mkfile_path)) 5 | framework: 6 | bash ./librimeFramework.sh 7 | cleanFramework: 8 | rm -rf Packages/RimeKit/Frameworks 9 | schema: 10 | bash ./InputSchemaBuild.sh 11 | cleanSchema: 12 | rm -rf .tmp Resources/SharedSupport/*.zip -------------------------------------------------------------------------------- /Packages/HamsterFileServer/.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 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "node": true 5 | }, 6 | "extends": [ 7 | "plugin:vue/essential", 8 | "eslint:recommended", 9 | "@vue/eslint-config-prettier" 10 | ], 11 | "rules": { 12 | "vue/multi-word-component-names": "off", 13 | "vue/no-reserved-component-names": "warn", 14 | "vue/no-mutating-props": "warn" 15 | }, 16 | "parserOptions": { 17 | "ecmaVersion": "latest", 18 | "sourceType": "module" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | dist -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5" 3 | } 4 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@/*": ["./src/*"] 6 | } 7 | }, 8 | "include": ["src/**/*"], 9 | "exclude": ["node_modules", "dist"] 10 | } 11 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/public/.gitkeep -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "File Browser", 3 | "short_name": "File Browser", 4 | "icons": [ 5 | { 6 | "src": "./img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "./static/img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "/", 17 | "display": "standalone", 18 | "background_color": "#ffffff", 19 | "theme_color": "#455a64" 20 | } 21 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/api/commands.js: -------------------------------------------------------------------------------- 1 | import { removePrefix } from "./utils"; 2 | import { baseURL } from "@/utils/constants"; 3 | import store from "@/store"; 4 | 5 | const ssl = window.location.protocol === "https:"; 6 | const protocol = ssl ? "wss:" : "ws:"; 7 | 8 | export default function command(url, command, onmessage, onclose) { 9 | url = removePrefix(url); 10 | url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${store.state.jwt}`; 11 | 12 | let conn = new window.WebSocket(url); 13 | conn.onopen = () => conn.send(command); 14 | conn.onmessage = onmessage; 15 | conn.onclose = onclose; 16 | } 17 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/api/index.js: -------------------------------------------------------------------------------- 1 | import * as files from "./files"; 2 | import * as share from "./share"; 3 | import * as users from "./users"; 4 | import * as settings from "./settings"; 5 | import * as pub from "./pub"; 6 | import search from "./search"; 7 | import commands from "./commands"; 8 | 9 | export { files, share, users, settings, pub, commands, search }; 10 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/api/search.js: -------------------------------------------------------------------------------- 1 | import { fetchURL, removePrefix } from "./utils"; 2 | import url from "../utils/url"; 3 | 4 | export default async function search(base, query) { 5 | base = removePrefix(base); 6 | query = encodeURIComponent(query); 7 | 8 | if (!base.endsWith("/")) { 9 | base += "/"; 10 | } 11 | 12 | let res = await fetchURL(`/api/search${base}?query=${query}`, {}); 13 | 14 | let data = await res.json(); 15 | 16 | data = data.map((item) => { 17 | item.url = `/files${base}` + url.encodePath(item.path); 18 | 19 | if (item.dir) { 20 | item.url += "/"; 21 | } 22 | 23 | return item; 24 | }); 25 | 26 | return data; 27 | } 28 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/api/settings.js: -------------------------------------------------------------------------------- 1 | import { fetchURL, fetchJSON } from "./utils"; 2 | 3 | export function get() { 4 | return fetchJSON(`/api/settings`, {}); 5 | } 6 | 7 | export async function update(settings) { 8 | await fetchURL(`/api/settings`, { 9 | method: "PUT", 10 | body: JSON.stringify(settings), 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/api/share.js: -------------------------------------------------------------------------------- 1 | import { fetchURL, fetchJSON, removePrefix, createURL } from "./utils"; 2 | 3 | export async function list() { 4 | return fetchJSON("/api/shares"); 5 | } 6 | 7 | export async function get(url) { 8 | url = removePrefix(url); 9 | return fetchJSON(`/api/share${url}`); 10 | } 11 | 12 | export async function remove(hash) { 13 | await fetchURL(`/api/share/${hash}`, { 14 | method: "DELETE", 15 | }); 16 | } 17 | 18 | export async function create(url, password = "", expires = "", unit = "hours") { 19 | url = removePrefix(url); 20 | url = `/api/share${url}`; 21 | if (expires !== "") { 22 | url += `?expires=${expires}&unit=${unit}`; 23 | } 24 | let body = "{}"; 25 | if (password != "" || expires !== "" || unit !== "hours") { 26 | body = JSON.stringify({ password: password, expires: expires, unit: unit }); 27 | } 28 | return fetchJSON(url, { 29 | method: "POST", 30 | body: body, 31 | }); 32 | } 33 | 34 | export function getShareURL(share) { 35 | return createURL("share/" + share.hash, {}, false); 36 | } 37 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/api/users.js: -------------------------------------------------------------------------------- 1 | import { fetchURL, fetchJSON } from "./utils"; 2 | 3 | export async function getAll() { 4 | return fetchJSON(`/api/users`, {}); 5 | } 6 | 7 | export async function get(id) { 8 | return fetchJSON(`/api/users/${id}`, {}); 9 | } 10 | 11 | export async function create(user) { 12 | const res = await fetchURL(`/api/users`, { 13 | method: "POST", 14 | body: JSON.stringify({ 15 | what: "user", 16 | which: [], 17 | data: user, 18 | }), 19 | }); 20 | 21 | if (res.status === 201) { 22 | return res.headers.get("Location"); 23 | } 24 | } 25 | 26 | export async function update(user, which = ["all"]) { 27 | await fetchURL(`/api/users/${user.id}`, { 28 | method: "PUT", 29 | body: JSON.stringify({ 30 | what: "user", 31 | which: which, 32 | data: user, 33 | }), 34 | }); 35 | } 36 | 37 | export async function remove(id) { 38 | await fetchURL(`/api/users/${id}`, { 39 | method: "DELETE", 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-cyrillic-ext.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-cyrillic-ext.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-cyrillic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-cyrillic.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-greek-ext.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-greek-ext.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-greek.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-greek.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-latin-ext.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-latin-ext.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-latin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-latin.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-vietnamese.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/bold-vietnamese.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-cyrillic-ext.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-cyrillic-ext.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-cyrillic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-cyrillic.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-greek-ext.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-greek-ext.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-greek.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-greek.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-latin-ext.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-latin-ext.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-latin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-latin.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-vietnamese.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/medium-vietnamese.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-cyrillic-ext.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-cyrillic-ext.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-cyrillic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-cyrillic.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-greek-ext.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-greek-ext.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-greek.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-greek.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-latin-ext.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-latin-ext.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-latin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-latin.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-vietnamese.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Front/src/assets/fonts/roboto/normal-vietnamese.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/header/Action.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/header/HeaderBar.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/prompts/Download.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 46 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/prompts/Help.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 38 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/prompts/Replace.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 48 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/prompts/ReplaceRename.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 48 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/prompts/ShareDelete.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 42 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/prompts/Upload.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 39 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/settings/Commands.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 31 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/settings/Languages.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 57 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/components/settings/Themes.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/css/_buttons.css: -------------------------------------------------------------------------------- 1 | .button { 2 | outline: 0; 3 | border: 0; 4 | padding: .5em 1em; 5 | border-radius: .1em; 6 | cursor: pointer; 7 | background: var(--blue); 8 | color: white; 9 | border: 1px solid rgba(0, 0, 0, 0.05); 10 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.05); 11 | transition: .1s ease all; 12 | } 13 | 14 | .button:hover { 15 | background-color: var(--dark-blue); 16 | } 17 | 18 | .button--block { 19 | margin: 0 0 0.5em; 20 | display: block; 21 | width: 100%; 22 | } 23 | 24 | .button--red { 25 | background: var(--red); 26 | } 27 | 28 | .button--blue { 29 | background: var(--blue); 30 | } 31 | 32 | .button--flat { 33 | color: var(--dark-blue); 34 | background: transparent; 35 | box-shadow: 0 0 0; 36 | border: 0; 37 | text-transform: uppercase; 38 | } 39 | 40 | .button--flat:hover { 41 | background: var(--moon-grey); 42 | } 43 | 44 | .button--flat.button--red { 45 | color: var(--dark-red); 46 | } 47 | 48 | .button--flat.button--grey { 49 | color: #6f6f6f; 50 | } 51 | 52 | .button[disabled] { 53 | opacity: .5; 54 | cursor: not-allowed; 55 | } 56 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/css/_inputs.css: -------------------------------------------------------------------------------- 1 | .input { 2 | border-radius: .1em; 3 | padding: .5em 1em; 4 | background: white; 5 | border: 1px solid rgba(0, 0, 0, 0.1); 6 | transition: .2s ease all; 7 | color: #333; 8 | margin: 0; 9 | } 10 | 11 | .input:hover, 12 | .input:focus { 13 | border-color: rgba(0, 0, 0, 0.2); 14 | } 15 | 16 | .input--block { 17 | margin-bottom: .5em; 18 | display: block; 19 | width: 100%; 20 | } 21 | 22 | .input--textarea { 23 | line-height: 1.15; 24 | font-family: monospace; 25 | min-height: 10em; 26 | resize: vertical; 27 | } 28 | 29 | .input--red { 30 | background: #fcd0cd; 31 | } 32 | 33 | .input--green { 34 | background: #c9f2da; 35 | } 36 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/css/_share.css: -------------------------------------------------------------------------------- 1 | .share { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: center; 5 | align-items: flex-start; 6 | } 7 | 8 | @media (max-width: 736px) { 9 | .share { 10 | display: block; 11 | } 12 | } 13 | 14 | .share__box { 15 | box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px; 16 | background: #fff; 17 | border-radius: 0.2em; 18 | margin: 5px; 19 | overflow: hidden; 20 | } 21 | 22 | .share__box__header { 23 | padding: 1em; 24 | text-align: center; 25 | } 26 | 27 | .share__box__icon i { 28 | font-size: 10em; 29 | color: #40c4ff; 30 | } 31 | 32 | .share__box__center { 33 | text-align: center; 34 | } 35 | 36 | .share__box__info { 37 | flex: 1 1 18em; 38 | } 39 | 40 | .share__box__element { 41 | padding: 1em; 42 | border-top: 1px solid rgba(0, 0, 0, 0.1); 43 | word-break: break-all; 44 | } 45 | 46 | .share__box__element .button { 47 | display: inline-block; 48 | } 49 | 50 | .share__box__element .button i { 51 | display: block; 52 | margin-bottom: 4px; 53 | } 54 | 55 | .share__box__items { 56 | text-align: left; 57 | flex: 10 0 25em; 58 | } 59 | 60 | .share__box__items #listing.list .item { 61 | cursor: pointer; 62 | border-left: 0; 63 | border-right: 0; 64 | border-bottom: 0; 65 | border-top: 1px solid rgba(0, 0, 0, 0.1); 66 | } 67 | 68 | .share__box__items #listing.list .item .name { 69 | width: 50%; 70 | } 71 | 72 | .share__box__items #listing.list .item .modified { 73 | width: 25%; 74 | } 75 | 76 | .share__wrong__password { 77 | background: var(--red); 78 | color: #fff; 79 | padding: .5em; 80 | text-align: center; 81 | animation: .2s opac forwards; 82 | } -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/css/_variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --blue: #2196f3; 3 | --dark-blue: #1E88E5; 4 | --red: #F44336; 5 | --dark-red: #D32F2F; 6 | --moon-grey: #f2f2f2; 7 | 8 | --icon-red: #da4453; 9 | --icon-orange: #f47750; 10 | --icon-yellow: #fdbc4b; 11 | --icon-green: #2ecc71; 12 | --icon-blue: #1d99f3; 13 | --icon-violet: #9b59b6; 14 | } 15 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/css/login.css: -------------------------------------------------------------------------------- 1 | #login { 2 | background: #fff; 3 | position: fixed; 4 | top: 0; 5 | left: 0; 6 | width: 100%; 7 | height: 100%; 8 | } 9 | 10 | #login img { 11 | width: 4em; 12 | height: 4em; 13 | margin: 0 auto; 14 | display: block; 15 | } 16 | 17 | #login h1 { 18 | text-align: center; 19 | font-size: 2.5em; 20 | margin: .4em 0 .67em; 21 | } 22 | 23 | #login form { 24 | position: fixed; 25 | top: 50%; 26 | left: 50%; 27 | transform: translate(-50%, -50%); 28 | max-width: 16em; 29 | width: 90%; 30 | } 31 | 32 | #login.recaptcha form { 33 | min-width: 304px; 34 | } 35 | 36 | #login #recaptcha { 37 | margin: .5em 0 0; 38 | } 39 | 40 | #login .wrong { 41 | background: var(--red); 42 | color: #fff; 43 | padding: .5em; 44 | text-align: center; 45 | animation: .2s opac forwards; 46 | } 47 | 48 | @keyframes opac { 49 | 0% { 50 | opacity: 0; 51 | } 52 | 100% { 53 | opacity: 1; 54 | } 55 | } 56 | 57 | #login p { 58 | cursor: pointer; 59 | text-align: right; 60 | color: var(--blue); 61 | text-transform: lowercase; 62 | font-weight: 500; 63 | font-size: 0.9rem; 64 | margin: .5rem 0; 65 | } 66 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/css/upload-files.css: -------------------------------------------------------------------------------- 1 | .upload-files .card.floating { 2 | left: auto; 3 | top: auto; 4 | margin: 0; 5 | right: 0; 6 | bottom: 0; 7 | transform: none; 8 | } 9 | 10 | .upload-files .file { 11 | margin-bottom: 8px; 12 | } 13 | 14 | .upload-files .file .file-name { 15 | font-size: 1.1em; 16 | display: flex; 17 | align-items: center; 18 | } 19 | 20 | .upload-files .file .file-name i { 21 | margin-right: 5px; 22 | } 23 | 24 | .upload-files .file .file-progress { 25 | margin-top: 2px; 26 | width: 100%; 27 | height: 5px; 28 | } 29 | 30 | .upload-files .file .file-progress div { 31 | height: 100%; 32 | background-color: #40c4ff; 33 | width: 0; 34 | transition: 0.2s ease width; 35 | border-radius: 10px; 36 | } 37 | 38 | .upload-files.closed .card-content { 39 | display: none; 40 | padding: 0em 1em 1em 1em; 41 | } 42 | 43 | .upload-files .card .card-title { 44 | display: flex; 45 | align-items: center; 46 | justify-content: center; 47 | font-size: 0.8em; 48 | padding: 1em 1em 0em; 49 | } 50 | 51 | .upload-files.closed .card-title { 52 | font-size: 0.7em; 53 | padding: 0.5em 1em; 54 | } 55 | 56 | @media (max-width: 450px) { 57 | .upload-files .card.floating { 58 | max-width: 100%; 59 | width: 100%; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/main.js: -------------------------------------------------------------------------------- 1 | import App from "@/App.vue"; 2 | import i18n from "@/i18n"; 3 | import router from "@/router"; 4 | import store from "@/store"; 5 | import Vue from "@/utils/vue"; 6 | import cssVars from "css-vars-ponyfill"; 7 | import { sync } from "vuex-router-sync"; 8 | import "whatwg-fetch"; 9 | 10 | cssVars(); 11 | 12 | sync(store, router); 13 | 14 | async function start() { 15 | // try { 16 | // if (loginPage) { 17 | // await validateLogin(); 18 | // } else { 19 | // await login("", "", ""); 20 | // } 21 | // } catch (e) { 22 | // console.log(e); 23 | // } 24 | 25 | // if (recaptcha) { 26 | // await new Promise((resolve) => { 27 | // const check = () => { 28 | // if (typeof window.grecaptcha === "undefined") { 29 | // setTimeout(check, 100); 30 | // } else { 31 | // resolve(); 32 | // } 33 | // }; 34 | 35 | // check(); 36 | // }); 37 | // } 38 | 39 | new Vue({ 40 | el: "#app", 41 | store, 42 | router, 43 | i18n, 44 | template: "", 45 | components: { App }, 46 | }); 47 | } 48 | 49 | start(); 50 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | import getters from "./getters"; 4 | import upload from "./modules/upload"; 5 | import mutations from "./mutations"; 6 | 7 | Vue.use(Vuex); 8 | 9 | const state = { 10 | user: { 11 | id: 1, 12 | locale: "zh-cn", 13 | viewMode: "list", 14 | singleClick: false, 15 | perm: { 16 | admin: true, 17 | execute: true, 18 | create: true, 19 | rename: true, 20 | modify: true, 21 | delete: true, 22 | share: true, 23 | download: true, 24 | }, 25 | commands: [], 26 | lockPassword: false, 27 | hideDotfiles: false, 28 | dateFormat: false, 29 | }, 30 | req: {}, 31 | oldReq: {}, 32 | clipboard: { 33 | key: "", 34 | items: [], 35 | }, 36 | jwt: "", 37 | progress: 0, 38 | loading: false, 39 | reload: false, 40 | selected: [], 41 | multiple: false, 42 | prompts: [], 43 | showShell: false, 44 | }; 45 | 46 | export default new Vuex.Store({ 47 | strict: true, 48 | state, 49 | getters, 50 | mutations, 51 | modules: { upload }, 52 | }); 53 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/utils/constants.js: -------------------------------------------------------------------------------- 1 | const name = window.FileBrowser.Name || "File Browser"; 2 | const disableExternal = window.FileBrowser.DisableExternal; 3 | const disableUsedPercentage = window.FileBrowser.DisableUsedPercentage; 4 | const baseURL = window.FileBrowser.BaseURL; 5 | const staticURL = window.FileBrowser.StaticURL; 6 | const recaptcha = window.FileBrowser.ReCaptcha; 7 | const recaptchaKey = window.FileBrowser.ReCaptchaKey; 8 | const signup = window.FileBrowser.Signup; 9 | const version = window.FileBrowser.Version; 10 | const logoURL = `${staticURL}/img/logo.svg`; 11 | const noAuth = window.FileBrowser.NoAuth; 12 | const authMethod = window.FileBrowser.AuthMethod; 13 | const loginPage = window.FileBrowser.LoginPage; 14 | const theme = window.FileBrowser.Theme; 15 | const enableThumbs = window.FileBrowser.EnableThumbs; 16 | const resizePreview = window.FileBrowser.ResizePreview; 17 | const enableExec = window.FileBrowser.EnableExec; 18 | const tusSettings = window.FileBrowser.TusSettings; 19 | const origin = window.location.origin; 20 | const tusEndpoint = `/api/tus`; 21 | 22 | export { 23 | name, 24 | disableExternal, 25 | disableUsedPercentage, 26 | baseURL, 27 | logoURL, 28 | recaptcha, 29 | recaptchaKey, 30 | signup, 31 | version, 32 | noAuth, 33 | authMethod, 34 | loginPage, 35 | theme, 36 | enableThumbs, 37 | resizePreview, 38 | enableExec, 39 | tusSettings, 40 | origin, 41 | tusEndpoint, 42 | }; 43 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/utils/cookie.js: -------------------------------------------------------------------------------- 1 | export default function (name) { 2 | let re = new RegExp( 3 | "(?:(?:^|.*;\\s*)" + name + "\\s*\\=\\s*([^;]*).*$)|^.*$" 4 | ); 5 | return document.cookie.replace(re, "$1"); 6 | } 7 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/utils/css.js: -------------------------------------------------------------------------------- 1 | export default function getRule(rules) { 2 | for (let i = 0; i < rules.length; i++) { 3 | rules[i] = rules[i].toLowerCase(); 4 | } 5 | 6 | let result = null; 7 | let find = Array.prototype.find; 8 | 9 | find.call(document.styleSheets, (styleSheet) => { 10 | result = find.call(styleSheet.cssRules, (cssRule) => { 11 | let found = false; 12 | 13 | if (cssRule instanceof window.CSSStyleRule) { 14 | for (let i = 0; i < rules.length; i++) { 15 | if (cssRule.selectorText.toLowerCase() === rules[i]) { 16 | found = true; 17 | } 18 | } 19 | } 20 | 21 | return found; 22 | }); 23 | 24 | return result != null; 25 | }); 26 | 27 | return result; 28 | } 29 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/utils/index.js: -------------------------------------------------------------------------------- 1 | import { partial } from "filesize"; 2 | 3 | /** 4 | * Formats filesize as KiB/MiB/... 5 | */ 6 | export const filesize = partial({ base: 2 }); 7 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/utils/url.js: -------------------------------------------------------------------------------- 1 | export function removeLastDir(url) { 2 | var arr = url.split("/"); 3 | if (arr.pop() === "") { 4 | arr.pop(); 5 | } 6 | 7 | return arr.join("/"); 8 | } 9 | 10 | // this code borrow from mozilla 11 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#Examples 12 | export function encodeRFC5987ValueChars(str) { 13 | return ( 14 | encodeURIComponent(str) 15 | // Note that although RFC3986 reserves "!", RFC5987 does not, 16 | // so we do not need to escape it 17 | .replace(/['()]/g, escape) // i.e., %27 %28 %29 18 | .replace(/\*/g, "%2A") 19 | // The following are not required for percent-encoding per RFC5987, 20 | // so we can allow for a little better readability over the wire: |`^ 21 | .replace(/%(?:7C|60|5E)/g, unescape) 22 | ); 23 | } 24 | 25 | export function encodePath(str) { 26 | return str 27 | .split("/") 28 | .map((v) => encodeURIComponent(v)) 29 | .join("/"); 30 | } 31 | 32 | export default { 33 | encodeRFC5987ValueChars, 34 | removeLastDir, 35 | encodePath, 36 | }; 37 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/utils/vue.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Noty from "noty"; 3 | import VueLazyload from "vue-lazyload"; 4 | import i18n from "@/i18n"; 5 | import { disableExternal } from "@/utils/constants"; 6 | import AsyncComputed from "vue-async-computed"; 7 | 8 | Vue.use(VueLazyload); 9 | Vue.use(AsyncComputed); 10 | 11 | Vue.config.productionTip = true; 12 | 13 | const notyDefault = { 14 | type: "info", 15 | layout: "bottomRight", 16 | timeout: 1000, 17 | progressBar: true, 18 | }; 19 | 20 | Vue.prototype.$noty = (opts) => { 21 | new Noty(Object.assign({}, notyDefault, opts)).show(); 22 | }; 23 | 24 | Vue.prototype.$showSuccess = (message) => { 25 | new Noty( 26 | Object.assign({}, notyDefault, { 27 | text: message, 28 | type: "success", 29 | }) 30 | ).show(); 31 | }; 32 | 33 | Vue.prototype.$showError = (error, displayReport = true) => { 34 | let btns = [ 35 | Noty.button(i18n.t("buttons.close"), "", function () { 36 | n.close(); 37 | }), 38 | ]; 39 | 40 | if (!disableExternal && displayReport) { 41 | btns.unshift( 42 | Noty.button(i18n.t("buttons.reportIssue"), "", function () { 43 | window.open( 44 | "https://github.com/filebrowser/filebrowser/issues/new/choose" 45 | ); 46 | }) 47 | ); 48 | } 49 | 50 | let n = new Noty( 51 | Object.assign({}, notyDefault, { 52 | text: error.message || error, 53 | type: "error", 54 | timeout: null, 55 | buttons: btns, 56 | }) 57 | ); 58 | 59 | n.show(); 60 | }; 61 | 62 | Vue.directive("focus", { 63 | inserted: function (el) { 64 | el.focus(); 65 | }, 66 | }); 67 | 68 | export default Vue; 69 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/views/Errors.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 47 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Front/src/views/Layout.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 47 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.8 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: "HamsterFileServer", 8 | defaultLocalization: "en", 9 | platforms: [ 10 | .iOS(.v14), 11 | ], 12 | products: [ 13 | .library( 14 | name: "HamsterFileServer", 15 | targets: ["HamsterFileServer"]), 16 | ], 17 | dependencies: [ 18 | .package(url: "https://github.com/imfuxiao/GCDWebServer.git", exact: "3.5.5"), 19 | .package(url: "https://github.com/weichsel/ZIPFoundation.git", exact: "0.9.16"), 20 | ], 21 | targets: [ 22 | .target( 23 | name: "HamsterFileServer", 24 | dependencies: [ 25 | .product(name: "GCDWebServers", package: "GCDWebServer"), 26 | .product(name: "ZIPFoundation", package: "ZIPFoundation"), 27 | ], 28 | path: "Sources", 29 | resources: [ 30 | .copy("Resources/FileServer.bundle"), 31 | ]), 32 | .testTarget( 33 | name: "HamsterFileServerTests", 34 | dependencies: ["HamsterFileServer"]), 35 | ]) 36 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/README.md: -------------------------------------------------------------------------------- 1 | # HamsterFileServer 2 | 3 | Hamster文件服务. 4 | 5 | ## 引用开源软件 6 | * [Vapor](https://github.com/vapor/vapor.git) 7 | * [Leaf](https://github.com/vapor/leaf.git) 8 | * [ZIPFoundation](https://github.com/weichsel/ZIPFoundation.git) 9 | * [FileBrowser](https://github.com/filebrowser/filebrowser): 前端UI源码是在FileBrowser的Vue源码上改动 10 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-cyrillic-c72dc79d.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-cyrillic-c72dc79d.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-cyrillic-ext-d2a7562e.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-cyrillic-ext-d2a7562e.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-greek-20b1d460.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-greek-20b1d460.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-latin-827bd1a8.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-latin-827bd1a8.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-latin-ext-2d83ee25.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-latin-ext-2d83ee25.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-vietnamese-2096d809.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/bold-vietnamese-2096d809.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/material-icons-8265f647.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/material-icons-8265f647.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/material-icons-fd84f88b.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/material-icons-fd84f88b.woff -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-cyrillic-ef372eb9.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-cyrillic-ef372eb9.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-cyrillic-ext-2d290fe6.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-cyrillic-ext-2d290fe6.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-greek-1320e5dd.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-greek-1320e5dd.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-latin-01a44f86.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-latin-01a44f86.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-latin-ext-b4dae108.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-latin-ext-b4dae108.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-vietnamese-20d9ffa9.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/medium-vietnamese-20d9ffa9.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-cyrillic-ext-457c1e0d.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-cyrillic-ext-457c1e0d.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-cyrillic-fb0297aa.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-cyrillic-fb0297aa.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-greek-adc0b6a1.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-greek-adc0b6a1.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-latin-ext-55f25e8b.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-latin-ext-55f25e8b.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-latin-f7bbc846.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-latin-f7bbc846.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-vietnamese-0af602fe.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/assets/normal-vietnamese-0af602fe.woff2 -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Sources/Resources/FileServer.bundle/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "File Browser", 3 | "short_name": "File Browser", 4 | "icons": [ 5 | { 6 | "src": "./img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "./static/img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "/", 17 | "display": "standalone", 18 | "background_color": "#ffffff", 19 | "theme_color": "#455a64" 20 | } 21 | -------------------------------------------------------------------------------- /Packages/HamsterFileServer/Tests/HamsterFileServerTests/HamsterFileServerTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import HamsterFileServer 3 | 4 | final class HamsterFileServerTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/README.md: -------------------------------------------------------------------------------- 1 | # HamsterKeyboard 2 | 3 | Hamster 输入法键盘包。 4 | 5 | 基于 [KeyboardKit](https://github.com/KeyboardKit/KeyboardKit) v7.8.0 源码改造。 6 | 7 | 将 KeyboardKit 中 视图层 SwiftUI 代码改为 UIKit。 8 | 9 | 视图层 UIKit 部分代码来源于:[tasty-imitation-keyboard](https://github.com/archagon/tasty-imitation-keyboard) 10 | 11 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Configuration/Models/CustomizeKeyboardsConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomizeKeyboardsConfiguration.swift 3 | // 4 | // 5 | // Created by morse on 2023/9/18. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct CustomizeKeyboardsConfiguration: Codable, Hashable {} 11 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Configuration/Models/HamsterPatchConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/3. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Hamster 配置补丁 11 | /// Hamster.custom.yaml 12 | public struct HamsterPatchConfiguration: Codable, Hashable, CustomStringConvertible { 13 | public var patch: HamsterConfiguration? 14 | } 15 | 16 | public extension HamsterPatchConfiguration { 17 | var description: String { 18 | "patch: \(patch as Optional)" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Configuration/Models/HapticIntensity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HapticIntensity.swift 3 | // 4 | // 5 | // Created by morse on 2023/6/30. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | /// 震动反馈强度 12 | public enum HapticIntensity: Int, CaseIterable, Hashable, Identifiable { 13 | public var id: Int { 14 | return rawValue 15 | } 16 | 17 | case softImpact = 0 18 | case lightImpact = 1 19 | case rigidImpact = 2 20 | case mediumImpact = 3 21 | case heavyImpact = 4 22 | 23 | public var text: String { 24 | switch self { 25 | case .softImpact: 26 | return "超轻" 27 | case .lightImpact: 28 | return "轻" 29 | case .rigidImpact: 30 | return "默认" 31 | case .mediumImpact: 32 | return "较强" 33 | case .heavyImpact: 34 | return "强" 35 | } 36 | } 37 | 38 | public func feedbackStyle() -> UIImpactFeedbackGenerator.FeedbackStyle { 39 | switch self { 40 | case .softImpact: 41 | return .soft 42 | case .lightImpact: 43 | return .light 44 | case .rigidImpact: 45 | return .rigid 46 | case .mediumImpact: 47 | return .medium 48 | case .heavyImpact: 49 | return .heavy 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Customize/自定义键盘.md: -------------------------------------------------------------------------------- 1 | # 自定义键盘 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Actions/DeleteBackwardRange.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeleteBackwardRange.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-05-06. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This enum can be used to vary how the backspace action will 13 | behave when pressing and holding the backspace key. 14 | 15 | 该枚举可用于改变按住退格键时退格操作的行为方式。 16 | */ 17 | public enum DeleteBackwardRange { 18 | /// Delete a single char at a time. 19 | /// 20 | /// 每次删除一个字符。 21 | case character 22 | 23 | /// Delete an entire word at a time. 24 | /// 25 | /// 每次删除整个单词。 26 | case word 27 | 28 | /// Delete an entire sentence at a time. 29 | /// 30 | /// 每次删除整个句子。 31 | case sentence 32 | } 33 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Actions/KeyboardAction+Autocomplete.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardAction+Autocomplete.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-03-18. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension KeyboardAction { 12 | /** 13 | Whether or not the action should apply currently active 14 | autocomplete suggestions where `isAutocomplete` is true. 15 | 16 | 当 `isAutocomplete` 为 true 时,是否应用当前活动的自动完成 suggestion。 17 | */ 18 | var shouldApplyAutocompleteSuggestion: Bool { 19 | switch self { 20 | case .character(let char): return char.isWordDelimiter 21 | case .primary(let type): return type.isSystemAction 22 | case .space: return true 23 | default: return false 24 | } 25 | } 26 | 27 | /** 28 | Whether or not the action should insert an autocomplete 29 | removed space. 30 | 31 | 该操作是否应插入自动完成删除的空格。 32 | */ 33 | var shouldReinsertAutocompleteInsertedSpace: Bool { 34 | shouldRemoveAutocompleteInsertedSpace 35 | } 36 | 37 | /** 38 | Whether or not the action should remove an autocomplete 39 | inserted space. 40 | 41 | 该操作是否应删除自动完成插入的空格。 42 | */ 43 | var shouldRemoveAutocompleteInsertedSpace: Bool { 44 | switch self { 45 | case .character(let char): return char.isWordDelimiter && self != .space 46 | default: return false 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Actions/KeyboardAction+InputCallout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardAction+InputCallout.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-09-30. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension KeyboardAction { 12 | /** 13 | The text that should be presented in a secondary action 14 | callout as users long press on the action. 15 | 16 | 当用户长按操作时,应在二级操作呼出中显示的文本。 17 | */ 18 | var inputCalloutText: String? { 19 | switch self { 20 | case .character(let char): return char 21 | case .emoji(let emoji): return emoji.char 22 | default: return nil 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Actions/KeyboardActionRows.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardAction+KeyboardActionRows.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2019-07-04. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This typealias represents a ``KeyboardActions`` array. 13 | 14 | 该类型别名代表一个 ``KeyboardActions`` 数组。 15 | 16 | The typealias makes it easier to create and handle keyboard 17 | action rows and collections. 18 | 19 | 通过类型别名,可以更轻松地创建和处理键盘操作的行和集合。 20 | */ 21 | public typealias KeyboardActionRows = [KeyboardActions] 22 | 23 | public extension KeyboardActionRows { 24 | /** 25 | Create keyboard action rows by mapping string arrays to 26 | a list of ``KeyboardAction/character(_:)`` actions. 27 | 28 | 通过将字符串数组映射到 ``KeyboardAction/character(_:)`` 列表,创建键盘操作行。 29 | */ 30 | init(characters: [[String]]) { 31 | self = characters.map { KeyboardActions(characters: $0) } 32 | } 33 | 34 | init(symbols: [[String]]) { 35 | self = symbols.map { KeyboardActions(symbols: $0) } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Appearance/KeyboardFontWeight.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/8/6. 6 | // 7 | 8 | import UIKit 9 | 10 | /** 11 | This enum defines supported keyboard font weights. 12 | 13 | 此枚举定义支持的键盘字体粗细。 14 | 15 | This type makes it possible to use fonts in `Codable` types. 16 | 17 | 这种类型使在 `Codable` 类型中使用字体成为可能。 18 | */ 19 | public enum KeyboardFontWeight: Codable, Equatable { 20 | case black 21 | case bold 22 | case heavy 23 | case light 24 | case medium 25 | case regular 26 | case semibold 27 | case thin 28 | case ultraLight 29 | } 30 | 31 | public extension KeyboardFontWeight { 32 | /// Get the native font weight for the weight. 33 | /// 34 | /// 获取系统字体Wight类型。 35 | var fontWeight: UIFont.Weight { 36 | switch self { 37 | case .black: return .black 38 | case .bold: return .bold 39 | case .heavy: return .heavy 40 | case .light: return .light 41 | case .medium: return .medium 42 | case .regular: return .regular 43 | case .semibold: return .semibold 44 | case .thin: return .thin 45 | case .ultraLight: return .ultraLight 46 | } 47 | } 48 | } 49 | 50 | public extension UIFont.Weight { 51 | var keyboardWeight: KeyboardFontWeight { 52 | switch self { 53 | case .black: return .black 54 | case .bold: return .bold 55 | case .heavy: return .heavy 56 | case .light: return .light 57 | case .medium: return .medium 58 | case .regular: return .regular 59 | case .semibold: return .semibold 60 | case .thin: return .thin 61 | case .ultraLight: return .ultraLight 62 | default: return .regular 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Appearance/NonStandardKeyboardStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClassifySymbolKeyboardStyle.swift 3 | // 4 | // 5 | // Created by morse on 2023/10/20. 6 | // 7 | 8 | import UIKit 9 | 10 | /// 非标准键盘样式(适用与分类符号键盘/Emoji键盘等) 11 | public struct NonStandardKeyboardStyle { 12 | /// 背景色 13 | public var backgroundColor: UIColor? 14 | 15 | /// 按下时背景色 16 | public var pressedBackgroundColor: UIColor? 17 | 18 | /// 前景色 19 | public var foregroundColor: UIColor? 20 | 21 | /// 按下时前景色 22 | public var pressedForegroundColor: UIColor 23 | 24 | /// 边框颜色 25 | public var borderColor: UIColor? 26 | 27 | /// 底部阴影颜色 28 | public var shadowColor: UIColor 29 | 30 | /// 圆角半径 31 | public var cornerRadius: CGFloat 32 | 33 | init( 34 | backgroundColor: UIColor? = nil, 35 | pressedBackgroundColor: UIColor? = nil, 36 | foregroundColor: UIColor? = nil, 37 | pressedForegroundColor: UIColor, 38 | borderColor: UIColor? = nil, 39 | shadowColor: UIColor, 40 | cornerRadius: CGFloat 41 | ) { 42 | self.backgroundColor = backgroundColor 43 | self.pressedBackgroundColor = pressedBackgroundColor 44 | self.foregroundColor = foregroundColor 45 | self.pressedForegroundColor = pressedForegroundColor 46 | self.borderColor = borderColor 47 | self.shadowColor = shadowColor 48 | self.cornerRadius = cornerRadius 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Autocomplete/AutocompleteSpaceState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutocompleteSpaceState.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-03-19. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This enum represents the state a text document proxy can be 13 | in, when inserting and removing spaces during autocomplete. 14 | 15 | 该枚举表示 ``textDocumentProxy`` 在自动完成过程中插入和删除空格时可能处于的状态。 16 | */ 17 | public enum AutocompleteSpaceState { 18 | /// This means that the proxy is not in a certain state. 19 | /// 20 | /// 这意味着代理不处于某种状态。 21 | case none 22 | 23 | /// This means that the proxy has an auto-inserted space. 24 | /// 25 | /// 这意味着代理有一个自动插入的空格。 26 | case autoInserted 27 | 28 | /// This means that the proxy has an auto-removed space. 29 | /// 30 | /// 这意味着代理有一个自动删除的空格。 31 | case autoRemoved 32 | } 33 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Autocomplete/DisabledAutocompleteProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisabledAutocompleteProvider.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-03-17. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This class is by default used as a placeholder autocomplete 13 | provider, until a real provider is injected. 14 | 15 | 该类默认用作自动完成提供程序的占位符,直到注入真正的提供程序。 16 | */ 17 | public class DisabledAutocompleteProvider: AutocompleteProvider { 18 | public init() {} 19 | 20 | public var locale: Locale = .current 21 | 22 | public func autocompleteSuggestions(for text: String, completion: (AutocompleteResult) -> Void) { 23 | completion(.success([])) 24 | } 25 | 26 | public var canIgnoreWords: Bool { false } 27 | public var canLearnWords: Bool { false } 28 | public var ignoredWords: [String] = [] 29 | public var learnedWords: [String] = [] 30 | 31 | public func hasIgnoredWord(_ word: String) -> Bool { false } 32 | public func hasLearnedWord(_ word: String) -> Bool { false } 33 | public func ignoreWord(_ word: String) {} 34 | public func learnWord(_ word: String) {} 35 | public func removeIgnoredWord(_ word: String) {} 36 | public func unlearnWord(_ word: String) {} 37 | } 38 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Callouts/CalloutActionProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalloutActionProvider.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-01-06. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This protocol can be implemented by any classes that can be 13 | used to get callout actions for a keyboard action. 14 | 15 | 该协议可以由任何可用于获取键盘操作的呼出操作的类来实现。 16 | */ 17 | public protocol CalloutActionProvider { 18 | /** 19 | Get callout actions for the provided `action`. 20 | 21 | 为提供的 `action` 获取呼出操作。 22 | 23 | These actions are presented in a callout when a user is 24 | long pressing this action. 25 | 26 | 当用户长按该操作时,这些操作会在呼出中显示。 27 | */ 28 | func calloutActions(for action: KeyboardAction) -> [KeyboardAction] 29 | } 30 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Callouts/Providers/DisabledCalloutActionProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisabledCalloutActionProvider.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-10-05. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This provider can be used to disable callout actions. 13 | 14 | 该提供程序可用于禁用呼出操作。 15 | */ 16 | public class DisabledCalloutActionProvider: CalloutActionProvider { 17 | public init() {} 18 | 19 | public func calloutActions( 20 | for action: KeyboardAction 21 | ) -> [KeyboardAction] { [] } 22 | } 23 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Casing/KeyboardCase+Button.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardCase+Button.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2020-07-01. 6 | // Copyright © 2020-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension KeyboardCase { 12 | /** 13 | The casing's standard button image. 14 | 15 | 键盘 Shift 按键不同状态对应的图像 16 | */ 17 | var standardButtonImage: UIImage { 18 | switch self { 19 | case .auto: return HamsterUIImage.shared.keyboardShiftLowercased 20 | case .capsLocked: return HamsterUIImage.shared.keyboardShiftCapslocked 21 | case .lowercased: return HamsterUIImage.shared.keyboardShiftLowercased 22 | case .uppercased: return HamsterUIImage.shared.keyboardShiftUppercased 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Device/DeviceType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceType.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2022-01-19. 6 | // Copyright © 2022-2023 Daniel Saidi. All rights reserved. 7 | // 8 | import UIKit 9 | 10 | /** 11 | This enum can be used to specify a device type. 12 | 13 | 该 enum 可用于指定设备类型。 14 | 15 | The static ``current`` property will resolve to the current 16 | device type. 17 | 18 | static ``current`` 属性将解析为当前设备类型。 19 | */ 20 | public enum DeviceType: String, CaseIterable, Equatable { 21 | case phone, pad, watch, mac, tv, other 22 | } 23 | 24 | public extension DeviceType { 25 | /** 26 | Get the current device type. 27 | 28 | 获取当前设备类型。 29 | */ 30 | static var current: DeviceType { 31 | UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad ? .pad : .phone 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Device/InterfaceOrientationResolver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InterfaceOrientationResolver.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2023-01-05. 6 | // Copyright © 2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /** 12 | This protocol can be implemented by any types that can find 13 | out the current interface orientation. 14 | 15 | 该协议可以由任何能够找到当前界面方向的类型来实现。 16 | 17 | This protocol is implemented by `UIScreen` in `UIKit`. 18 | */ 19 | protocol InterfaceOrientationResolver { 20 | /** 21 | Get the current interface orientation 22 | 23 | 获取当前界面方向 24 | */ 25 | var interfaceOrientation: InterfaceOrientation { get } 26 | } 27 | 28 | // MARK: - UIScreen 29 | 30 | extension UIScreen: InterfaceOrientationResolver {} 31 | 32 | extension UIScreen { 33 | /** 34 | Get the current interface orientation. 35 | 36 | 获取当前界面方向 37 | 38 | This is required since keyboard extensions cannot check 39 | the status bar style of the application. 40 | 41 | 由于键盘扩展应用无法检查应用程序的状态栏样式,因此需要这样做。 42 | */ 43 | var interfaceOrientation: InterfaceOrientation { 44 | // 转换为固定的坐标 45 | let point = coordinateSpace.convert(CGPoint.zero, to: fixedCoordinateSpace) 46 | switch (point.x, point.y) { 47 | case (0, 0): return .portrait 48 | case let (x, y) where x != 0 && y != 0: return .portraitUpsideDown 49 | case let (0, y) where y != 0: return .landscapeLeft 50 | case let (x, 0) where x != 0: return .landscapeRight 51 | default: return .unknown 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Emojis/Emoji.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Emoji.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-01-17. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /** 12 | This struct is just a wrapper around a single character. It 13 | can be used to get a little bit of type safety, and to work 14 | more structured with emojis. 15 | 16 | 该 struct 只是对单个字符的封装。它可以用来获得一点类型安全,并使表情符号的工作更有条理。 17 | */ 18 | public struct Emoji: Hashable, Codable, Identifiable { 19 | /** 20 | Create an emoji instance, using a certain emoji `char`. 21 | 22 | 使用某个表情符号 `char` 创建表情符号实例。 23 | */ 24 | public init(_ char: String) { 25 | self.char = char 26 | } 27 | 28 | /** 29 | The character that can be used to display the emoji. 30 | 31 | 可用于显示表情符号的字符。 32 | */ 33 | public let char: String 34 | } 35 | 36 | public extension Emoji { 37 | /** 38 | The emoji's unique identifier. 39 | 40 | 表情符号的唯一标识符。 41 | */ 42 | var id: String { char } 43 | } 44 | 45 | public extension Emoji { 46 | /** 47 | Get all emojis from all categories. 48 | 49 | 获取所有类别的所有表情符号。 50 | */ 51 | static var all: [Emoji] { 52 | EmojiCategory.all.flatMap { $0.emojis } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Emojis/EmojiProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmojiProvider.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-01-16. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This protocol can be implemented by any classes that can be 13 | used to get a list of emojis. 14 | 15 | 该协议可以由任何可用于获取表情符号 list 的类来实现。 16 | */ 17 | public protocol EmojiProvider { 18 | /** 19 | The emojis being returned by the provider. 20 | 21 | 返回的表情符号。 22 | */ 23 | var emojis: [Emoji] { get } 24 | } 25 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Emojis/FrequentEmojiProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FrequentEmojiProvider.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-01-16. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This protocol can be implemented by classes that can return 13 | a list of frequently used emojis. 14 | 15 | 该协议可以通过可以返回常用表情符号列表的类来实现。 16 | 17 | When using this protocol, you should trigger `registerEmoji` 18 | whenever a user selects an emoji, then use the registration 19 | to populate a frequent list that is returned by `emojis`. 20 | 21 | 使用此协议时,每当用户选择一个表情符号时, 都会触发 `registerEmoji`, 22 | 然后该协议的实现者会保存这个 emoji, 最终调用 EmojiProvider 协议的 `emojis` 返回的这些常用表情符号的 list。 23 | */ 24 | public protocol FrequentEmojiProvider: EmojiProvider { 25 | /** 26 | Register that an emoji has been used. This will be used 27 | to prepare the emojis that will be returned by `emojis`. 28 | 29 | 注册已使用的 emoji。注册的 emoji 将由 `emojis` 属性返回。 30 | */ 31 | func registerEmoji(_ emoji: Emoji) 32 | } 33 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Extensions/UIEdgeInsets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIEdgeInsets+Insets.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2020-12-02. 6 | // Copyright © 2020-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | import UIKit 11 | 12 | extension UIEdgeInsets { 13 | /** 14 | Create an `UIEdgeInsets` with the same insets everywhere. 15 | */ 16 | static func all(_ all: CGFloat) -> UIEdgeInsets { 17 | self.init(top: all, left: all, bottom: all, right: all) 18 | } 19 | 20 | /** 21 | Create an `UIEdgeInsets` with horizontal/vertical values. 22 | */ 23 | static func horizontal(_ horizontal: CGFloat, vertical: CGFloat) -> UIEdgeInsets { 24 | self.init(top: vertical, left: horizontal, bottom: vertical, right: horizontal) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Extensions/UIReturnKeyType+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIReturnKeyType+.swift 3 | // 4 | // 5 | // Created by morse on 2023/8/8. 6 | // 7 | 8 | import UIKit 9 | 10 | public extension UIReturnKeyType { 11 | /** 12 | The corresponding ``KeyboardReturnKeyType``. 13 | 14 | 对应自定义的 ``KeyboardReturnKeyType`` 类型 15 | 16 | Return types that have no matching primary type will be 17 | mapped to ``KeyboardReturnKeyType/custom(title:)``. 18 | 19 | 没有匹配的 Return 按键类型将被映射为 ``KeyboardReturnKeyType/custom(title:)``. 20 | */ 21 | var keyboardReturnKeyType: KeyboardReturnKeyType { 22 | switch self { 23 | case .default: return .return 24 | case .done: return .done 25 | case .go: return .go 26 | case .google: return .custom(title: "Google") 27 | case .join: return .join 28 | case .next: return .next 29 | case .route: return .custom(title: "route") 30 | case .search: return .search 31 | case .send: return .send 32 | case .yahoo: return .custom(title: "Yahoo") 33 | case .emergencyCall: return .custom(title: "emergencyCall") 34 | case .continue: return .custom(title: "continue") 35 | @unknown default: return .custom(title: "unknown") 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Feedback/AudioFeedbackEngine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AudioFeedbackEngine.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-10-15. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This protocol can be implemented by any classes that can be 13 | used to trigger audio feedback. 14 | 15 | 该协议可以由任何可用于触发音频反馈的类来实现。 16 | */ 17 | public protocol AudioFeedbackEngine { 18 | /** 19 | Trigger a certain audio feedback type. 20 | 21 | 触发某种音频反馈类型。 22 | **/ 23 | func trigger(_ audio: AudioFeedback) 24 | } 25 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Feedback/HapticFeedbackEngine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HapticFeedbackEngine.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-04-01. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This protocol can be implemented by any classes that can be 13 | used to prepare and trigger haptic feedback. 14 | 15 | 该协议可以由任何可用于准备和触发触觉反馈的类来实现。 16 | */ 17 | public protocol HapticFeedbackEngine { 18 | /** 19 | Prepare a certain haptic feedback type. 20 | 21 | 准备某种触觉反馈类型。 22 | */ 23 | func prepare(_ feedback: HapticFeedback) 24 | 25 | /** 26 | Trigger a certain haptic feedback type. 27 | 28 | 触发某种触觉反馈类型。 29 | */ 30 | func trigger(_ feedback: HapticFeedback) 31 | } 32 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Feedback/HapticIntensity+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HapticIntensity+.swift 3 | // 4 | // 5 | // Created by morse on 2023/8/7. 6 | // 7 | 8 | import Foundation 9 | 10 | public extension HapticIntensity { 11 | func hapticFeedback() -> HapticFeedback { 12 | switch self { 13 | case .softImpact: 14 | return .softImpact 15 | case .lightImpact: 16 | return .lightImpact 17 | case .rigidImpact: 18 | return .rigidImpact 19 | case .mediumImpact: 20 | return .mediumImpact 21 | case .heavyImpact: 22 | return .heavyImpact 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Feedback/StandardAudioFeedbackEngine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StandardAudioFeedbackEngine.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2019-10-15. 6 | // Copyright © 2019-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import AudioToolbox 10 | 11 | /** 12 | This engine uses system features to trigger audio feedbacks. 13 | It is the default ``AudioFeedback/engine`` on all platforms 14 | where it's supported. 15 | 16 | 该引擎使用系统功能触发音频反馈。 17 | 在所有支持该引擎的平台上,它都是默认的 ``AudioFeedback/engine`` 引擎。 18 | 19 | You can use, modify and replace the ``shared`` engine. This 20 | lets you customize the global audio feedback experience. 21 | 22 | 您可以使用、修改和替换 "shared" 引擎。这可以让你定制全局音频反馈。 23 | 24 | Note that the engine is currently only supported on certain 25 | platforms. 26 | 27 | 请注意,该引擎目前仅支持某些平台。 28 | */ 29 | open class StandardAudioFeedbackEngine: AudioFeedbackEngine { 30 | public init() {} 31 | 32 | /** 33 | Trigger a certain audio feedback type. 34 | 35 | 触发某种音频反馈类型。 36 | **/ 37 | open func trigger(_ audio: AudioFeedback) { 38 | switch audio { 39 | case .none: return 40 | default: 41 | DispatchQueue.global().async { 42 | AudioServicesPlaySystemSound(audio.id) 43 | } 44 | } 45 | } 46 | } 47 | 48 | public extension StandardAudioFeedbackEngine { 49 | /** 50 | A shared instance that can be used from anywhere. 51 | 52 | 可在任何地方使用的共享实例。 53 | */ 54 | static var shared = StandardAudioFeedbackEngine() 55 | } 56 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Gestures/DragGestureHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardGesture.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-04-01. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | 11 | /** 12 | This protocol can be implemented by classes that can handle 13 | drag gestures from a start position to a current one. 14 | 15 | 该协议可以由能够处理从起始位置到当前位置的拖动手势的类来实现。 16 | */ 17 | public protocol DragGestureHandler { 18 | /** 19 | Handle drag gestures from a start to a current location. 20 | 21 | 处理从起始位置到当前位置的拖动手势。 22 | */ 23 | func handleDragGesture(from startLocation: CGPoint, to currentLocation: CGPoint) 24 | } 25 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Gestures/GestureButtonDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GestureButtonDefaults.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2022-11-24. 6 | // Copyright © 2022-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | 该结构体可用于配置手势的默认值。 13 | */ 14 | public enum GestureButtonDefaults { 15 | /// The max time between two taps for them to count as a double tap, by default `0.2`. 16 | /// 17 | /// 双击的两次点击之间最大间隔时间,默认为 `0.2`。单位:秒 18 | public static var doubleTapTimeout = 0.2 19 | 20 | /// The time it takes for a press to count as a long press, by default `0.5`. 21 | /// 22 | /// 识别长按所需的时间,默认为 `0.5`。 23 | public static var longPressDelay = 0.5 24 | 25 | /// The time it takes for a press to count as a repeat trigger, by default `0.5`. 26 | /// 27 | /// 重复触发的所需时间,默认为 `0.5`。 28 | public static var repeatDelay = 0.5 29 | } 30 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Gestures/SpaceDragSensitivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceDragSensitivity.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-01-10. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This enum can be used to change the drag sensitivity of the 13 | spacebar. 14 | 15 | 该枚举可用于更改空格键的拖动灵敏度。 16 | 17 | `NOTE` The sensitivity value corresponds to how many points 18 | a spacebar must be dragged for the input cursor to move one 19 | step. This means that the sensitivity is `inverted`. Higher 20 | values mean that the cursor moves less. 21 | 22 | `NOTE` 灵敏度值指必须拖动空格键多少 point 才能移动屏幕上的输入光标一步。 23 | 这意味着灵敏度是“颠倒的”。值越高意味着光标移动的越少。 24 | */ 25 | public enum SpaceDragSensitivity: Codable, Identifiable { 26 | case low, medium, high, custom(points: Int) 27 | } 28 | 29 | public extension SpaceDragSensitivity { 30 | var id: Int { points } 31 | 32 | var points: Int { 33 | switch self { 34 | case .low: return 10 35 | case .medium: return 5 36 | case .high: return 2 37 | case .custom(let points): return points 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Gestures/SpaceLongPressBehavior.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceLongPressBehavior.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2023-02-21. 6 | // Copyright © 2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This enum defines various space key long press actions. 13 | 14 | 该枚举定义了各种空格键长按操作。 15 | */ 16 | public enum SpaceLongPressBehavior: Codable { 17 | /// Long pressing space starts moving the input cursor. 18 | /// 19 | /// 长按空格键开始移动输入光标。 20 | /// 21 | /// This is the default behavior in native iOS keyboards. 22 | /// 这是 iOS 原生键盘的默认行为。 23 | case moveInputCursor 24 | 25 | /// Long pressing space opens a locale context menu. 26 | /// 27 | /// 长按空格键可打开本地化上下文菜单。 28 | /// 29 | /// Only use this when you think that really makes sense. 30 | /// Long pressing space to start moving the input cursor 31 | /// is the default and expected behavior. 32 | /// 33 | /// 只有当你认为这样做有意义时才会使用。长按空格开始移动输入光标是默认的预期行为。 34 | case openLocaleContextMenu 35 | } 36 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Input/InputSetProviderBased.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputSetProviderBased.swift 3 | // KeyboardKit 4 | // 5 | // 6 | // Created by Daniel Saidi on 2022-12-29. 7 | // Copyright © 2022-2023 Daniel Saidi. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | 12 | /** 13 | This protocol is implemented by services that may depend on 14 | an ``InputSetProvider`` and must be reconfigured when a new 15 | input set provider is being used. 16 | 17 | 该协议由可能依赖于 ``InputSetProvider`` 的服务实现, 18 | 并且在使用新的 InputSetProvider 时必须重新配置。 19 | 20 | > Note: This will no longer be needed when the library uses 21 | keyboard layout providers that use their own providers. The 22 | context provider can then be removed and this as well. 23 | 24 | > Note:当库使用的 KeyboardLayoutProvider 是自己实现的 Provider 时,将不再需要此功能。 25 | > 这样就可以移除上下文提供程序,也可以移除此程序。 26 | */ 27 | public protocol InputSetProviderBased { 28 | /** 29 | Register a new input set provider. 30 | */ 31 | func register(inputSetProvider: InputSetProvider) 32 | } 33 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Input/InputSetProviderProxy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/9/2. 6 | // 7 | 8 | import Foundation 9 | 10 | /// InputSetProvider 协议代理,支持根据 keyboardContext 切换不同的 InputSetProvider 11 | public protocol InputSetProviderProxy: InputSetProvider { 12 | func provider(for context: KeyboardContext) -> InputSetProvider 13 | } 14 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Input/InputSetRows.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputSetRows.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-02-03. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This typealias represents a list of input set rows. 13 | 14 | 该类型别名表示 InputSetRow 的列表。 15 | */ 16 | public typealias InputSetRows = [InputSetRow] 17 | 18 | public extension InputSetRows { 19 | /** 20 | Get all input characters for a certain keyboard case. 21 | 22 | 获取特定键盘状态下的所有输入字符。 23 | */ 24 | func characters(for case: KeyboardCase = .lowercased) -> [[String]] { 25 | map { $0.characters(for: `case`) } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Keyboard/KeyboardAutocapitalizationType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardAutocapitalizationType.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2023-03-26. 6 | // Copyright © 2020-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /** 12 | This enum defines all supported auto-capitalization types. 13 | 14 | 该枚举定义了所有支持的自动大写类型。 15 | */ 16 | public enum KeyboardAutocapitalizationType: String, CaseIterable { 17 | /// All characters should be auto-capitalized. 18 | /// 19 | /// 所有字符均应自动大写。 20 | case allCharacters 21 | 22 | /// All new sentences should be auto-capitalized. 23 | /// 24 | /// 所有新句子都应自动大写。 25 | case sentences 26 | 27 | /// All new words should be auto-capitalized. 28 | /// 29 | /// 所有新单词都应自动大写。 30 | case words 31 | 32 | /// Auto-capitalization should not be applied. 33 | /// 34 | /// 不应使用自动大写。 35 | case none 36 | } 37 | 38 | public extension UITextAutocapitalizationType { 39 | /** 40 | Get the KeyboardKit-specific auto-capitalization type. 41 | 42 | 获取特定于 KeyboardKit 的自动大写类型。 43 | */ 44 | var keyboardType: KeyboardAutocapitalizationType { 45 | switch self { 46 | case .none: return .none 47 | case .words: return .words 48 | case .sentences: return .sentences 49 | case .allCharacters: return .allCharacters 50 | @unknown default: return .none 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Keyboard/NextKeyboardController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NextKeyboardController.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2023-01-25. 6 | // Copyright © 2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /** 12 | This class is used as global state for next keyboard button 13 | views, since they need an input view controller to function. 14 | 15 | 该类用作下一个键盘按钮视图的全局状态,因为它们需要 InputViewController 才能运行。 16 | 17 | The KeyboardKit-specific ``KeyboardInputViewController`` is 18 | automatically setting itself to the shared instance when it 19 | is loaded in `viewDidLoad`. 20 | 21 | 特定于 KeyboardKit 的 ``KeyboardInputViewController`` 22 | 在 `viewDidLoad` 中加载时会自动将 self 设置为共享实例。 23 | */ 24 | public final class NextKeyboardController { 25 | private init() {} 26 | 27 | public weak static var shared: UIInputViewController? 28 | } 29 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Daniel Saidi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Layout/KeyboardLayoutItemSize.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardLayoutItemSize.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-02-03. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | 11 | /** 12 | This struct provides the size of a keyboard layout item. It 13 | has a regular height, but a declarative width. 14 | 15 | 该结构提供了键盘布局 item 的尺寸。 16 | 它有一个常规高度,但有一个声明性的宽度。 17 | */ 18 | public struct KeyboardLayoutItemSize: Equatable { 19 | /** 20 | Create a new layout item size. 21 | 22 | 创建新的布局 item 尺寸。 23 | 24 | - Parameters: 25 | - width: The declarative width of the item. 26 | item 的声明性宽度。 27 | - height: The fixed height of the item. 28 | item 的固定高度 29 | */ 30 | public init( 31 | width: KeyboardLayoutItemWidth, 32 | height: CGFloat 33 | ) { 34 | self.width = width 35 | self.height = height 36 | } 37 | 38 | /** 39 | The declarative width of the item. 40 | 41 | item 的声明性宽度。 42 | */ 43 | public var width: KeyboardLayoutItemWidth 44 | 45 | /** 46 | The fixed height of the item. 47 | 48 | item 的固定高度。 49 | */ 50 | public var height: CGFloat 51 | } 52 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Layout/KeyboardLayoutProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardLayoutProvider.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2020-12-01. 6 | // Copyright © 2020-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This protocol can be implemented by any classes that can be 13 | used to generate a ``KeyboardLayout`` for a certain context. 14 | 15 | 该协议可由任何可用于为特定上下文生成 ``KeyboardLayout`` 的类实现。 16 | 17 | KeyboardKit will create a ``StandardKeyboardLayoutProvider`` 18 | instance when the keyboard extension is started, then apply 19 | it to ``KeyboardInputViewController/keyboardLayoutProvider`` 20 | and use it by default when generating keyboard layouts. 21 | 22 | 键盘扩展启动时,KeyboardKit 将创建一个 ``StandardKeyboardLayoutProvider`` 实例, 23 | 然后将其应用到 ``KeyboardInputViewController/keyboardLayoutProvider`` 中, 24 | 并在生成键盘布局时默认使用该实例。 25 | 26 | You can create a custom implementation of this protocol, by 27 | inheriting and customizing the standard class or creating a 28 | new implementation from scratch. When you're implementation 29 | is ready, just replace the controller service with your own 30 | implementation to make the library use it instead. 31 | 32 | 您可以通过继承和定制标准类或从头开始创建一个新的实现来创建该协议的自定义实现。 33 | 当你的实现准备就绪时,只需用你自己的实现替换 controler 中的服务,就能让程序库使用它。 34 | */ 35 | public protocol KeyboardLayoutProvider: AnyObject, InputSetProviderBased { 36 | /** 37 | The layout keyboard to use for a given keyboard context. 38 | */ 39 | func keyboardLayout(for context: KeyboardContext) -> KeyboardLayout 40 | } 41 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Layout/KeyboardLayoutProviderProxy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceKeyboardLayoutProvider.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-02-16. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This protocol extends ``KeyboardLayoutProvider`` with a way 13 | for a layout provider to resolve various providers based on 14 | a ``KeyboardContext`` instance. 15 | 16 | 此协议扩展了 ``KeyboardLayoutProvider``, 17 | 为布局 provider 提供了一种根据 ``KeyboardContext``实例解析各种 provider 的方法。 18 | 19 | This is for instance used to let a single provider use many 20 | nested providers and select one depending on the context. 21 | 22 | 例如,这可以让单个 KeyboardLayoutProvider 实例使用多个嵌套 provider,并根据上下文选择其中一个。 23 | */ 24 | public protocol KeyboardLayoutProviderProxy: KeyboardLayoutProvider { 25 | /** 26 | The keyboard layout provider to use for a given context. 27 | 28 | 在给定上下文中使用的键盘布局提供程序。 29 | */ 30 | func keyboardLayoutProvider(for context: KeyboardContext) -> KeyboardLayoutProvider 31 | } 32 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Layout/KeyboardRowItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardRowItem.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-05-08. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This protocol can be implemented by types that represent an 13 | item in a kind of row, such as input sets, layout items etc. 14 | 15 | 该协议可以通过表示某行中 item 的类型来实现,如 InputSetItem、KeyboardLayoutItem 等。 16 | 17 | The reason for having this protocol is mainly to have a way 18 | to share functionality. It is implemented by ``InputSetItem`` 19 | and ``KeyboardLayoutItem`` and provide collection extension 20 | functions in `KeyboardRowItem+Collection`. 21 | 22 | 制定此协议的原因主要是为了共享功能。 23 | 它由 ``InputSetItem`` 和 ``KeyboardLayoutItem`` 实现,并在 `KeyboardRowItem+Collection` 中提供集合扩展函数。 24 | 25 | The reason to why not using `Identifiable` instead, is that 26 | the row ID may not be unique. The same item may appear many 27 | times in the same row. 28 | 29 | 不使用 `Identifiable` 的原因是,rowId 可能不是唯一的。 30 | 同一 item 可能会在同一行中出现多次。 31 | */ 32 | public protocol KeyboardRowItem { 33 | associatedtype ID: Equatable 34 | 35 | /** 36 | An ID that identifies the item in a row. Note that this 37 | is not necessarily unique. 38 | 39 | 标识行中 item 的 ID。 40 | 请注意,这不一定是唯一的。 41 | */ 42 | var rowId: ID { get } 43 | } 44 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Navigation/KeyboardUrlOpener.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardUrlOpener.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2023-05-29. 6 | // Copyright © 2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This class can be used to open URLs from a keyboard without 13 | having to use `UIApplication`. 14 | 15 | 该类可用于通过键盘打开 URL,而无需使用 `UIApplication`。 16 | 17 | You can use ``KeyboardUrlOpener/shared`` to avoid having to 18 | create custom instances. Note that you have to manually set 19 | the ``controller`` when you create a custom opener or use a 20 | custom controller. 21 | 22 | 您可以使用 ``KeyboardUrlOpener/shared`` 来避免创建自定义实例。 23 | 请注意,在创建自定义 opener 或使用自定义 controller 时,必须手动设置属性 ``controller`` 。 24 | */ 25 | public class KeyboardUrlOpener { 26 | public init() {} 27 | 28 | public enum UrlError: Error { 29 | case nilUrl, noKeyboardController 30 | } 31 | 32 | /// A shared url opener. 33 | public static let shared = KeyboardUrlOpener() 34 | 35 | /// The controller to use to open the URL. 36 | public unowned var controller: KeyboardController? 37 | 38 | /// Open a custom URL. 39 | public func open(_ url: URL?) throws { 40 | guard let controller else { throw UrlError.noKeyboardController } 41 | controller.openUrl(url) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Previews/Keyboard+Preview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Keyboard+Preview.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-01-28. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension KeyboardInputViewController { 12 | /** 13 | This preview controller can be used in SwiftUI/UIKit previews. 14 | 15 | 该预览控制器可用于 SwiftUI/UIKit 预览。 16 | */ 17 | static var preview: KeyboardInputViewController { 18 | KeyboardInputViewController() 19 | } 20 | } 21 | 22 | public extension KeyboardContext { 23 | /** 24 | This preview context can be used in SwiftUI/UIKit previews. 25 | 26 | 此预览上下文可用于 SwiftUI/UIKit 预览。 27 | */ 28 | static var preview: KeyboardContext { 29 | KeyboardContext(controller: KeyboardInputViewController.preview) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Previews/KeyboardActionHandler+Preview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Actions+Preview.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-01-25. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | 11 | public extension KeyboardActionHandler where Self == PreviewKeyboardActionHandler { 12 | /** 13 | This preview handler can be used in SwiftUI previews. 14 | 15 | 该预览处理程序可用于 SwiftUI 预览。 16 | */ 17 | static var preview: KeyboardActionHandler { PreviewKeyboardActionHandler() } 18 | } 19 | 20 | /** 21 | This action handler can be used in SwiftUI previews. 22 | 23 | 此操作处理程序可在 SwiftUI 预览中使用。 24 | */ 25 | public class PreviewKeyboardActionHandler: KeyboardActionHandler { 26 | public init() {} 27 | 28 | public func canHandle(_ gesture: KeyboardGesture, on action: KeyboardAction) -> Bool { false } 29 | public func handle(_ action: KeyboardAction) {} 30 | public func handle(_ gesture: KeyboardGesture, on action: KeyboardAction) {} 31 | public func handle(_ gesture: KeyboardGesture, on key: Key) {} 32 | public func handleDrag(on action: KeyboardAction, from startLocation: CGPoint, to currentLocation: CGPoint) {} 33 | } 34 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Previews/UITextDocumentProxy+Preview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Proxy+Preview.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2021-01-25. 6 | // Copyright © 2021-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UITextDocumentProxy where Self == PreviewTextDocumentProxy { 12 | /** 13 | This preview proxy can be used in SwiftUI previews. 14 | 15 | 该预览代理可用于 SwiftUI 预览。 16 | */ 17 | static var preview: UITextDocumentProxy { PreviewTextDocumentProxy() } 18 | } 19 | 20 | /** 21 | This document proxy can be used in SwiftUI previews. 22 | 23 | 此文档代理可用于 SwiftUI 预览。 24 | */ 25 | public class PreviewTextDocumentProxy: NSObject, UITextDocumentProxy { 26 | override public init() { 27 | super.init() 28 | } 29 | 30 | // 自动大写类型 31 | public var autocapitalizationType: UITextAutocapitalizationType = .none 32 | public var documentContextBeforeInput: String? 33 | public var documentContextAfterInput: String? 34 | public var hasText: Bool = false 35 | public var selectedText: String? 36 | public var documentInputMode: UITextInputMode? 37 | public var documentIdentifier: UUID = .init() 38 | public var returnKeyType: UIReturnKeyType = .default 39 | 40 | public func adjustTextPosition(byCharacterOffset offset: Int) {} 41 | public func deleteBackward() {} 42 | public func insertText(_ text: String) {} 43 | public func setMarkedText(_ markedText: String, selectedRange: NSRange) {} 44 | public func unmarkText() {} 45 | } 46 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/README.md: -------------------------------------------------------------------------------- 1 | ## KeyboardKit 改造 2 | 3 | 基于 KeyboardKit 7.8.0 改造。 4 | 5 | 1. 替换了 KeyboardKit SwiftUI View,使用 UIKit 布局键盘的UI。 6 | 2. 增加个性化的 KeyboardAction, KeyboardGesture,以适合中文键盘的使用。 7 | 3. 增加了中文键盘 LayoutProvider,如中文标准26键,中文九宫格,自定义键盘。 8 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Settings/KeyboardSettingsUrlProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardSettingsUrlProvider.swift 3 | // KeyboardKit 4 | // 5 | // Created by Daniel Saidi on 2020-03-19. 6 | // Copyright © 2020-2023 Daniel Saidi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /** 12 | This protocol can be implemented by any type that should be 13 | able to resolve a URL to an app's keyboard settings page in 14 | System Settings. 15 | 16 | 此协议可以由任何能够解析系统设置中应用程序键盘设置页面 URL 的类型来实现。 17 | 18 | This protocol is implemented by `URL`. 19 | 20 | 该协议由 `URL` 实现。 21 | */ 22 | public protocol KeyboardSettingsUrlProvider {} 23 | 24 | public extension KeyboardSettingsUrlProvider { 25 | /** 26 | The url to the app's settings screen in System Settings. 27 | 28 | If the app has no custom settings screen, this url will 29 | open the main system settings screen. 30 | */ 31 | static var keyboardSettings: URL? { 32 | URL(string: UIApplication.openSettingsURLString) 33 | } 34 | } 35 | 36 | extension URL: KeyboardSettingsUrlProvider {} 37 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Symbol/FrequentSymbolProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FrequentSymbolProvider.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/5/30. 6 | // 7 | 8 | import Foundation 9 | 10 | /// 常用符号 11 | protocol FrequentSymbolProvider: SymbolProvider { 12 | func registerSymbol(_ symbol: Symbol) 13 | 14 | func reset() 15 | } 16 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Symbol/MostRecentSymbolProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MostRecentSymbolProvider.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/5/30. 6 | // 7 | 8 | import Foundation 9 | import HamsterKit 10 | 11 | public class MostRecentSymbolProvider: FrequentSymbolProvider { 12 | public init( 13 | maxCount: Int = 30, 14 | defaults: UserDefaults = .hamster 15 | ) { 16 | self.maxCount = maxCount 17 | self.defaults = defaults 18 | } 19 | 20 | private let defaults: UserDefaults 21 | private let maxCount: Int 22 | public static let key = "com.ihsiao.app.hamster.keyboard.mostRecentSymbolProvider.symbol" 23 | private static let common = [",", "。", "?", "!"] 24 | 25 | var symbols: [Symbol] { 26 | symbolChars.map { Symbol(char: $0) } 27 | } 28 | 29 | var symbolChars: [String] { 30 | defaults.stringArray(forKey: Self.key) ?? Self.common 31 | } 32 | 33 | func registerSymbol(_ symbol: Symbol) { 34 | var symbols = self.symbols.filter { $0.char != symbol.char } 35 | symbols.insert(symbol, at: 0) 36 | let result = Array(symbols.prefix(maxCount)) 37 | let chars = result.map { $0.char } 38 | defaults.set(chars, forKey: Self.key) 39 | } 40 | 41 | public func reset() { 42 | defaults.set(Self.common, forKey: Self.key) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Symbol/Symbol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Symbol.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/5/30. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct Symbol: Codable, Identifiable, Hashable { 11 | public var id: UUID 12 | public var char: String 13 | 14 | public init(id: UUID = UUID(), char: String) { 15 | self.id = id 16 | self.char = char 17 | } 18 | } 19 | 20 | extension Symbol { 21 | static var all: [Symbol] { 22 | SymbolCategory.all.flatMap { $0.symbols } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/KeyboardKit/Symbol/SymbolProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SymbolProvider.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/5/30. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol SymbolProvider { 11 | /** 12 | The symbols being returned by the provider. 13 | */ 14 | var symbols: [Symbol] { get } 15 | } 16 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Preview Helpers/UIViewControllerPreview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewControllerPreview.swift 3 | // 4 | // 5 | // Created by morse on 2023/8/10. 6 | // 7 | 8 | import UIKit 9 | 10 | #if canImport(SwiftUI) && DEBUG 11 | import SwiftUI 12 | 13 | struct UIViewControllerPreview: UIViewControllerRepresentable { 14 | func updateUIViewController(_ uiViewController: ViewController, context: Context) {} 15 | 16 | let viewController: ViewController 17 | 18 | init(_ builder: @escaping () -> ViewController) { 19 | viewController = builder() 20 | } 21 | 22 | // MARK: - UIViewControllerRepresentable 23 | 24 | func makeUIViewController(context: Context) -> ViewController { 25 | viewController 26 | } 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Preview Helpers/UIViewPreview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewPreview.swift 3 | // 4 | // 5 | // Created by morse on 2023/8/10. 6 | // 7 | 8 | import UIKit 9 | 10 | #if canImport(SwiftUI) && DEBUG 11 | import SwiftUI 12 | 13 | struct UIViewPreview: UIViewRepresentable { 14 | let view: View 15 | 16 | init(_ builder: @escaping () -> View) { 17 | view = builder() 18 | } 19 | 20 | // MARK: UIViewRepresentable 21 | 22 | func makeUIView(context: Context) -> UIView { 23 | return view 24 | } 25 | 26 | func updateUIView(_ view: UIView, context: Context) { 27 | view.setContentHuggingPriority(.defaultHigh, for: .horizontal) 28 | view.setContentHuggingPriority(.defaultHigh, for: .vertical) 29 | } 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardButtonBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFF" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x6B", 27 | "green" : "0x6B", 28 | "red" : "0x6B" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardButtonBackgroundForColorSchemeBug.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "1.000", 9 | "green" : "1.000", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "0.300", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardButtonBackgroundForDarkAppearance.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x97", 9 | "green" : "0x97", 10 | "red" : "0x97" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardButtonForeground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "platform" : "ios", 6 | "reference" : "labelColor" 7 | }, 8 | "idiom" : "universal" 9 | }, 10 | { 11 | "appearances" : [ 12 | { 13 | "appearance" : "luminosity", 14 | "value" : "dark" 15 | } 16 | ], 17 | "color" : { 18 | "platform" : "ios", 19 | "reference" : "labelColor" 20 | }, 21 | "idiom" : "universal" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardButtonForegroundForDarkAppearance.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFF" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "platform" : "ios", 24 | "reference" : "labelColor" 25 | }, 26 | "idiom" : "universal" 27 | } 28 | ], 29 | "info" : { 30 | "author" : "xcode", 31 | "version" : 1 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardButtonShadow.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "0.300", 8 | "blue" : "0x00", 9 | "green" : "0x00", 10 | "red" : "0x00" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "0.700", 26 | "blue" : "0x00", 27 | "green" : "0x00", 28 | "red" : "0x00" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardDarkButtonBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xBA", 9 | "green" : "0xB1", 10 | "red" : "0xAB" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x47", 27 | "green" : "0x47", 28 | "red" : "0x47" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardDarkButtonBackgroundForColorSchemeBug.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.753", 9 | "green" : "0.718", 10 | "red" : "0.702" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "0.100", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardDarkButtonBackgroundForDarkAppearance.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x75", 9 | "green" : "0x75", 10 | "red" : "0x75" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardKeyboardBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.867", 9 | "green" : "0.839", 10 | "red" : "0.835" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.173", 27 | "green" : "0.173", 28 | "red" : "0.173" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Colors.xcassets/standardKeyboardBackgroundForDarkAppearance.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.416", 9 | "green" : "0.416", 10 | "red" : "0.416" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.173", 27 | "green" : "0.173", 28 | "red" : "0.173" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/chineseState.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "cn.pdf", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "filename" : "cnDark.pdf", 15 | "idiom" : "universal" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | }, 22 | "properties" : { 23 | "preserves-vector-representation" : true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/chineseState.imageset/cn.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/chineseState.imageset/cn.pdf -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/chineseState.imageset/cnDark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/chineseState.imageset/cnDark.pdf -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/englishState.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "en.pdf", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "filename" : "enDark.pdf", 15 | "idiom" : "universal" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/englishState.imageset/en.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/englishState.imageset/en.pdf -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/englishState.imageset/enDark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/englishState.imageset/enDark.pdf -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/keyboardEmoji.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "keyboard-emoji.pdf", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "filename" : "keyboard-emoji-dark.pdf", 15 | "idiom" : "universal" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | }, 22 | "properties" : { 23 | "preserves-vector-representation" : true, 24 | "template-rendering-intent" : "template" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/keyboardEmoji.imageset/keyboard-emoji-dark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/keyboardEmoji.imageset/keyboard-emoji-dark.pdf -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/keyboardEmoji.imageset/keyboard-emoji.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imfuxiao/Hamster/65693706d01fc6c19ed6071968e542b0a2ef3f36/Packages/HamsterKeyboardKit/Sources/Resources/Images.xcassets/keyboardEmoji.imageset/keyboard-emoji.pdf -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | KeyboardKit 4 | 5 | Created by Daniel Saidi on 2021-02-15. 6 | Copyright © 2021 Daniel Saidi. All rights reserved. 7 | */ 8 | 9 | "locale" = "en"; 10 | 11 | "done" = "done"; 12 | "go" = "go"; 13 | "join" = "join"; 14 | "next" = "next"; 15 | "ok" = "OK"; 16 | "return" = "return"; 17 | "search" = "search"; 18 | "send" = "send"; 19 | "space" = "space"; 20 | 21 | "keyboardTypeAlphabetic" = "ABC"; 22 | "keyboardTypeNumeric" = "123"; 23 | "keyboardTypeSymbolic" = "#+="; 24 | 25 | "searchEmoji" = "Search Emoji"; 26 | 27 | // 常用符号 28 | "frequent" = "Frequent"; 29 | // 英文符号 30 | "ascii" = "English"; 31 | // 中文符号 32 | "cn" = "Chinese"; 33 | // 数学符号 34 | "math" = "Math"; 35 | // 特殊符号 36 | "special" = "Special"; 37 | // 单位 38 | "unit" = "Unit"; 39 | // 列表 40 | "list" = "List"; 41 | // 拼音 42 | "pinyin" = "Pinyin"; 43 | // 注音 44 | "bopomofo" = "Bopomofo"; 45 | // 部首 46 | "radical" = "Radical"; 47 | // 希腊 48 | "grease" = "Greek"; 49 | // 俄语 50 | "rusa" = "Rus"; 51 | // 拉丁 52 | "lation" = "Lation"; 53 | // 韩文 54 | "korea" = "Korea"; 55 | // 音标 56 | "phonetic" = "IPA"; 57 | // 颜文字 58 | "kaomoji" = "Kaomoji"; 59 | // 假名 60 | "jp" = "Katakana"; 61 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | Hamster 4 | 5 | Created by morse on 11/4/2023. 6 | 7 | */ 8 | "locale" = "zh-Hans"; 9 | 10 | "done" = "完成"; 11 | "go" = "前往"; 12 | "join" = "加入"; 13 | "ok" = "确定"; 14 | "return" = "换行"; 15 | "search" = "搜索"; 16 | "space" = "空格"; 17 | "send" = "发送"; 18 | "next" = "next"; 19 | 20 | "keyboardTypeAlphabetic" = "英"; 21 | "keyboardTypeNumeric" = "123"; 22 | "keyboardTypeSymbolic" = "#+="; 23 | 24 | "keyboard.action.switchInputSchema" = "#方案切换"; 25 | 26 | // 常用符号 27 | "frequent" = "常用"; 28 | // 英文符号 29 | "ascii" = "英文"; 30 | // 中文符号 31 | "cn" = "中文"; 32 | // 数学符号 33 | "math" = "数学"; 34 | // 特殊符号 35 | "special" = "特殊"; 36 | // 单位 37 | "unit" = "单位"; 38 | // 列表 39 | "list" = "列表"; 40 | // 拼音 41 | "pinyin" = "拼音"; 42 | // 注音 43 | "bopomofo" = "注音"; 44 | // 部首 45 | "radical" = "部首"; 46 | // 希腊 47 | "grease" = "希腊"; 48 | // 俄语 49 | "rusa" = "俄文"; 50 | // 拉丁 51 | "lation" = "拉丁"; 52 | // 韩文 53 | "korea" = "韩文"; 54 | // 音标 55 | "phonetic" = "音标"; 56 | // 颜文字 57 | "kaomoji" = "颜文字"; 58 | // 假名 59 | "jp" = "片假"; 60 | 61 | "searchEmoji" = "搜索Emoji"; 62 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/Resources/zh-Hant.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | Hamster 4 | 5 | Created by morse on 11/4/2023. 6 | 7 | */ 8 | "locale" = "zh-Hant"; 9 | 10 | "done" = "完成"; 11 | "go" = "前往"; 12 | "join" = "加入"; 13 | "ok" = "確認"; 14 | "return" = "換行"; 15 | "search" = "搜尋"; 16 | "space" = "空格"; 17 | "send" = "發送"; 18 | "next" = "next"; 19 | 20 | "keyboardTypeAlphabetic" = "英"; 21 | "keyboardTypeNumeric" = "123"; 22 | "keyboardTypeSymbolic" = "#+="; 23 | 24 | "keyboard.action.switchInputSchema" = "#方案切換"; 25 | 26 | // 常用符號 27 | "frequent" = "常用"; 28 | // 英文符號 29 | "ascii" = "英文"; 30 | // 中文符號 31 | "cn" = "中文"; 32 | // 數學符號 33 | "math" = "數學"; 34 | // 特殊符號 35 | "special" = "特殊"; 36 | // 單位 37 | "unit" = "單位"; 38 | // 列表 39 | "list" = "列表"; 40 | // 拼音 41 | "pinyin" = "拼音"; 42 | // 注音 43 | "bopomofo" = "注音"; 44 | // 部首 45 | "radical" = "部首"; 46 | // 希臘 47 | "grease" = "希臘"; 48 | // 俄語 49 | "rusa" = "俄文"; 50 | // 拉丁 51 | "lation" = "拉丁"; 52 | // 韓文 53 | "korea" = "韓文"; 54 | // 音標 55 | "phonetic" = "音標"; 56 | // 顏文字 57 | "kaomoji" = "顏文字"; 58 | // 假名 59 | "jp" = "片假"; 60 | 61 | "searchEmoji" = "搜尋Emoji"; 62 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/View/StandarKeyboard/ClassifySymbolic/ViewModel/ClassifySymbolicViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/9/5. 6 | // 7 | 8 | import Combine 9 | import Foundation 10 | 11 | class ClassifySymbolicViewModel { 12 | /// 当前符号分类 13 | @Published 14 | var currentCategory: SymbolCategory = .frequent 15 | } 16 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Sources/View/StandarKeyboard/EmojisKeyboard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmojisKeyboard.swift 3 | // 4 | // 5 | // Created by morse on 2023/9/5. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Emojis 表情键盘 11 | class EmojisKeyboard: KeyboardTouchView { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Tests/Configuration/Sample/HamsterPatchConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/4. 6 | // 7 | 8 | import Foundation 9 | 10 | @testable import HamsterKeyboardKit 11 | 12 | public extension HamsterPatchConfiguration { 13 | static let preview = HamsterPatchConfiguration( 14 | patch: HamsterConfiguration( 15 | general: GeneralConfiguration( 16 | enableAppleCloud: false 17 | ), 18 | toolbar: KeyboardToolbarConfiguration( 19 | enableToolbar: false 20 | ), 21 | keyboard: KeyboardConfiguration( 22 | displayButtonBubbles: false 23 | ) 24 | ) 25 | ) 26 | 27 | static let preview2 = HamsterPatchConfiguration(patch: HamsterConfiguration.preview) 28 | } 29 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Tests/HamsterKeyboardTests/Extensions/CGFloat+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGFloat+.swift 3 | // 4 | // 5 | // Created by morse on 2023/8/14. 6 | // 7 | 8 | @testable import HamsterKeyboardKit 9 | import XCTest 10 | 11 | final class CGFloatTest: XCTestCase { 12 | func testRounded() throws { 13 | print(CGFloat.rounded(CGFloat(702) / CGFloat(10))) 14 | print(CGFloat.rounded(CGFloat(304) / CGFloat(9))) 15 | print(CGFloat.rounded(CGFloat(304) / CGFloat(9))) 16 | print(CGFloat.rounded(CGFloat(304) / CGFloat(9)).rounded(.down)) 17 | print(CGFloat.rounded(CGFloat(12.9999))) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Tests/HamsterKeyboardTests/Extensions/String+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+.swift 3 | // 4 | // 5 | // Created by morse on 2023/8/7. 6 | // 7 | import XCTest 8 | 9 | @testable import HamsterKeyboardKit 10 | 11 | final class StringExtensionTests: XCTestCase { 12 | func testIsCapitalized() throws { 13 | XCTAssertTrue("Abc".isCapitalized) 14 | XCTAssertFalse("abc".isCapitalized) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Packages/HamsterKeyboardKit/Tests/HamsterKeyboardTests/Extensions/UILabel+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+.swift 3 | // 4 | // 5 | // Created by morse on 2023/10/26. 6 | // 7 | 8 | import XCTest 9 | 10 | @testable import HamsterKeyboardKit 11 | 12 | final class UILabel_: XCTestCase { 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testFontSizeMapping() throws { 22 | let targetSize = CGSize(width: 100, height: 100) 23 | 24 | for fontSize in 10 ... 30 { 25 | let font = UIFont.systemFont(ofSize: CGFloat(fontSize)) 26 | let size = UILabel.estimatedSize("🉐", targetSize: targetSize, font: font) 27 | print("🉐 \(fontSize) font size: \(size), fontPointSize: \(font.pointSize)") 28 | } 29 | var size = UILabel.estimatedSize("得", targetSize: targetSize, font: UIFont.systemFont(ofSize: 20)) 30 | print("得 size: \(size)") 31 | 32 | size = UILabel.estimatedSize("·", targetSize: targetSize, font: UIFont.systemFont(ofSize: 20)) 33 | print("· size: \(size)") 34 | 35 | size = UILabel.estimatedSize("♀", targetSize: targetSize, font: UIFont.systemFont(ofSize: 20)) 36 | print("♀ size: \(size)") 37 | 38 | print("🉐".count) 39 | } 40 | 41 | func testPerformanceExample() throws { 42 | // This is an example of a performance test case. 43 | self.measure { 44 | // Put the code you want to measure the time of here. 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Packages/HamsterKit/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "yams", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/jpsim/Yams.git", 7 | "state" : { 8 | "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", 9 | "version" : "5.0.6" 10 | } 11 | }, 12 | { 13 | "identity" : "zipfoundation", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/weichsel/ZIPFoundation.git", 16 | "state" : { 17 | "revision" : "43ec568034b3731101dbf7670765d671c30f54f3", 18 | "version" : "0.9.16" 19 | } 20 | } 21 | ], 22 | "version" : 2 23 | } 24 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.8 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: "HamsterKit", 8 | platforms: [ 9 | .iOS(.v15), 10 | ], 11 | products: [ 12 | .library( 13 | name: "HamsterKit", 14 | targets: ["HamsterKit"]), 15 | ], 16 | dependencies: [ 17 | .package(url: "https://github.com/weichsel/ZIPFoundation.git", exact: "0.9.16"), 18 | .package(url: "https://github.com/jpsim/Yams.git", exact: "5.0.6"), 19 | ], 20 | targets: [ 21 | .target( 22 | name: "HamsterKit", 23 | dependencies: [ 24 | "ZIPFoundation", 25 | "Yams", 26 | ], 27 | path: "Sources"), 28 | .testTarget( 29 | name: "HamsterKitTests", 30 | dependencies: ["HamsterKit"], 31 | path: "Tests"), 32 | ]) 33 | -------------------------------------------------------------------------------- /Packages/HamsterKit/README.md: -------------------------------------------------------------------------------- 1 | # HamsterKit 2 | 3 | Hamster 工具箱。存放设置应用和键盘扩展共同使用的相关工具。 4 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Sources/Constants/HamsterConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HamsterConstants.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/3. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Hamster 应用常量 11 | public enum HamsterConstants { 12 | /// AppGroup ID 13 | public static let appGroupName = "group.dev.fuxiao.app.Hamster" 14 | 15 | /// iCloud ID 16 | public static let iCloudID = "iCloud.dev.fuxiao.app.hamsterapp" 17 | 18 | /// keyboard Bundle ID 19 | public static let keyboardBundleID = "dev.fuxiao.app.Hamster.HamsterKeyboard" 20 | 21 | /// 跳转至系统添加键盘URL 22 | public static let addKeyboardPath = "app-settings:root=General&path=Keyboard/KEYBOARDS" 23 | 24 | // MARK: 与Squirrel.app保持一致 25 | 26 | /// RIME 预先构建的数据目录中 27 | public static let rimeSharedSupportPathName = "SharedSupport" 28 | 29 | /// RIME UserData目录 30 | public static let rimeUserPathName = "Rime" 31 | 32 | /// RIME 内置输入方案及配置zip包 33 | public static let inputSchemaZipFile = "SharedSupport.zip" 34 | 35 | /// 仓内置方案 zip 包 36 | public static let userDataZipFile = "rime-ice.zip" 37 | 38 | /// APP URL 39 | /// 注意: 此值需要与info.plist中的参数保持一致 40 | public static let appURL = "hamster://dev.fuxiao.app.hamster" 41 | } 42 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Sources/Context/KeyboardContext.swift: -------------------------------------------------------------------------------- 1 | // KeyboardContext.swift 2 | // 3 | // 4 | // Created by morse on 2023/6/30. 5 | // 6 | 7 | import Foundation 8 | 9 | ///// 键盘运行时上下文 10 | //public actor KeyboardContext: ObservableObject { 11 | // /// 单手模式 12 | // @Published 13 | // var enableOneHandMode: Bool = false 14 | // 15 | // /// 单手模式:左边 16 | // /// true 左边 false 右边 17 | // @Published 18 | // var isLeftOfOneHandMode: Bool = false 19 | //} 20 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Sources/Extensions/DateFormatter+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/4. 6 | // 7 | 8 | import Foundation 9 | 10 | public extension DateFormatter { 11 | /// 年-月-日 时:分:秒 格式 12 | static var longTimeFormatStyle: DateFormatter { 13 | let format = DateFormatter() 14 | format.locale = Locale(identifier: "zh_Hans_SG") 15 | format.dateFormat = "yyyy-MM-dd HH:mm:ss" 16 | return format 17 | } 18 | 19 | /// 年-月-日 格式 20 | static var shortTimeFormatStyle: DateFormatter { 21 | let format = DateFormatter() 22 | format.locale = Locale(identifier: "zh_Hans_SG") 23 | format.dateFormat = "yyyyMMdd" 24 | return format 25 | } 26 | 27 | /// 临时文件名 28 | static var tempFileNameStyle: DateFormatter { 29 | let format = DateFormatter() 30 | format.locale = Locale(identifier: "zh_Hans_SG") 31 | format.dateFormat = "yyyyMMdd-HHmmss" 32 | return format 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Sources/Model/CandidateWord.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/4. 6 | // 7 | 8 | import Foundation 9 | 10 | /// 候选字 11 | public struct CandidateWord { 12 | public var text: String 13 | public var comment: String 14 | 15 | public init(text: String, comment: String) { 16 | self.text = text 17 | self.comment = comment 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Sources/Model/RimeSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RimeSchema.swift 3 | // 4 | // 5 | // Created by morse on 2023/6/30. 6 | // 7 | 8 | import Foundation 9 | 10 | /// RIME 输入方案 11 | public struct RimeSchema: Identifiable, Equatable, Hashable, Comparable, Codable { 12 | public var id: String 13 | public var schemaId: String 14 | public var schemaName: String 15 | 16 | public init(schemaId: String, schemaName: String) { 17 | self.id = schemaId 18 | self.schemaId = schemaId 19 | self.schemaName = schemaName 20 | } 21 | } 22 | 23 | public extension RimeSchema { 24 | static func < (lhs: RimeSchema, rhs: RimeSchema) -> Bool { 25 | lhs.schemaId <= rhs.schemaId 26 | } 27 | 28 | static func == (lhs: RimeSchema, rhs: RimeSchema) -> Bool { 29 | return lhs.schemaId == rhs.schemaId 30 | } 31 | 32 | func hash(into hasher: inout Hasher) { 33 | hasher.combine(schemaId) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Sources/Persistent/PersistentController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PersistentController.swift 3 | // HamsterApp 4 | // 5 | // Created by morse on 16/3/2023. 6 | // 7 | 8 | import CoreData 9 | 10 | struct PersistentController { 11 | static let shared = PersistentController() 12 | 13 | let container: NSPersistentContainer 14 | init() { 15 | let name = "HamsterApp" 16 | 17 | let storeURL = FileManager.default.containerURL( 18 | forSecurityApplicationGroupIdentifier: HamsterConstants.appGroupName)! 19 | .appendingPathComponent("\(name).sqlite") 20 | 21 | let storeDescription = NSPersistentStoreDescription(url: storeURL) 22 | container = NSPersistentContainer(name: name) 23 | container.persistentStoreDescriptions = [storeDescription] 24 | container.loadPersistentStores { _, error in 25 | if let error = error as NSError? { 26 | fatalError("Unresolved error \(error), \(error.userInfo)") 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Sources/Shoutcat/ShortcutItemType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShortcutItemType.swift 3 | // 4 | // 5 | // Created by morse on 2023/9/15. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum ShortcutItemType: String { 11 | case rimeDeploy = "RIME重新部署" 12 | case rimeSync = "RIME同步" 13 | case rimeReset = "RIME重置" 14 | case none = "" 15 | } 16 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Sources/Utilities/AppInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/4. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum AppInfo { 11 | static let infoDictionary = Bundle.main.infoDictionary ?? [:] 12 | 13 | public static let appVersion: String = { 14 | (Self.infoDictionary["CFBundleShortVersionString"] as? String ?? "") 15 | + "(" + (Self.infoDictionary["CFBundleVersion"] as? String ?? "") + ")" 16 | }() 17 | 18 | public static let rimeVersion: String = { 19 | Self.infoDictionary["rimeVersion"] as? String ?? "" 20 | }() 21 | } 22 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Sources/Utilities/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Logger.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2/8/2023. 6 | // 7 | 8 | import OSLog 9 | 10 | public extension Logger { 11 | private static var subsystem = Bundle.main.bundleIdentifier! 12 | 13 | static let statistics = Logger(subsystem: subsystem, category: "statistics") 14 | } 15 | -------------------------------------------------------------------------------- /Packages/HamsterKit/Tests/Extensions/StringTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringTest.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/4. 6 | // 7 | 8 | @testable import HamsterKit 9 | 10 | import XCTest 11 | 12 | final class StringTest: XCTestCase { 13 | func testRegex() throws { 14 | let simpleDigits = "[0-9]+" 15 | XCTAssertTrue("123".isMatch(regex: simpleDigits)) 16 | XCTAssertFalse("abc".isMatch(regex: simpleDigits)) 17 | 18 | let fileName = "^.*[.]userdb$" 19 | let target = "Rime/extended.userdb" 20 | let target2 = "Rime/user.yaml" 21 | let target3 = "Rimeuserdb" 22 | XCTAssertTrue(target.isMatch(regex: fileName)) 23 | XCTAssertFalse(target2.isMatch(regex: fileName)) 24 | XCTAssertFalse(target3.isMatch(regex: fileName)) 25 | XCTAssertTrue("mi".isMatch(regex: "\\w.*")) 26 | } 27 | 28 | func testContainsChineseCharacters() throws { 29 | XCTAssertTrue("x你".containsChineseCharacters) 30 | XCTAssertTrue("你x".containsChineseCharacters) 31 | XCTAssertTrue("你".containsChineseCharacters) 32 | XCTAssertFalse("x".containsChineseCharacters) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.8 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: "HamsterUIKit", 8 | platforms: [ 9 | .iOS(.v15), 10 | ], 11 | products: [ 12 | .library( 13 | name: "HamsterUIKit", 14 | targets: ["HamsterUIKit"]), 15 | ], 16 | dependencies: [ 17 | ], 18 | targets: [ 19 | .target( 20 | name: "HamsterUIKit", 21 | dependencies: [], 22 | path: "Sources"), 23 | .testTarget( 24 | name: "HamsterUIKitTests", 25 | dependencies: ["HamsterUIKit"], 26 | path: "Tests"), 27 | ]) 28 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/README.md: -------------------------------------------------------------------------------- 1 | # HamsterUIKit 2 | 3 | 对 UIKit 相关扩展, 及可以复用的自定义 UIKit 组件 4 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/Sources/Model/Conform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConformAlert.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/12. 6 | // 7 | 8 | import UIKit 9 | 10 | public struct Conform { 11 | public var title: String 12 | public var message: String 13 | public var okTitle: String 14 | public var cancelTitle: String 15 | public var okAction: () -> Void 16 | public var cancelAction: () -> Void 17 | 18 | public init( 19 | title: String, 20 | message: String, 21 | okTitle: String = "确定", 22 | cancelTitle: String = "取消", 23 | okAction: @escaping () -> Void, 24 | cancelAction: @escaping () -> Void = {} 25 | ) { 26 | self.title = title 27 | self.message = message 28 | self.okTitle = okTitle 29 | self.cancelTitle = cancelTitle 30 | self.okAction = okAction 31 | self.cancelAction = cancelAction 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/Sources/Model/ErrorMessage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/8. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct ErrorMessage: Identifiable { 11 | public let id: UUID 12 | public var title: String 13 | public var message: String 14 | 15 | public init(title: String, message: String) { 16 | self.id = UUID() 17 | self.title = title 18 | self.message = message 19 | } 20 | } 21 | 22 | extension ErrorMessage: Equatable { 23 | public static func == (lhs: Self, rhs: Self) -> Bool { 24 | lhs.id == rhs.id 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/Sources/UIKIt/Extensions/UIView+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView.swift 3 | // 4 | // 5 | // Created by morse on 21/7/2023. 6 | // 7 | 8 | import UIKit 9 | 10 | public extension UIView { 11 | func fillSuperview(withConstant constant: CGFloat = .zero) { 12 | guard let superview = superview else { return } 13 | 14 | translatesAutoresizingMaskIntoConstraints = false 15 | NSLayoutConstraint.activate([ 16 | topAnchor.constraint(equalTo: superview.topAnchor, constant: constant), 17 | superview.bottomAnchor.constraint(equalTo: bottomAnchor, constant: constant), 18 | leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: constant), 19 | superview.trailingAnchor.constraint(equalTo: trailingAnchor, constant: constant), 20 | ]) 21 | } 22 | 23 | func fillSuperviewOnMarginsGuide() { 24 | guard let superview = superview else { return } 25 | 26 | translatesAutoresizingMaskIntoConstraints = false 27 | let layoutGuide = superview.layoutMarginsGuide 28 | NSLayoutConstraint.activate([ 29 | topAnchor.constraint(equalTo: layoutGuide.topAnchor), 30 | bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor), 31 | leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor), 32 | trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor), 33 | ]) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/Sources/UIKIt/NibLessCompotents/NibLessCollectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NibLessCollectionView.swift 3 | // 4 | // 5 | // Created by morse on 2023/9/13. 6 | // 7 | 8 | import UIKit 9 | 10 | open class NibLessCollectionView: UICollectionView { 11 | override public init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { 12 | super.init(frame: frame, collectionViewLayout: layout) 13 | } 14 | 15 | @available(*, unavailable) 16 | public required init?(coder: NSCoder) { 17 | fatalError("init(coder:) has not been implemented") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/Sources/UIKIt/NibLessCompotents/NibLessNavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NibLessNavigationController.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/5. 6 | // 7 | 8 | import UIKit 9 | 10 | open class NibLessNavigationController: UINavigationController { 11 | public init() { 12 | super.init(nibName: nil, bundle: nil) 13 | } 14 | 15 | override public init(rootViewController: UIViewController) { 16 | super.init(rootViewController: rootViewController) 17 | } 18 | 19 | override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 20 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 21 | } 22 | 23 | @available(*, unavailable, message: "为了支持从 init 依赖注入,从 nib 加载这个视图控制器是不被支持的。") 24 | public required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/Sources/UIKIt/NibLessCompotents/NibLessTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NibLessTableViewCell.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/5. 6 | // 7 | 8 | import UIKit 9 | 10 | open class NibLessTableViewCell: UITableViewCell { 11 | override public init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 12 | super.init(style: style, reuseIdentifier: reuseIdentifier) 13 | } 14 | 15 | @available(*, unavailable, message: "为了支持从 init 依赖注入,从 nib 加载这个视图控制器是不被支持的。") 16 | public required init?(coder: NSCoder) { 17 | fatalError("init(coder:) has not been implemented") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/Sources/UIKIt/NibLessCompotents/NibLessView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NilLessView.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/5. 6 | // 7 | 8 | import UIKit 9 | 10 | open class NibLessView: UIView { 11 | override public init(frame: CGRect) { 12 | super.init(frame: frame) 13 | } 14 | 15 | @available(*, unavailable, 16 | message: "Loading this view from a nib is unsupported in favor of initializer dependency injection.") 17 | public required init?(coder aDecoder: NSCoder) { 18 | fatalError("Loading this view from a nib is unsupported in favor of initializer dependency injection.") 19 | } 20 | 21 | /// 构建视图层次 22 | open func constructViewHierarchy() {} 23 | 24 | /// 激活视图约束 25 | open func activateViewConstraints() {} 26 | 27 | /// 设置 View 样式 28 | open func setupAppearance() {} 29 | } 30 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/Sources/UIKIt/UIViewController+ErrorPresentation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+ErrorPresentation.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/8. 6 | // 7 | 8 | import UIKit 9 | 10 | public extension UIViewController { 11 | /// alert error message 12 | func presentError(error: ErrorMessage) { 13 | let errorAlertController = UIAlertController(title: error.title, message: error.message, preferredStyle: .alert) 14 | let okAction = UIAlertAction(title: "确认", style: .default) 15 | errorAlertController.addAction(okAction) 16 | present(errorAlertController, animated: true) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Packages/HamsterUIKit/Tests/HamsterUIKitTests/HamsterUIKitTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import HamsterUIKit 3 | 4 | final class HamsterUIKitTests: XCTestCase { 5 | func testExample() throws { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | XCTAssertEqual(HamsterUIKit().text, "Hello, World!") 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/README.md: -------------------------------------------------------------------------------- 1 | # HamsteriOS 2 | 3 | Hamster 输入法设置主App 4 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Extensions/UICellConfigurationState+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICellConfigurationState+.swift 3 | // 4 | // 5 | // Created by morse on 2023/9/24. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIConfigurationStateCustomKey { 11 | static let settingItemModel = UIConfigurationStateCustomKey("com.ihsiao.apps.hamster.keyboard.settings.SettingItem") 12 | } 13 | 14 | extension UICellConfigurationState { 15 | var settingItemModel: SettingItemModel? { 16 | set { self[.settingItemModel] = newValue } 17 | get { return self[.settingItemModel] as? SettingItemModel } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Model/About/AboutCellInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutCellInfo.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/7. 6 | // 7 | 8 | import UIKit 9 | 10 | public struct AboutCellInfo { 11 | let text: String 12 | let secondaryText: String? 13 | let cellType: AboutCellType 14 | let typeValue: String? 15 | let accessoryType: UITableViewCell.AccessoryType? 16 | let navigationAction: (() -> Void)? 17 | 18 | init(text: String, secondaryText: String? = nil, type: AboutCellType = .copy, typeValue: String? = nil, accessoryType: UITableViewCell.AccessoryType? = nil, navigationAction: (() -> Void)? = nil) { 19 | self.text = text 20 | self.secondaryText = secondaryText 21 | self.cellType = type 22 | self.typeValue = typeValue 23 | self.accessoryType = accessoryType 24 | self.navigationAction = navigationAction 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Model/About/AboutCellType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutCellType.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/7. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum AboutCellType: String { 11 | case link 12 | case mail 13 | case copy 14 | case navigation 15 | } 16 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Model/Backup/BackupSwipeAction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/8. 6 | // 7 | 8 | import Foundation 9 | 10 | enum BackupSwipeAction { 11 | case delete 12 | case rename 13 | } 14 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Model/FavoriteButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FavoriteButton.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/5. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum FavoriteButton: String, Hashable, Equatable { 11 | case rimeDeploy 12 | case rimeSync 13 | // case rimeRest 14 | case appBackup 15 | } 16 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Model/FileInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/7. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct FileInfo: Identifiable, Hashable { 11 | public let id = UUID() 12 | public var url: URL 13 | public var fileResourceType: URLFileResourceType? 14 | public var fileModifiedDate: Date? 15 | } 16 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Model/Keyboard/ToolbarStepperModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToolbarStepperModel.swift 3 | // 4 | // 5 | // Created by morse on 14/7/2023. 6 | // 7 | 8 | import UIKit 9 | 10 | public struct StepperModel { 11 | var text: String 12 | var value: Double 13 | var minValue: Double 14 | var maxValue: Double 15 | var stepValue: Double 16 | var valueChangeHandled: (Double) -> Void 17 | 18 | public init(text: String = "", value: Double = 0, minValue: Double = 0, maxValue: Double = .infinity, stepValue: Double = 1, valueChangeHandled: @escaping (Double) -> Void = { _ in }) { 19 | self.text = text 20 | self.value = value 21 | self.minValue = minValue 22 | self.maxValue = maxValue 23 | self.stepValue = stepValue 24 | self.valueChangeHandled = valueChangeHandled 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Model/OpenSource/OpenSourceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenSourceInfo.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/7. 6 | // 7 | 8 | import Foundation 9 | 10 | struct OpenSourceInfo { 11 | let name: String 12 | let projectURL: String 13 | } 14 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Model/SettingSectionModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingSectionModel.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/5. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct SettingSectionModel: Hashable { 11 | public var title: String 12 | public var footer: String? 13 | public var items: [SettingItemModel] 14 | 15 | init(title: String = "", footer: String? = nil, items: [SettingItemModel]) { 16 | self.title = title 17 | self.footer = footer 18 | self.items = items 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Model/SettingType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingModel.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/12. 6 | // 7 | 8 | import UIKit 9 | 10 | /// 设置类型 11 | public enum SettingType: Hashable, Equatable { 12 | case navigation 13 | case toggle 14 | case textField 15 | case button 16 | 17 | // 下拉选项 cell 18 | case pullDown 19 | 20 | // 设置文本 21 | case settings 22 | 23 | case step 24 | } 25 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Model/SettingsSubView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/7. 6 | // 7 | 8 | import Foundation 9 | 10 | /// 主页设置子页面 11 | public enum SettingsSubView: String { 12 | /// 输入方案页面 13 | case inputSchema 14 | 15 | /// 输入方案上传页面 16 | case uploadInputSchema 17 | 18 | /// 文件管理页面 19 | case finder 20 | 21 | /// 键盘设置页面 22 | case keyboardSettings 23 | 24 | /// 键盘配色页面 25 | case colorSchema 26 | 27 | /// 反馈设置页面 28 | case feedback 29 | 30 | /// iCloud页面 31 | case iCloud 32 | 33 | /// 备份页面 34 | case backup 35 | 36 | /// RIME 页面 37 | case rime 38 | 39 | /// 关于页面 40 | case about 41 | 42 | /// 主页面 43 | case main 44 | 45 | 46 | /// 空页面 47 | case none 48 | } 49 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/Resources/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/About/OpenSource/OpenSourceTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenSourceTableViewCell.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/7. 6 | // 7 | import HamsterUIKit 8 | import UIKit 9 | 10 | class OpenSourceTableViewCell: NibLessTableViewCell { 11 | static let identifier = "OpenSourceTableViewCell" 12 | 13 | override public init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 14 | self.openSourceInfo = .init(name: "", projectURL: "") 15 | super.init(style: style, reuseIdentifier: reuseIdentifier) 16 | } 17 | 18 | var openSourceInfo: OpenSourceInfo 19 | 20 | override func updateConfiguration(using state: UICellConfigurationState) { 21 | super.updateConfiguration(using: state) 22 | 23 | var config = UIListContentConfiguration.subtitleCell() 24 | config.text = openSourceInfo.name 25 | config.secondaryText = openSourceInfo.projectURL 26 | contentConfiguration = config 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/About/OpenSource/OpenSourceViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenProjectViewController.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/15. 6 | // 7 | 8 | import HamsterUIKit 9 | import UIKit 10 | 11 | protocol OpenSourceViewModelFactory { 12 | func makeOpenSourceViewModel() -> OpenSourceViewModel 13 | } 14 | 15 | class OpenSourceViewController: NibLessViewController { 16 | private let openSourceViewModel: OpenSourceViewModel 17 | 18 | init(openSourceViewModelFactory: OpenSourceViewModelFactory) { 19 | self.openSourceViewModel = openSourceViewModelFactory.makeOpenSourceViewModel() 20 | super.init() 21 | } 22 | } 23 | 24 | // override UIViewController 25 | 26 | extension OpenSourceViewController { 27 | override func loadView() { 28 | title = "使用开源列表" 29 | view = OpenSourceRootView(openSourceViewModel: openSourceViewModel) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/Compotents/TableFooterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FooterView.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/14. 6 | // 7 | 8 | import UIKit 9 | 10 | class TableFooterView: UITableViewHeaderFooterView { 11 | static let identifier = "HamsterTableFooterView" 12 | 13 | init(footer: String) { 14 | self.text = footer 15 | super.init(reuseIdentifier: Self.identifier) 16 | var config = UIListContentConfiguration.groupedFooter() 17 | config.text = text 18 | contentConfiguration = config 19 | } 20 | 21 | @available(*, unavailable) 22 | required init?(coder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | var text = "" 27 | } 28 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/Feedback/KeyboardFeedbackViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardFeedbackViewController.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/15. 6 | // 7 | 8 | import HamsterUIKit 9 | import UIKit 10 | 11 | protocol KeyboardFeedbackViewModelFactory { 12 | func makeKeyboardFeedbackViewModel() -> KeyboardFeedbackViewModel 13 | } 14 | 15 | class KeyboardFeedbackViewController: NibLessViewController { 16 | private let keyboardFeedbackViewModel: KeyboardFeedbackViewModel 17 | 18 | init(keyboardFeedbackViewModelFactory: KeyboardFeedbackViewModelFactory) { 19 | self.keyboardFeedbackViewModel = keyboardFeedbackViewModelFactory.makeKeyboardFeedbackViewModel() 20 | 21 | super.init() 22 | } 23 | 24 | override func loadView() { 25 | title = "键盘反馈" 26 | view = KeyboardFeedbackRootView( 27 | keyboardFeedbackViewModel: keyboardFeedbackViewModel 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/InputSchema/CloudInputSchema/CreateInputSchemaViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UploadInputSchemaViewController.swift 3 | // 4 | // 5 | // Created by morse on 2023/11/13. 6 | // 7 | 8 | import Combine 9 | import HamsterUIKit 10 | import UIKit 11 | 12 | class CreateInputSchemaViewController: NibLessViewController { 13 | private let inputSchemaViewModel: InputSchemaViewModel 14 | private var subscriptions = Set() 15 | 16 | init(inputSchemaViewModel: InputSchemaViewModel) { 17 | self.inputSchemaViewModel = inputSchemaViewModel 18 | super.init() 19 | combine() 20 | } 21 | 22 | override func loadView() { 23 | view = CreateInputSchemaRootView(inputSchemaViewModel: inputSchemaViewModel) 24 | title = "上传开源输入方案" 25 | } 26 | 27 | func combine() { 28 | inputSchemaViewModel.uploadInputSchemaConfirmSubject 29 | .eraseToAnyPublisher() 30 | .receive(on: DispatchQueue.main) 31 | .sink { [unowned self] callback in 32 | alertConfirm(alertTitle: "上传输入方案", message: "请勿上传非自己创作且无版权的输入方案,不符合规范的方案会被定期清除。", confirmTitle: "确认上传", confirmCallback: { 33 | callback() 34 | }) 35 | } 36 | .store(in: &subscriptions) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/Keyboard/SubViews/KeyboardLayout/Components/SwipeListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwipeListView.swift 3 | // 4 | // 5 | // Created by morse on 2023/10/18. 6 | // 7 | 8 | import HamsterKeyboardKit 9 | import HamsterUIKit 10 | import UIKit 11 | 12 | /// 键盘划动列表 13 | class SwipeListView: NibLessCollectionView {} 14 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/Keyboard/SubViews/SpaceSettings/SpaceSettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceSettingsViewController.swift 3 | // 4 | // 5 | // Created by morse on 2023/10/12. 6 | // 7 | 8 | import HamsterUIKit 9 | import UIKit 10 | 11 | /// 空格设置 ViewController 12 | public class SpaceSettingsViewController: NibLessViewController { 13 | private let keyboardSettingsViewModel: KeyboardSettingsViewModel 14 | 15 | init(keyboardSettingsViewModel: KeyboardSettingsViewModel) { 16 | self.keyboardSettingsViewModel = keyboardSettingsViewModel 17 | 18 | super.init() 19 | } 20 | 21 | override public func loadView() { 22 | view = SpaceSettingsRootView(keyboardSettingsViewModel: keyboardSettingsViewModel) 23 | title = "空格设置" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/Keyboard/SubViews/SymbolKeyboardSettings/SymbolKeyboardSettingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SymbolKeyboardSettingViewController.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/17. 6 | // 7 | import HamsterUIKit 8 | import ProgressHUD 9 | import UIKit 10 | 11 | class SymbolKeyboardSettingsViewController: NibLessViewController { 12 | private let keyboardSettingsViewModel: KeyboardSettingsViewModel 13 | 14 | init(keyboardSettingsViewModel: KeyboardSettingsViewModel) { 15 | self.keyboardSettingsViewModel = keyboardSettingsViewModel 16 | 17 | super.init() 18 | } 19 | } 20 | 21 | // MARK: override UIViewController 22 | 23 | extension SymbolKeyboardSettingsViewController { 24 | override func loadView() { 25 | title = "符号键盘设置" 26 | view = SymbolKeyboardSettingsRootView(keyboardSettingsViewModel: keyboardSettingsViewModel) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/Keyboard/SubViews/SymbolSettings/SymbolSettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SymbolSettingsViewController.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/17. 6 | // 7 | 8 | import HamsterUIKit 9 | import UIKit 10 | 11 | public class SymbolSettingsViewController: NibLessViewController { 12 | private let keyboardSettingsViewModel: KeyboardSettingsViewModel 13 | 14 | init(keyboardSettingsViewModel: KeyboardSettingsViewModel) { 15 | self.keyboardSettingsViewModel = keyboardSettingsViewModel 16 | 17 | super.init() 18 | } 19 | } 20 | 21 | public extension SymbolSettingsViewController { 22 | override func loadView() { 23 | title = "符号设置" 24 | view = SymbolSettingsRootView(keyboardSettingsViewModel: keyboardSettingsViewModel) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/Keyboard/SubViews/Toolbar/ToolbarSettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CandidateTextBarSettingViewController.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/15. 6 | // 7 | 8 | import HamsterUIKit 9 | import UIKit 10 | 11 | public class ToolbarSettingsViewController: NibLessViewController { 12 | private let keyboardSettingsViewModel: KeyboardSettingsViewModel 13 | 14 | init(keyboardSettingsViewModel: KeyboardSettingsViewModel) { 15 | self.keyboardSettingsViewModel = keyboardSettingsViewModel 16 | 17 | super.init() 18 | } 19 | 20 | override public func loadView() { 21 | title = "候选栏" 22 | view = ToolbarSettingsRootView(keyboardSettingsViewModel: keyboardSettingsViewModel) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/KeyboardColor/KeyboardColorViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorSchemaSettingViewController.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/15. 6 | // 7 | 8 | import HamsterUIKit 9 | import UIKit 10 | 11 | protocol KeyboardColorViewModelFactory { 12 | func makeKeyboardColorViewModel() -> KeyboardColorViewModel 13 | } 14 | 15 | class KeyboardColorViewController: NibLessViewController { 16 | private let keyboardColorViewModel: KeyboardColorViewModel 17 | private let rootView: KeyboardColorRootView 18 | 19 | init(keyboardColorViewModelFactory: KeyboardColorViewModelFactory) { 20 | self.keyboardColorViewModel = keyboardColorViewModelFactory.makeKeyboardColorViewModel() 21 | self.rootView = KeyboardColorRootView(keyboardColorViewModel: keyboardColorViewModel) 22 | 23 | super.init() 24 | } 25 | } 26 | 27 | // MARK: override UIViewController 28 | 29 | extension KeyboardColorViewController { 30 | override func loadView() { 31 | title = "键盘配色" 32 | view = rootView 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/RIME/Logger/RimeLoggerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RimeLoggerViewController.swift 3 | // 4 | // 5 | // Created by morse on 2023/11/8. 6 | // 7 | 8 | import HamsterUIKit 9 | import UIKit 10 | 11 | class RimeLoggerViewController: NibLessViewController { 12 | private let finderViewModel: FinderViewModel 13 | 14 | lazy var rimeLoggerFileBrowseView: FileBrowserView = { 15 | // 防止文件夹被删除而产生异常 16 | try? FileManager.createDirectory(override: false, dst: FileManager.sandboxRimeLogDirectory) 17 | let fileBrowserViewModel = FileBrowserViewModel(rootURL: FileManager.sandboxRimeLogDirectory, enableEditorState: false) 18 | return FileBrowserView(finderViewModel: finderViewModel, fileBrowserViewModel: fileBrowserViewModel) 19 | }() 20 | 21 | init(finderViewModel: FinderViewModel) { 22 | self.finderViewModel = finderViewModel 23 | super.init() 24 | } 25 | 26 | override func loadView() { 27 | view = rimeLoggerFileBrowseView 28 | title = "RIME 日志" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/Settings/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // 4 | // Created by morse on 2023/6/12. 5 | // 6 | 7 | import HamsterKit 8 | import HamsterUIKit 9 | import OSLog 10 | import ProgressHUD 11 | import UIKit 12 | 13 | protocol SettingsViewModelFactory { 14 | func makeSettingsViewModel() -> SettingsViewModel 15 | } 16 | 17 | public class SettingsViewController: NibLessViewController { 18 | // MARK: - properties 19 | 20 | private var settingsViewModel: SettingsViewModel 21 | private var rimeViewModel: RimeViewModel 22 | private var backupViewModel: BackupViewModel 23 | 24 | init(settingsViewModel: SettingsViewModel, rimeViewModel: RimeViewModel, backupViewModel: BackupViewModel) { 25 | self.settingsViewModel = settingsViewModel 26 | self.rimeViewModel = rimeViewModel 27 | self.backupViewModel = backupViewModel 28 | super.init() 29 | } 30 | } 31 | 32 | // MARK: override UIViewController 33 | 34 | public extension SettingsViewController { 35 | override func loadView() { 36 | title = "输入法设置" 37 | view = SettingsRootView(settingsViewModel: settingsViewModel, rimeViewModel: rimeViewModel, backupViewModel: backupViewModel) 38 | } 39 | 40 | override func viewDidAppear(_ animated: Bool) { 41 | super.viewDidAppear(animated) 42 | Task { 43 | do { 44 | try await self.settingsViewModel.loadAppData() 45 | } catch { 46 | ProgressHUD.failed("导入数据异常", interaction: false, delay: 2) 47 | Logger.statistics.error("load app data error: \(error)") 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/UploadInputSchema/UploadInputSchemaViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UploadInputSchemaViewController.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/13. 6 | // 7 | 8 | import HamsterUIKit 9 | import ProgressHUD 10 | import UIKit 11 | 12 | protocol UploadInputSchemaViewModelFactory { 13 | func makeUploadInputSchemaViewModel() -> UploadInputSchemaViewModel 14 | } 15 | 16 | public class UploadInputSchemaViewController: NibLessViewController { 17 | private let viewModel: UploadInputSchemaViewModel 18 | 19 | init(uploadInputSchemaViewModelFactory: UploadInputSchemaViewModelFactory) { 20 | self.viewModel = uploadInputSchemaViewModelFactory.makeUploadInputSchemaViewModel() 21 | 22 | super.init() 23 | } 24 | } 25 | 26 | // MARK: override UIViewController 27 | 28 | public extension UploadInputSchemaViewController { 29 | override func loadView() { 30 | title = "输入方案上传" 31 | view = UploadInputSchemaRootView(uploadInputSchemaViewModel: viewModel) 32 | } 33 | 34 | override func viewDidLoad() { 35 | super.viewDidLoad() 36 | 37 | UIApplication.shared.isIdleTimerDisabled = true 38 | viewModel.startWiFiMonitor() 39 | } 40 | 41 | override func viewWillDisappear(_ animated: Bool) { 42 | super.viewWillDisappear(animated) 43 | 44 | UIApplication.shared.isIdleTimerDisabled = false 45 | viewModel.stopFileServer() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/UILayer/iCloud/AppleCloudViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppleCloudViewController.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/14. 6 | // 7 | import HamsterUIKit 8 | import ProgressHUD 9 | import UIKit 10 | 11 | protocol AppleCloudViewModelFactory { 12 | func makeAppleCloudViewModel() -> AppleCloudViewModel 13 | } 14 | 15 | class AppleCloudViewController: NibLessViewController { 16 | // MARK: properties 17 | 18 | let appleCloudViewModelFactory: AppleCloudViewModelFactory 19 | 20 | // MARK: methods 21 | 22 | init(appleCloudViewModelFactory: AppleCloudViewModelFactory) { 23 | self.appleCloudViewModelFactory = appleCloudViewModelFactory 24 | super.init() 25 | } 26 | } 27 | 28 | // MARK: override UIViewController 29 | 30 | extension AppleCloudViewController { 31 | override func loadView() { 32 | title = "iCloud同步" 33 | let viewModel = appleCloudViewModelFactory.makeAppleCloudViewModel() 34 | view = AppleCloudRootView(viewModel: viewModel) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/ViewModel/Main/MainViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/7. 6 | // 7 | 8 | import Combine 9 | import Foundation 10 | import HamsterKit 11 | 12 | public class MainViewModel: ObservableObject { 13 | public let subViewSubject = PassthroughSubject() 14 | public var subViewPublished: AnyPublisher { 15 | subViewSubject.eraseToAnyPublisher() 16 | } 17 | 18 | public let shortcutItemTypeSubject = PassthroughSubject() 19 | public var shortcutItemTypePublished: AnyPublisher { 20 | shortcutItemTypeSubject.eraseToAnyPublisher() 21 | } 22 | 23 | /// 导航到输入方案页面 24 | public func navigationToInputSchema() { 25 | subViewSubject.send(.inputSchema) 26 | } 27 | 28 | /// 导航到 RIME 设置页面 29 | public func navigationToRIME() { 30 | subViewSubject.send(.rime) 31 | } 32 | 33 | public func execShortcutCommand(_ shortItemType: ShortcutItemType) { 34 | shortcutItemTypeSubject.send(shortItemType) 35 | } 36 | 37 | public func navigation(_ subView: SettingsSubView) { 38 | subViewSubject.send(subView) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/ViewModel/OpenSource/OpenSourceViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenSourceViewModel.swift 3 | // 4 | // 5 | // Created by morse on 2023/7/7. 6 | // 7 | 8 | import Foundation 9 | 10 | class OpenSourceViewModel { 11 | let openSourceList: [OpenSourceInfo] = [ 12 | .init(name: "librime", projectURL: "https://github.com/rime/librime"), 13 | .init(name: "KeyboardKit", projectURL: "https://github.com/KeyboardKit/KeyboardKit"), 14 | .init(name: "Runestone", projectURL: "https://github.com/simonbs/Runestone"), 15 | .init(name: "TreeSitterLanguages", projectURL: "https://github.com/simonbs/TreeSitterLanguages"), 16 | .init(name: "ProgressHUD", projectURL: "https://github.com/relatedcode/ProgressHUD"), 17 | .init(name: "ZIPFoundation", projectURL: "https://github.com/weichsel/ZIPFoundation"), 18 | .init(name: "Yams", projectURL: "https://github.com/jpsim/Yams"), 19 | // .init(name: "ZippyJSON", projectURL: "https://github.com/michaeleisel/ZippyJSON"), 20 | .init(name: "GCDWebServer", projectURL: "https://github.com/swisspol/GCDWebServer"), 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Sources/ViewModel/UploadInputSchema/UploadInputSchemaViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UploadInputSchemaViewModel.swift 3 | // Hamster 4 | // 5 | // Created by morse on 2023/6/13. 6 | // 7 | 8 | import Foundation 9 | import HamsterFileServer 10 | import HamsterKit 11 | import Network 12 | import OSLog 13 | import UIKit 14 | 15 | class UploadInputSchemaViewModel { 16 | private lazy var fileServer: FileServer = { 17 | let server = FileServer( 18 | port: 80, 19 | publicDirectory: FileManager.sandboxDirectory 20 | ) 21 | return server 22 | }() 23 | 24 | @Published 25 | public private(set) var fileServerRunning = false 26 | 27 | private var wifiEnable = false 28 | } 29 | 30 | extension UploadInputSchemaViewModel { 31 | func startWiFiMonitor() { 32 | let monitor = NWPathMonitor(requiredInterfaceType: .wifi) 33 | monitor.pathUpdateHandler = { [unowned self] path in 34 | if path.status == .satisfied { 35 | self.wifiEnable = true 36 | monitor.cancel() 37 | return 38 | } 39 | self.wifiEnable = false 40 | monitor.cancel() 41 | } 42 | monitor.start(queue: .main) 43 | } 44 | 45 | @objc func managerfileServer() { 46 | if fileServerRunning { 47 | Logger.statistics.debug("stop file server") 48 | self.fileServer.shutdown() 49 | fileServerRunning = false 50 | } else { 51 | Logger.statistics.debug("start file server") 52 | self.fileServer.start() 53 | fileServerRunning = true 54 | } 55 | } 56 | 57 | func stopFileServer() { 58 | fileServerRunning = false 59 | self.fileServer.shutdown() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Packages/HamsteriOS/Tests/CloudKit/CloudKitHelperTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CloudKitHelperTest.swift 3 | // 4 | // 5 | // Created by morse on 2023/11/11. 6 | // 7 | 8 | import XCTest 9 | 10 | @testable import HamsteriOS 11 | 12 | final class CloudKitHelperTest: XCTestCase { 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testInputSchemeList() async throws { 22 | try await CloudKitHelper.shared.inputSchemeList() 23 | } 24 | 25 | func testPerformanceExample() throws { 26 | // This is an example of a performance test case. 27 | self.measure { 28 | // Put the code you want to measure the time of here. 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Packages/RimeKit/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /Packages/RimeKit/README.md: -------------------------------------------------------------------------------- 1 | # RimeKit 2 | 3 | 使用 Objective-C 调用 librime 的 api,并桥接到Swift中,最终由 Rime.swift 提供 librime 的相关 API。 4 | 5 | 注意:为了减小项目大小,没有将依赖的二进制 framework 放到项目中,编译时需要先下载依赖的二进制 framework。 6 | 7 | 编译好的 framework 在项目:https://github.com/imfuxiao/LibrimeKit 的 release 中。 8 | 9 | 可在项目根路径下执行 `make framework` 下载。 10 | 11 | # 注意事项 12 | 13 | `SbxlmRimeKitObjC` 与 `SbxlmRimeKit` 均为软链接,是为了解决声笔输入使用改版的 `librime`, 即 `"librime-sbxlm` 14 | -------------------------------------------------------------------------------- /Resources/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 11/4/2023. 6 | 7 | */ 8 | "CFBundleDisplayName" = "Hamster"; 9 | -------------------------------------------------------------------------------- /Resources/zh-HK.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 11/4/2023. 6 | 7 | */ 8 | "CFBundleDisplayName" = "倉輸入法"; 9 | -------------------------------------------------------------------------------- /Resources/zh-Hans.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 11/4/2023. 6 | 7 | */ 8 | "CFBundleDisplayName" = "仓输入法"; 9 | -------------------------------------------------------------------------------- /Resources/zh-Hant.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 11/4/2023. 6 | 7 | */ 8 | "CFBundleDisplayName" = "倉輸入法"; 9 | -------------------------------------------------------------------------------- /Resources/zh.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 11/4/2023. 6 | 7 | */ 8 | "CFBundleDisplayName" = "仓输入法"; 9 | -------------------------------------------------------------------------------- /SbxlmKeyboard/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSExtension 6 | 7 | NSExtensionAttributes 8 | 9 | IsASCIICapable 10 | 11 | PrefersRightToLeft 12 | 13 | PrimaryLanguage 14 | zh-Hans 15 | RequestsOpenAccess 16 | 17 | 18 | NSExtensionPointIdentifier 19 | com.apple.keyboard-service 20 | NSExtensionPrincipalClass 21 | $(PRODUCT_MODULE_NAME).KeyboardViewController 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /SbxlmKeyboard/KeyboardViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardViewController.swift 3 | // SbxlmKeyboard 4 | // 5 | // Created by morse on 2023/11/16. 6 | // 7 | 8 | import HamsterKeyboardKit 9 | import UIKit 10 | 11 | class KeyboardViewController: KeyboardInputViewController {} 12 | -------------------------------------------------------------------------------- /SbxlmKeyboard/SbxlmKeyboard.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | group.dev.fuxiao.app.Hamster 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SbxlmKeyboard/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 2023/11/16. 6 | 7 | */ 8 | "CFBundleDisplayName" = "声笔"; 9 | -------------------------------------------------------------------------------- /SbxlmKeyboard/zh-HK.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 2023/11/16. 6 | 7 | */ 8 | "CFBundleDisplayName" = "声笔"; 9 | -------------------------------------------------------------------------------- /SbxlmKeyboard/zh-Hans.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 2023/11/16. 6 | 7 | */ 8 | "CFBundleDisplayName" = "声笔"; 9 | -------------------------------------------------------------------------------- /SbxlmKeyboard/zh-Hant.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 2023/11/16. 6 | 7 | */ 8 | "CFBundleDisplayName" = "声笔"; 9 | -------------------------------------------------------------------------------- /SbxlmKeyboard/zh.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Hamster 4 | 5 | Created by morse on 2023/11/16. 6 | 7 | */ 8 | "CFBundleDisplayName" = "声笔"; 9 | -------------------------------------------------------------------------------- /ci_scripts/ci_post_clone.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ci_post_clone.sh 4 | # Hamster 5 | # 6 | # Created by morse on 2023/9/28. 7 | # 8 | set -e 9 | 10 | OUTPUT="${CI_PRIMARY_REPOSITORY_PATH}/Frameworks" 11 | 12 | # 下载依赖的 librime framework 13 | LibrimeKitVersion="2.3.0" 14 | mkdir -p $OUTPUT 15 | rm -rf $OUTPUT/*.xcframwork && ( 16 | curl -OL https://github.com/imfuxiao/LibrimeKit/releases/download/${LibrimeKitVersion}/Frameworks.tgz 17 | mkdir -p $OUTPUT 18 | tar -zxf Frameworks.tgz -C $OUTPUT/.. 19 | rm -rf Frameworks.tgz 20 | ) 21 | 22 | # 生成 SharedSupport.zip 与 rime-ice.zip 23 | OUTPUT="${CI_PRIMARY_REPOSITORY_PATH}/Resources/SharedSupport" 24 | mkdir -p $OUTPUT 25 | bash ${CI_PRIMARY_REPOSITORY_PATH}/InputSchemaBuild.sh 26 | cp ${CI_PRIMARY_REPOSITORY_PATH}/.tmp/SharedSupport/SharedSupport.zip $OUTPUT 27 | cp ${CI_PRIMARY_REPOSITORY_PATH}/.tmp/.rime-ice/rime-ice.zip $OUTPUT -------------------------------------------------------------------------------- /librimeFramework.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # encoding: utf-8 3 | set -e 4 | 5 | OUTPUT="${PWD}/Frameworks" 6 | 7 | # 如果存在就不再执行 8 | # if [[ -d ${OUTPUT} ]] 9 | # then 10 | # exit 0 11 | # fi 12 | 13 | # 下载依赖的 librime framework 14 | LibrimeKitVersion="2.4.2" 15 | mkdir -p $OUTPUT 16 | rm -rf $OUTPUT/*.xcframwork && ( 17 | curl -OL https://github.com/imfuxiao/LibrimeKit/releases/download/${LibrimeKitVersion}/Frameworks.tgz 18 | tar -zxf Frameworks.tgz -C ${OUTPUT}/.. 19 | rm -rf Frameworks.tgz 20 | ) 21 | --------------------------------------------------------------------------------