diff --git a/.github/workflows/deployment.yaml b/.github/workflows/deployment.yaml index 1d6cdf02..f9b2e529 100644 --- a/.github/workflows/deployment.yaml +++ b/.github/workflows/deployment.yaml @@ -9,7 +9,7 @@ jobs: strategy: matrix: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs platform: [mac, ios] - runs-on: macos-latest # https://github.com/actions/runner-images + runs-on: macos-26 # https://github.com/actions/runner-images steps: - uses: actions/checkout@v4 # https://github.com/actions/checkout with: @@ -37,8 +37,10 @@ jobs: if: ${{ github.event.release.prerelease }} - run: npm run build:${{ matrix.platform }}-safari-15 if: ${{ ! github.event.release.prerelease }} - - name: Set xcode version - run: sudo xcode-select -s "/Applications/Xcode_16.2.app" # https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md#xcode + # - name: Set xcode version + # run: sudo xcode-select -s "/Applications/Xcode_26.1.1.app" # https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md#xcode + # - run: xcodebuild -downloadPlatform iOS # Temporary fix for Xcode_26_beta + # if: matrix.platform == 'ios' - name: Run fastlane id: fastlane working-directory: ./fastlane diff --git a/README.md b/README.md index b000d558..09cdc78a 100644 --- a/README.md +++ b/README.md @@ -54,14 +54,15 @@ After installing the iOS App, you need two main steps to make the extension work > [!NOTE] > -> The App cannot detect whether you have enabled the extension in Safari, therefore, the App prompt will not change after you enable the extension. Currently the App interface is only used to set or change the userscripts directory. +> The iOS App cannot detect whether you have enabled the extension in Safari, therefore, the App prompt will not change after you enable the extension. Currently the App interface is only used to set or change the userscripts directory. > -> You could select an iCloud folder for syncing scripts between macOS and iOS, but please note that there may be delays in synchronization, and you may encounter files be evictioned due to iCloud optimization, please refer to [#424](https://github.com/quoid/userscripts/issues/424). +> You could select an iCloud folder for syncing scripts between macOS and iOS, but please note that there may be delays in synchronization, and you may encounter files be evictioned due to iCloud optimization, please refer to [#424](https://github.com/quoid/userscripts/issues/424). (**Since macOS 15 / iOS 18, please set "keep downloaded" for the folder to avoid eviction**) There are two main ways to install a user script from the iOS version: -- Visit any `.user.js` URL in Safari, then open the extension `popup` and you will see an installation prompt -- You can also save `.user.js` files directly to the Userscripts directory you set above +- Visit a `.user.js` URL in Safari, then open the extension `popup` and you will see an installation prompt + - The URL should end with `.user.js` in `/PATH` part, not the `?QUERY` or `#HASH` parts +- You could also save files with the extension `.user.js` directly to the Userscripts directory you set above > [!TIP] > @@ -177,10 +178,13 @@ Userscripts Safari currently supports the following userscript metadata: - when paired with `@updateURL`, this will allow the user to update a userscript from a remote source, if the version on their machine is `<` version at the update URL - `@version` does nothing by itself, it needs to be paired with` @updateURL` for remote updating to function properly - `@updateURL` - - the remote url to check version against + - the remote url to check version against, the url `/PATH` should end with `.meta.js` and contains the Metadata block - if the version of the file located at the update URL is `>` the version on the local machine, the file will be updated - `@updateURL` does nothing by itself, it needs to be paired with `@version` for remote updating to function properly + - Read this [link](https://stackoverflow.com/questions/38023717/) for more details + - _Note: The extension does not correctly implement the entire update process, tracking in issue [#248](https://github.com/quoid/userscripts/issues/248)_ - `@downloadURL` + - the url `/PATH` should end with `.user.js`, that is, it cannot be in `?QUERY` or `#HASH` parts - optional download location for a remotely updateable file (\*i.e. a file that has both `@version` and `@updateURL`) - when paired with `@version` and `@updateURL`, if the local version is `<` the version of the file that `@updateURL` points to, the extension will attempt to update the file's code with the contents of the file located at the `@downloadURL` - `@downloadURL` does nothing by itself, it needs `@version` and `@updateURL` to present in order to function properly @@ -382,6 +386,6 @@ Userscripts does not collect any data from its users nor monitor activities or a ## License -Copyright (c) 2018-2024 Justin Wasack +Copyright © 2018-2025 Justin Wasack Licensed under the [GNU General Public License v3.0](/LICENSE) license for all open source applications. A commercial license is required for all other applications. diff --git a/docs/contributing.md b/docs/contributing.md index 7d09d9d4..3cb92e9d 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -117,4 +117,4 @@ If you are not a member of the project, first you need to fork the master branch # About -[Userscripts](https://github.com/quoid/userscripts) @ 2018-2023 +[Userscripts](https://github.com/quoid/userscripts) diff --git a/docs/dev.md b/docs/dev.md index 2bfa7681..262aac05 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -79,4 +79,4 @@ Note that all existing `.xcconfig` files already include `.dev.xcconfig` files o # About -[Userscripts](https://github.com/quoid/userscripts) @ 2018-2024 +[Userscripts](https://github.com/quoid/userscripts) diff --git a/docs/structure.md b/docs/structure.md index d493c3b7..1a0cd320 100644 --- a/docs/structure.md +++ b/docs/structure.md @@ -38,4 +38,4 @@ Build the project from here to extension bundled app # About -[Userscripts](https://github.com/quoid/userscripts) @ 2018-2023 +[Userscripts](https://github.com/quoid/userscripts) diff --git a/fastlane/Gemfile.lock b/fastlane/Gemfile.lock index 989cb342..c6013a88 100644 --- a/fastlane/Gemfile.lock +++ b/fastlane/Gemfile.lock @@ -1,39 +1,40 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.7) - base64 - nkf - rexml - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + CFPropertyList (3.0.8) + abbrev (0.1.2) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) artifactory (3.0.17) atomos (0.1.3) - aws-eventstream (1.3.2) - aws-partitions (1.1106.0) - aws-sdk-core (3.224.0) + aws-eventstream (1.4.0) + aws-partitions (1.1188.0) + aws-sdk-core (3.239.2) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) base64 + bigdecimal jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.101.0) - aws-sdk-core (~> 3, >= 3.216.0) + aws-sdk-kms (1.118.0) + aws-sdk-core (~> 3, >= 3.239.1) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.186.1) - aws-sdk-core (~> 3, >= 3.216.0) + aws-sdk-s3 (1.205.0) + aws-sdk-core (~> 3, >= 3.234.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.11.0) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) + bigdecimal (3.3.1) claide (1.1.0) colored (1.2) colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) + csv (3.3.5) declarative (0.0.20) digest-crc (0.7.0) rake (>= 12.0.0, < 14.0.0) @@ -53,14 +54,14 @@ GEM faraday-rack (~> 1.0) faraday-retry (~> 1.0) ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.7) + faraday-cookie_jar (0.0.8) faraday (>= 0.8.0) - http-cookie (~> 1.0.0) + http-cookie (>= 1.0.0) faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) + faraday-em_synchrony (1.0.1) faraday-excon (1.1.0) faraday-httpclient (1.0.1) - faraday-multipart (1.1.0) + faraday-multipart (1.1.1) multipart-post (~> 2.0) faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) @@ -70,15 +71,18 @@ GEM faraday_middleware (1.2.1) faraday (~> 1.0) fastimage (2.4.0) - fastlane (2.227.2) + fastlane (2.229.1) CFPropertyList (>= 2.3, < 4.0.0) + abbrev (~> 0.1.2) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) aws-sdk-s3 (~> 1.0) babosa (>= 1.0.3, < 2.0.0) + base64 (~> 0.2.0) bundler (>= 1.12.0, < 3.0.0) colored (~> 1.2) commander (~> 4.6) + csv (~> 3.3) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 4.0) excon (>= 0.71.0, < 1.0.0) @@ -98,7 +102,9 @@ GEM jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) multipart-post (>= 2.0.0, < 3.0.0) + mutex_m (~> 0.3.0) naturally (~> 2.2) + nkf (~> 0.2.0) optparse (>= 0.1.1, < 1.0.0) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) @@ -157,37 +163,37 @@ GEM httpclient (2.9.0) mutex_m jmespath (1.6.2) - json (2.12.0) - jwt (2.10.1) + json (2.16.0) + jwt (2.10.2) base64 logger (1.7.0) mini_magick (4.13.2) mini_mime (1.1.5) - multi_json (1.15.0) + multi_json (1.17.0) multipart-post (2.4.1) mutex_m (0.3.0) nanaimo (0.4.0) - naturally (2.2.1) + naturally (2.3.0) nkf (0.2.0) - optparse (0.6.0) + optparse (0.8.0) os (1.1.4) plist (3.7.2) - public_suffix (6.0.2) - rake (13.2.1) + public_suffix (7.0.0) + rake (13.3.1) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.4.1) + rexml (3.4.4) rouge (3.28.0) ruby2_keywords (0.0.5) rubyzip (2.4.1) security (0.1.5) - signet (0.20.0) + signet (0.21.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 3.0) + jwt (>= 1.5, < 4.0) multi_json (~> 1.10) simctl (1.6.10) CFPropertyList @@ -217,11 +223,11 @@ GEM xcpretty (~> 0.2, >= 0.0.7) PLATFORMS - arm64-darwin-23 + arm64-darwin-25 ruby DEPENDENCIES fastlane BUNDLED WITH - 2.6.9 + 2.7.2 diff --git a/public/ext/shared/_locales/en/messages.json b/public/ext/shared/_locales/en/messages.json index a39bfc24..c17c6cad 100644 --- a/public/ext/shared/_locales/en/messages.json +++ b/public/ext/shared/_locales/en/messages.json @@ -85,6 +85,24 @@ "settings_global_active_desc": { "message": "Toggle on/off script injection for the pages you visit" }, + "settings_theme_mode": { + "message": "Theme mode" + }, + "settings_theme_mode_desc": { + "message": "Toggle theme mode" + }, + "settings_theme_mode_auto": { + "message": "Auto (System)" + }, + "settings_theme_mode_dark": { + "message": "Dark" + }, + "settings_theme_mode_light": { + "message": "Light" + }, + "settings_theme_mode_custom": { + "message": "Custom" + }, "settings_global_exclude_match": { "message": "Global Exclude Match patterns" }, diff --git a/scripts/dev-safari.js b/scripts/dev-safari.js index 34943850..c9236b9f 100644 --- a/scripts/dev-safari.js +++ b/scripts/dev-safari.js @@ -186,6 +186,7 @@ async function serverCheck(url) { /** run development server */ const server = await createServer({ ...sharedConfig, + publicDir: "public/ext/vendor/", plugins: [svelte()], server: { host: true, diff --git a/src/app/App.svelte b/src/app/App.svelte index 7e6c5c5b..bbfe8df8 100644 --- a/src/app/App.svelte +++ b/src/app/App.svelte @@ -1,7 +1,7 @@ @@ -72,10 +108,30 @@ {/if} {/if} -
- - +
+ {#if !sidebarHidden} + +
+ {/if} +
+{#if splitterActive} +
+{/if}
    {#each $notifications as item (item.id)} notifications.remove(item.id)} {item} /> @@ -120,6 +176,34 @@ overflow: hidden; } + .resize-mask { + background-color: transparent; + cursor: col-resize; + position: fixed; + inset: 0; + z-index: 20; + } + + .splitter { + --splitter-width: 6px; + --splitter-offset: calc(var(--splitter-width) / 2); + --sidebar-fixed-width: max(var(--sidebar-min-width), var(--sidebar-width)); + + background-color: transparent; + cursor: col-resize; + position: fixed; + left: calc(var(--sidebar-fixed-width) - var(--splitter-offset)); + width: var(--splitter-width); + height: 100svh; + z-index: 21; + transition: background-color 0.2s ease-in-out; + + &:hover, + &.dragging { + background-color: var(--color-blue); + } + } + ul { bottom: 1rem; left: 50%; diff --git a/src/ext/extension-page/Components/Editor/CodeMirror.svelte b/src/ext/extension-page/Components/Editor/CodeMirror.svelte index f097189b..b2cbacda 100644 --- a/src/ext/extension-page/Components/Editor/CodeMirror.svelte +++ b/src/ext/extension-page/Components/Editor/CodeMirror.svelte @@ -150,11 +150,6 @@ "Cmd-S": () => saveHandler(), "Cmd-F": () => activateSearch(), Esc: () => (searchActive = false), - Tab: (cm) => { - // convert tabs to spaces and add invisible elements - const s = Array(cm.getOption("indentUnit") + 1).join(" "); - cm.replaceSelection(s); - }, }, }); diff --git a/src/ext/extension-page/Components/Editor/Editor.svelte b/src/ext/extension-page/Components/Editor/Editor.svelte index 0bdd3507..e0b7c719 100644 --- a/src/ext/extension-page/Components/Editor/Editor.svelte +++ b/src/ext/extension-page/Components/Editor/Editor.svelte @@ -13,6 +13,10 @@ import iconDownload from "@shared/img/icon-download.svg?raw"; import iconTrash from "@shared/img/icon-trash.svg?raw"; import iconSync from "@shared/img/icon-sync.svg?raw"; + import iconSidebar from "@shared/img/icon-sidebar.svg?raw"; + + export let sidebarHidden; + export let sidebarSwitch; // the data the populates editor elements let canUpdate; @@ -167,13 +171,22 @@
    No Item Selected
    {/if}
    + {#if sidebarHidden} + + {/if}
    {name}
    -
    +
    {#if $v4state.includes("saving")} Saving... {:else if $v4state.includes("trashing")} @@ -233,6 +246,7 @@ flex-grow: 1; overflow: hidden; position: relative; + width: calc(100svw - var(--sidebar-width)); } .info { @@ -241,26 +255,28 @@ } .editor__empty { - align-items: center; background-color: inherit; color: var(--text-color-disabled); - display: flex; font: var(--text-large); - height: 100%; - justify-content: center; - left: 0; letter-spacing: var(--letter-spacing-large); + display: flex; + place-content: center; + place-items: center; position: absolute; - top: 0; - width: 100%; - z-index: 85; + inset: 0; + z-index: 10; } .editor__header { align-items: center; display: flex; flex-shrink: 0; - padding: 1rem 0.5rem 1rem 1.5rem; + padding: 1rem; + gap: 1rem; + } + + .sidebar-switch { + scale: 1.5; } .editor__header__content { diff --git a/src/ext/extension-page/Components/Sidebar/Sidebar.svelte b/src/ext/extension-page/Components/Sidebar/Sidebar.svelte index cbc62445..c5af6b55 100644 --- a/src/ext/extension-page/Components/Sidebar/Sidebar.svelte +++ b/src/ext/extension-page/Components/Sidebar/Sidebar.svelte @@ -11,6 +11,7 @@ import Loader from "@shared/Components/Loader.svelte"; import iconPlus from "@shared/img/icon-plus.svg?raw"; import iconSettings from "@shared/img/icon-settings.svg?raw"; + import iconSidebar from "@shared/img/icon-sidebar.svg?raw"; import { cmChanged, cmGetInstance, @@ -18,6 +19,8 @@ cmSetSessionCode, } from "../Editor/CodeMirror.svelte"; + export let sidebarSwitch; + // disable buttons accordingly $: disabled = !$v4state.includes("ready"); @@ -30,6 +33,8 @@ $: if (list.find((a) => a.active)) { const active = list.find((a) => a.active); scrollToEl(active.filename); + } else { + activate(list[0]); } /** @@ -76,8 +81,8 @@ } async function activate(item) { - // if not in ready state or the item is already active - if (!$v4state.includes("ready") || item.active) return; + // if item is empty or already active + if (!item || item.active) return; // check if there's a temp item and it's not the item to be activated // can occur when user clicks a non-temp item while a temp item exists @@ -204,11 +209,17 @@