diff --git a/.github/workflows/api-verification.yml b/.github/workflows/api-verification.yml index e8cc9aa95a..af6bc682dc 100644 --- a/.github/workflows/api-verification.yml +++ b/.github/workflows/api-verification.yml @@ -23,6 +23,7 @@ jobs: GIT_BRANCH: ${{ steps.environment_step.outputs.GIT_BRANCH }} GIT_BASE_BRANCH: ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }} GIT_USER: ${{ steps.environment_step.outputs.GIT_USER }} + SKIP_API_CHECK: ${{ steps.environment_step.outputs.SKIP_API_CHECK }} steps: - name: Restore artifacts @@ -40,7 +41,7 @@ jobs: - name: Set pending status on PR builds id: set_status - if: steps.environment_step.outputs.IS_TEST_BUILD == 'true' + if: steps.environment_step.outputs.IS_TEST_BUILD == 'true' && steps.environment_step.outputs.SKIP_API_CHECK != 'true' shell: bash run: | gh api \ @@ -53,6 +54,7 @@ jobs: -f context="$STATUS_CONTEXT" - name: Checkout + if: steps.environment_step.outputs.SKIP_API_CHECK != 'true' uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 with: ref: '${{ steps.environment_step.outputs.GIT_SHA }}' @@ -61,11 +63,13 @@ jobs: path: ${{ github.workspace }}/keyman/ - name: Install devscripts + if: steps.environment_step.outputs.SKIP_API_CHECK != 'true' uses: ./keyman/.github/actions/apt-install with: packages: devscripts equivs - name: "Verify API for libkeymancore*.so (${{ steps.environment_step.outputs.GIT_BRANCH }}, branch ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }}, by ${{ steps.environment_step.outputs.GIT_USER }})" + if: steps.environment_step.outputs.SKIP_API_CHECK != 'true' run: | echo "Verify API for libkeymancore*.so (${{ steps.environment_step.outputs.GIT_BRANCH }}, branch ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }}, by ${{ steps.environment_step.outputs.GIT_USER }}):" >> $GITHUB_STEP_SUMMARY @@ -79,17 +83,17 @@ jobs: verify 2>> $GITHUB_STEP_SUMMARY - name: Archive .symbols file + if: steps.environment_step.outputs.SKIP_API_CHECK != 'true' && always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: libkeymancore.symbols path: ${{ github.workspace }}/keyman/linux/debian/tmp/DEBIAN/symbols - if: always() set_status: name: Set result status on PR builds needs: [api_verification] runs-on: ubuntu-latest - if: ${{ always() && needs.api_verification.outputs.IS_TEST_BUILD == 'true' }} + if: ${{ always() && needs.api_verification.outputs.IS_TEST_BUILD == 'true' && needs.api_verification.outputs.SKIP_API_CHECK != 'true' }} steps: - name: Set success if: needs.api_verification.result == 'success' diff --git a/.github/workflows/build-test-publish-docker.yml b/.github/workflows/build-test-publish-docker.yml new file mode 100644 index 0000000000..7919d9ff81 --- /dev/null +++ b/.github/workflows/build-test-publish-docker.yml @@ -0,0 +1,90 @@ +# This workflow runs the build, test and publish steps for the +# Keyman build Docker images. +# It is triggered by a schedule and repository_dispatch event. + +name: "Build, test, and publish Docker images" +run-name: "Build, test, and publish Docker images (branch ${{ github.ref_name }} on ${{ github.event_name }}/${{ github.event.action }} by ${{ github.actor }})" +defaults: + run: + shell: bash + +on: + schedule: + #run every Fri, 3 pm UTC + - cron: '0 15 * * 5' + + repository_dispatch: + types: ['build-test-publish-docker:*'] + +env: + REGISTRY: ghcr.io + USERNAME: "${{ github.actor }}" + PASSWORD: "${{ github.token }}" + +jobs: + + message: + name: Print message + runs-on: ubuntu-latest + if: ${{ github.event.act }} + + steps: + - name: Print message + id: print-message + run: | + echo "Build, test, and publish Docker images (branch ${{ github.ref_name }} on ${{ github.event_name }}/${{ github.event.action }} by ${{ github.actor }})" + + build: + name: Build Docker images + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + id: checkout + uses: actions/checkout@v4 + - name: Build Docker images + id: build + run: | + if [ -z "${{ env.USERNAME }}" ]; then + resources/docker-images/build.sh build: --registry ${{ env.REGISTRY }} + else + echo ${{ env.PASSWORD }} | resources/docker-images/build.sh build: --registry ${{ env.REGISTRY }} --username ${{ env.USERNAME }} + fi + + publish: + name: Publish Docker images + runs-on: ubuntu-latest + needs: build + + steps: + - name: Checkout repository + id: checkout + uses: actions/checkout@v4 + + - name: Publish Docker images + id: publish + run: | + if [ -z "${{ env.USERNAME }}" ]; then + resources/docker-images/build.sh publish: --registry ${{ env.REGISTRY }} + else + echo ${{ env.PASSWORD }} | resources/docker-images/build.sh publish: --registry ${{ env.REGISTRY }} --username ${{ env.USERNAME }} + fi + + test: + name: Test Docker images + runs-on: ubuntu-latest + needs: publish + + steps: + - name: Checkout repository + id: checkout + uses: actions/checkout@v4 + + - name: Test Docker images + id: test + run: | + if [ -z "${{ env.USERNAME }}" ]; then + resources/docker-images/build.sh test: --registry ${{ env.REGISTRY }} + else + echo ${{ env.PASSWORD }} | resources/docker-images/build.sh test: --registry ${{ env.REGISTRY }} --username ${{ env.USERNAME }} + fi diff --git a/.github/workflows/deb-packaging.yml b/.github/workflows/deb-packaging.yml index a40fcb7259..472adf91b0 100644 --- a/.github/workflows/deb-packaging.yml +++ b/.github/workflows/deb-packaging.yml @@ -23,6 +23,7 @@ on: # baseRef: The ref of the previous commit. For a PR the same as `baseBranch`. # user: The user that triggered the build or created the PR # isTestBuild: false for Releases, otherwise true +# skipApiCheck: true to skip the API check, otherwise false env: COLOR_GREEN: "\e[32m" @@ -332,6 +333,7 @@ jobs: echo "GIT_BRANCH=${{ github.event.client_payload.branch }}" >> artifacts/env echo "GIT_BASE_BRANCH=${{ github.event.client_payload.baseBranch }}" >> artifacts/env echo "GIT_USER=${{ github.event.client_payload.user }}" >> artifacts/env + echo "SKIP_API_CHECK=${{ github.event.client_payload.skipApiCheck }}" >> artifacts/env - name: Cache artifacts uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 diff --git a/.github/workflows/delete-build-containers.yml b/.github/workflows/delete-build-containers.yml new file mode 100644 index 0000000000..d869831366 --- /dev/null +++ b/.github/workflows/delete-build-containers.yml @@ -0,0 +1,19 @@ + + +on: + schedule: + #run every Fri, 6 pm UTC + - cron: '0 18 * * 5' # + +jobs: + cleanup-images: + name: ghcr.io cleanup action + runs-on: ubuntu-latest + + steps: + - uses: dataaxiom/ghcr-cleanup-action@cd0cdb900b5dbf3a6f2cc869f0dbb0b8211f50c4 # v1.0.16 + with: + dry-run: true + expand-packages: true + packages: keyman-*-ci + delete-untagged: true diff --git a/.github/workflows/pr-build-status.yml b/.github/workflows/pr-build-status.yml index 236970f141..585a0b19b8 100644 --- a/.github/workflows/pr-build-status.yml +++ b/.github/workflows/pr-build-status.yml @@ -1,4 +1,4 @@ -# GENERATED FILE - DO NOT EDIT! +# GENERATED FILE - DO NOT EDIT! # # Keyman is copyright (C) SIL Global. MIT License. # @@ -35,174 +35,194 @@ jobs: id: run_pr_build_status_script uses: actions/github-script@v7 with: - script: | - // This code is copied out of resources/build/pr-build-status/pr-build-status.mjs - // where it is tested. It is copied inline here in order to avoid requiring the - // repository to be checked out, which dramatically reduces the run time of the - // check. - // - // Note: we don't currently look at check runs, only statuses - // - // Verify the following statuses: - // 'user_testing' - // 'API Verification' (github-actions[bot]) - // - // At least 1 of the following statuses must be found: - // 'Test*' (keyman-server), e.g. 'Test Build (Keyman)' - // 'Ubuntu Packaging' (github-actions[bot]) - // - // Ignore the following statuses: - // check/web/file-size - // - - function reduceStatuses(statuses) { - const filtered_statuses = statuses.reduce((o, status) => { - if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) { - if(!o[status.context]) o[status.context] = {type: 'build', state: status.state}; - } else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') { - if(!o[status.context]) o[status.context] = {type: 'user-test', state: status.state};; - } else if(status.context == 'API Verification') { - if(!o[status.context]) o[status.context] = {type: 'check', state: status.state}; - } else if(status.context == 'Ubuntu Packaging') { - if(!o[status.context]) o[status.context] = {type: 'build', state: status.state}; - } else if(status.context == 'check/web/file-size') { - // Ignore check/web/file-size -- we won't block automerge for this at this point - } else { - // We fail with an 'unknown status' response if we get a new status check - // so we can be sure we are not skipping known status checks - o[status.context] = {type: 'unknown', state: status.state}; - } - return o; - - }, {}); - return filtered_statuses; - } - - // - // Given the collection of status checks we care about, return - // an aggregate status -- error, failed, pending, or success, - // and a summary description - // - function calculateFinalStatus(filtered_statuses) { - const counts = {}; - let hasBuilds = false; - for(const context of Object.keys(filtered_statuses)) { - const { state, type } = filtered_statuses[context]; - if(type == 'unknown') { - // We special-case for unknown status checks, and never permit them - return [ - 'error', `An unknown context ${context} was found, cannot calculate build status.` - ]; - } - if(type == 'build') { - hasBuilds = true; - } - counts[state] = counts[state] ? counts[state] + 1 : 1; - } - - // If we do not have any statuses yet, we wait - if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) { - return ['pending', 'Builds have not yet been triggered ⌛']; - } - - const state = - counts.error ? 'error' : - counts.failed ? 'failed' : - counts.pending ? 'pending' : - 'success'; - - let description = ''; - function appendDescription(count, state) { - if(!count) return; - if(description != '') description += '; '; - description += `${count} check${count == 1 ? '' : 's'} ${state}`; - } - appendDescription(counts.error, 'in an error state ❌'); - appendDescription(counts.failed, 'failed ❌'); - appendDescription(counts.pending, 'pending ⌛'); - appendDescription(counts.success, 'completed successfully ✅'); - - return [ state, description ]; - } - - async function getCommitStatuses(github, owner, repo, sha) { - const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', { - owner, - repo, - sha, - headers: { - 'X-GitHub-Api-Version': '2022-11-28' - } - }); - return statuses; - } - - async function getCommitCheckRuns(github, owner, repo, sha) { - const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', { - owner, - repo, - sha, - headers: { - 'X-GitHub-Api-Version': '2022-11-28' - } - }); - return statuses; - } - - function calculateCheckResult(statuses) { - if(!Array.isArray(statuses)) { - return ['error', 'Failed to retrieve status checks from GitHub ❌']; - } - - const filtered_statuses = reduceStatuses(statuses); - - const result = calculateFinalStatus(filtered_statuses); - return result; - } - - async function test(github, owner, repo, sha) { - // Get statuses from sha - const statuses = await getCommitStatuses(github, owner, repo, sha); - return calculateCheckResult(statuses); - } - - async function createCheck(github, owner, repo, sha) { - const check = await github.rest.checks.create({ - owner, - repo, - head_sha: sha, - name: 'Build Outcome', - status: 'in_progress', - }); - return check.data.id; - } - - async function updateCheck(github, owner, repo, checkRunId, status, description) { - const checkStatus = status == 'pending' ? 'in_progress' : 'completed'; - const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure'); - - await github.rest.checks.update({ - owner, - repo, - check_run_id: checkRunId, - status: checkStatus, - conclusion, - output: { - title: description, - summary: '' - } - }); - } - - const { owner, repo } = context.repo; - const sha = - context.payload?.check_suite?.sha || /* check run completed */ - context.payload?.inputs?.commit || /* manual run */ - context.payload?.after || /* push */ - context.payload?.commit?.sha || /* status */ - context.sha; /* probably 'master'! */ - - const checkRunId = await createCheck(github, owner, repo, sha); - const res = await test(github, owner, repo, sha); - await updateCheck(github, owner, repo, checkRunId, res[0], res[1]); - + script: | + // This code is copied out of resources/build/pr-build-status/pr-build-status.mjs + // where it is tested. It is copied inline here in order to avoid requiring the + // repository to be checked out, which dramatically reduces the run time of the + // check. + // + // Note: we don't currently look at check runs, only statuses + // + // Verify the following statuses: + // 'user_testing' + // 'API Verification' (github-actions[bot]) + // + // At least 1 of the following statuses must be found: + // 'Test*' (keyman-server), e.g. 'Test Build (Keyman)' + // 'Ubuntu Packaging' (github-actions[bot]) + // + // Ignore the following statuses: + // check/web/file-size + // + + function addLog(message) { + console.log(message); + return message + String.fromCharCode(10); + } + + function addStatus(o, type, context, state) { + if(!o[context]) { + o[context] = {type, state}; + return addLog(`Found ${context}: ${state}`); + } else { + return addLog(`Skipping repeated ${context}: ${state}`); + } + } + + function reduceStatuses(statuses) { + let summary = ''; + const filtered_statuses = statuses.reduce((o, status) => { + if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) { + summary += addStatus(o, 'build', status.context, status.state); + } else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') { + summary += addStatus(o, 'user-test', status.context, status.state); + } else if(status.context == 'API Verification') { + summary += addStatus(o, 'check', status.context, status.state); + } else if(status.context == 'Ubuntu Packaging') { + summary += addStatus(o, 'build', status.context, status.state); + } else if(status.context == 'check/web/file-size') { + // Ignore check/web/file-size -- we won't block automerge for this at this point + summary += addLog(`Skipping ${status.context}`); + } else { + // We fail with an 'unknown status' response if we get a new status check + // so we can be sure we are not skipping known status checks + summary += addLog(`Found unknown ${status.context}: ${status.state}`); + o[status.context] = {type: 'unknown', state: status.state}; + } + return o; + + }, {}); + return { filtered_statuses, summary }; + } + + // + // Given the collection of status checks we care about, return + // an aggregate status -- error, failure, pending, or success, + // and a summary description + // + function calculateFinalStatus(filtered_statuses, summary) { + const counts = {}; + let hasBuilds = false; + for(const context of Object.keys(filtered_statuses)) { + const { state, type } = filtered_statuses[context]; + if(type == 'unknown') { + // We special-case for unknown status checks, and never permit them + console.error(`An unknown context ${context} was found, cannot calculate build status.`); + return [ + 'error', `An unknown context ${context} was found, cannot calculate build status.`, summary + ]; + } + if(type == 'build') { + hasBuilds = true; + } + counts[state] = counts[state] ? counts[state] + 1 : 1; + } + + // If we do not have any statuses yet, we wait + if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) { + console.log(`Builds not yet triggered`); + return ['pending', 'Builds have not yet been triggered ⌛', summary]; + } + + const state = + counts.error ? 'error' : + counts.failure ? 'failure' : + counts.pending ? 'pending' : + 'success'; + + let description = ''; + function appendDescription(count, state) { + if(!count) return; + if(description != '') description += '; '; + description += `${count} check${count == 1 ? '' : 's'} ${state}`; + } + appendDescription(counts.error, 'in an error state ❌'); + appendDescription(counts.failure, 'failed ❌'); + appendDescription(counts.pending, 'pending ⌛'); + appendDescription(counts.success, 'completed successfully ✅'); + + console.log(`Finished filtering and reviewing status checks: ${state}: ${description}`); + return [ state, description, summary ]; + } + + async function getCommitStatuses(github, owner, repo, sha) { + console.log(`Getting commit statuses for ${sha}`); + const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', { + owner, + repo, + sha, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }); + return statuses; + } + + async function getCommitCheckRuns(github, owner, repo, sha) { + console.log(`Getting commit check runs for ${sha}`); + const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', { + owner, + repo, + sha, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }); + return statuses; + } + + function calculateCheckResult(statuses) { + if(!Array.isArray(statuses)) { + return ['error', 'Failed to retrieve status checks from GitHub ❌', 'statuses is not an array']; + } + + const { filtered_statuses, summary } = reduceStatuses(statuses); + return calculateFinalStatus(filtered_statuses, summary); + } + + async function test(github, owner, repo, sha) { + // Get statuses from sha + const statuses = await getCommitStatuses(github, owner, repo, sha); + return calculateCheckResult(statuses); + } + + async function createCheck(github, owner, repo, sha) { + const check = await github.rest.checks.create({ + owner, + repo, + head_sha: sha, + name: 'Build Outcome', + status: 'in_progress', + }); + return check.data.id; + } + + async function updateCheck(github, owner, repo, checkRunId, status, description, summary) { + const checkStatus = status == 'pending' ? 'in_progress' : 'completed'; + const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure'); + + await github.rest.checks.update({ + owner, + repo, + check_run_id: checkRunId, + status: checkStatus, + conclusion, + output: { + title: description, + summary + } + }); + } + + const { owner, repo } = context.repo; + const sha = + context.payload?.check_suite?.sha || /* check run completed */ + context.payload?.inputs?.commit || /* manual run */ + context.payload?.after || /* push */ + context.payload?.commit?.sha || /* status */ + context.sha; /* probably 'master'! */ + + const checkRunId = await createCheck(github, owner, repo, sha); + const res = await test(github, owner, repo, sha); + await updateCheck(github, owner, repo, checkRunId, res[0], res[1], res[2]); + diff --git a/HISTORY.md b/HISTORY.md index e553e13395..9361b9a402 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,50 @@ # Keyman Version History +## 19.0.117 alpha 2025-09-11 + +* fix(resources): ignore KEYMAN_VERSION_FOR_FILENAME in test (#14730) +* fix(resources): pr build status - find 'failure' state in status checks and log (#14732) + +## 19.0.116 alpha 2025-09-10 + +* fix(windows): kmshell continues when sm is in waitingrestart and returns to idle (#14644) +* maint(linux): allow to skip API check (#14699) +* maint(resources): adding GitHub actions workflow for Docker builds (#14674) +* maint(developer): use versioned filenames for PR artifacts (#14701) +* maint(windows): use versioned filenames for PR artifacts (#14702) +* maint(android): use versioned filenames for PR artifacts (#14703) +* maint(ios): use versioned filenames for PR artifacts (#14704) +* maint(mac): use versioned filenames for PR artifacts (#14705) +* maint(resources): rename KEYMAN_VERSION_FOR_PR_FILENAME (#14720) + +## 19.0.115 alpha 2025-09-09 + +* fix(linux): don't crash if `kmp.json` is missing `keyboards` section (#14708) + +## 19.0.114 alpha 2025-09-08 + +* docs(web): Improve empty What's New page for Web (#14660) +* maint(linux): remove obsolete Makefile (#14694) +* maint(resources): honor configure comment character in commit message (#14695) +* fix(web): fix error trying to calculate style scaling effects (#14684) +* refactor(resources): remove shellcheck warnings (#14698) + +## 19.0.113 alpha 2025-09-04 + +* fix(android): Update KMSample1 layout when changing orientation (#14678) + +## 19.0.112 alpha 2025-09-03 + +* fix(developer): add clear messages about validation failures to New LM Project Parameters form (#14659) +* docs(developer): tweak get-started for kmc and add entry to HISTORY.md (#14667) +* fix(android): Cleanup list layout in Engine and FirstVoices for edge to edge (#14655) +* docs(common): update license date and owner (#14668) +* maint(linux): skip tarball creation for `Build-bot: build` buildLevel (#14671) +* docs(common): update docs Keyman Desktop → Keyman for Windows (#14669) +* feat(web): adds transform cataloging methods to predictive-text worker (#14581) +* feat(web): batch correction-search paths by related subsets (#14592) +* refactor(web): simplify edit-distance calculation-object inputs (#14618) + ## 19.0.111 alpha 2025-09-02 * change(developer): update SIL logo (#14656) diff --git a/VERSION.md b/VERSION.md index ee647f65c3..773f715f5c 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -19.0.112 \ No newline at end of file +19.0.118 \ No newline at end of file diff --git a/android/KMAPro/build.sh b/android/KMAPro/build.sh index 74fed10588..06370c06d2 100755 --- a/android/KMAPro/build.sh +++ b/android/KMAPro/build.sh @@ -46,7 +46,7 @@ MODEL_PACKAGE_ID="nrc.en.mtnt" MODELS_TARGET="android/KMAPro/kMAPro/src/main/assets/${MODEL_PACKAGE_ID}.model.kmp" builder_describe_outputs \ configure "/${MODELS_TARGET}" \ - build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION}.apk + build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk #### Build @@ -77,13 +77,15 @@ if builder_start_action build; then # Copy Keyman Engine for Android cp "${KEYMAN_ROOT}/android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar" "${KEYMAN_ROOT}/android/KMAPro/kMAPro/libs/keyman-engine.aar" - + # Convert markdown to html for offline help build_help_html android KMAPro/kMAPro/src/main/assets/info echo "BUILD_FLAGS $BUILD_FLAGS" ./gradlew $DAEMON_FLAG clean $BUILD_FLAGS + mv "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION}.apk" "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk" + builder_finish_action success build fi diff --git a/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java b/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java index 86e1f5af22..add6bc5d63 100644 --- a/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java +++ b/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java @@ -2,7 +2,9 @@ package com.keyman.kmsample1; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Point; import android.os.Bundle; +import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; @@ -25,6 +27,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene public static Context context; private ConstraintLayout constraintLayout; private KMTextView textView; + private int lastOrientation = Configuration.ORIENTATION_UNDEFINED; @Override protected void onCreate(Bundle savedInstanceState) { @@ -99,6 +102,18 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene protected void onResume() { super.onResume(); KMManager.onResume(); + + // onConfigurationChanged() only triggers when device is rotated while app is in foreground + // This handles when device is rotated while app is in background + // using KMManager.getOrientation() since getConfiguration().orientation is unreliable #10241 + int newOrientation = KMManager.getOrientation(context); + if (newOrientation != lastOrientation) { + lastOrientation = newOrientation; + Configuration newConfig = this.getResources().getConfiguration(); + KMManager.onConfigurationChanged(newConfig); + } + resizeTextView(textView.isKeyboardVisible()); + KMManager.addKeyboardEventListener(this); KMKeyboardDownloaderActivity.addKeyboardDownloadEventListener(this); } @@ -116,6 +131,10 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + KMManager.onConfigurationChanged(newConfig); + + resizeTextView(textView.isKeyboardVisible()); + lastOrientation = newConfig.orientation; } @Override @@ -131,11 +150,44 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene @Override public void onKeyboardShown() { // Handle Keyman keyboard shown event here if needed + resizeTextView(true); } @Override public void onKeyboardDismissed() { // Handle Keyman keyboard dismissed event here if needed + resizeTextView(false); + } + + public void resizeTextView(boolean isKeyboardVisible) { + int bannerHeight = 0; + int keyboardHeight = 0; + if (isKeyboardVisible) { + bannerHeight = KMManager.getBannerHeight(this); + keyboardHeight = KMManager.getKeyboardHeight(this); + } + + TypedValue outValue = new TypedValue(); + getTheme().resolveAttribute(android.R.attr.actionBarSize, outValue, true); + int actionBarHeight = getResources().getDimensionPixelSize(outValue.resourceId); + + // *** TO DO: Try to check if status bar is visible, set statusBarHeight to 0 if it is not visible *** + int statusBarHeight = 0; + int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) + statusBarHeight = getResources().getDimensionPixelSize(resourceId); + + // Navigation bar height + int navigationBarHeight = 0; + resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android"); + if (resourceId > 0) { + navigationBarHeight = getResources().getDimensionPixelSize(resourceId); + } + + Point size = KMManager.getWindowSize(context); + int screenHeight = size.y; + textView.setHeight( + screenHeight - statusBarHeight - actionBarHeight - bannerHeight - keyboardHeight - navigationBarHeight); } @Override diff --git a/android/build.sh b/android/build.sh index 9d48effbf1..54e61dd9a5 100755 --- a/android/build.sh +++ b/android/build.sh @@ -62,11 +62,12 @@ function do_test_help() { function archive_artifacts() { local UPLOAD_PATH KEYMAN_ENGINE_ANDROID_ZIP KEYMAN_APK FIRSTVOICES_APK + local KEYMAN_FULLY_VERSIONED_APK FIRSTVOICES_FULLY_VERSIONED_APK UPLOAD_PATH="${KEYMAN_ROOT}/android/upload/${KEYMAN_VERSION}" KEYMAN_ENGINE_ANDROID_ZIP="keyman-engine-android-${KEYMAN_VERSION}.zip" - KEYMAN_APK="keyman-${KEYMAN_VERSION}.apk" - FIRSTVOICES_APK="firstvoices-${KEYMAN_VERSION}.apk" + KEYMAN_APK="keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk" + FIRSTVOICES_APK="firstvoices-${KEYMAN_VERSION_FOR_FILENAME}.apk" rm -rf "${UPLOAD_PATH}" mkdir -p "${UPLOAD_PATH}" @@ -87,12 +88,12 @@ function archive_artifacts() { ) # Copy release APK - cp "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/release/${KEYMAN_APK}" "${UPLOAD_PATH}" + cp "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/release/${KEYMAN_APK}" "${UPLOAD_PATH}/${KEYMAN_APK}" # FirstVoices app if [[ "${RELEASE_OEM_FIRSTVOICES-false}" = true ]]; then - cp "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/release/${FIRSTVOICES_APK}" "${UPLOAD_PATH}" + cp "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/release/${FIRSTVOICES_APK}" "${UPLOAD_PATH}/${FIRSTVOICES_APK}" fi # diff --git a/developer/docs/help/index.md b/developer/docs/help/index.md index 771bae620c..0b2f452087 100644 --- a/developer/docs/help/index.md +++ b/developer/docs/help/index.md @@ -8,7 +8,7 @@ and tutorials, and full reference information. ## Guides and Tutorials - [What is Keyman Developer?](guides/intro) -- [What's new in 19.0](whats-new) +- [What's new in 19.0](whatsnew) - [Developing Keyman keyboard layouts](guides/develop) - [Testing Keyman keyboards](guides/test) - [Distributing Keyman keyboards](guides/distribute) @@ -35,4 +35,4 @@ and tutorials, and full reference information. - [Keyman Engine for Windows](/developer/engine/windows/current-version/) - [Keyman Engine for Web](/developer/engine/web/) - [Keyman Engine for iPhone and iPad](/developer/engine/iphone-and-ipad/current-version/) -- [Keyman Engine for Android](/developer/engine/android/current-version/) \ No newline at end of file +- [Keyman Engine for Android](/developer/engine/android/current-version/) diff --git a/developer/docs/help/whats-new.md b/developer/docs/help/whatsnew.md similarity index 100% rename from developer/docs/help/whats-new.md rename to developer/docs/help/whatsnew.md diff --git a/developer/src/inst/build.sh b/developer/src/inst/build.sh index 8b37048e1e..5d44f228eb 100755 --- a/developer/src/inst/build.sh +++ b/developer/src/inst/build.sh @@ -16,7 +16,7 @@ builder_describe "Installation files for Keyman Developer" \ # after all other builds complete builder_describe_outputs \ - publish /developer/src/inst/keymandeveloper-${KEYMAN_VERSION}.exe + publish /developer/src/inst/keymandeveloper-${KEYMAN_VERSION_FOR_FILENAME}.exe builder_parse "$@" @@ -78,7 +78,7 @@ function do_publish() { # Copy the installation archive # - copy-kmdev + copy-keymandeveloper-installer-to-artifacts builder_if_release_build_level verify-installer-signatures } @@ -87,8 +87,8 @@ function do_test() { verify-node-installer-version } -function copy-kmdev() { - builder_heading copy-kmdev +function copy-keymandeveloper-installer-to-artifacts() { + builder_heading copy-keymandeveloper-installer-to-artifacts mkdir -p "$DEVELOPER_ROOT/release/${KEYMAN_VERSION}" @@ -96,7 +96,7 @@ function copy-kmdev() { make-kmc-install-zip cp -f "$DEVELOPER_ROOT/src/inst/keymandeveloper.msi" "$DEVELOPER_ROOT/release/${KEYMAN_VERSION}/keymandeveloper.msi" - cp -f "$DEVELOPER_ROOT/src/inst/keymandeveloper-${KEYMAN_VERSION}.exe" "$DEVELOPER_ROOT/release/${KEYMAN_VERSION}/keymandeveloper-${KEYMAN_VERSION}.exe" + cp -f "$DEVELOPER_ROOT/src/inst/keymandeveloper-${KEYMAN_VERSION_FOR_FILENAME}.exe" "$DEVELOPER_ROOT/release/${KEYMAN_VERSION}/keymandeveloper-${KEYMAN_VERSION_FOR_FILENAME}.exe" } function verify-program-signatures() { @@ -234,8 +234,8 @@ function make-installer() { echo "MSIFileName=keymandeveloper.msi" >> setup.inf echo "Title=Keyman Developer ${KEYMAN_VERSION_WITH_TAG}" >>setup.inf add_zip_files setup.zip keymandeveloper.msi setup.inf - cat "$DEVELOPER_PROGRAM/setup.exe" setup.zip > "keymandeveloper-$KEYMAN_VERSION.exe" - wrap-signcode //d "Keyman Developer" "keymandeveloper-$KEYMAN_VERSION.exe" + cat "$DEVELOPER_PROGRAM/setup.exe" setup.zip > "keymandeveloper-$KEYMAN_VERSION_FOR_FILENAME.exe" + wrap-signcode //d "Keyman Developer" "keymandeveloper-$KEYMAN_VERSION_FOR_FILENAME.exe" # # Zip the files we distribute as part of the standalone kmc distro into release\$Version\kmcomp-$Version.zip @@ -251,7 +251,7 @@ function make-kmc-install-zip() { cd "${DEVELOPER_ROOT}/bin" # TODO: rename this to keyman-developer-cli-$Version.zip - local KMCOMP_ZIP="${DEVELOPER_ROOT}/release/${KEYMAN_VERSION}/kmcomp-${KEYMAN_VERSION}.zip" + local KMCOMP_ZIP="${DEVELOPER_ROOT}/release/${KEYMAN_VERSION}/kmcomp-${KEYMAN_VERSION_FOR_FILENAME}.zip" add_zip_files "${KMCOMP_ZIP}" -q -r \ kmconvert.exe \ diff --git a/docs/core-api-verification.md b/docs/core-api-verification.md index fc3e0f629d..0bf266676f 100644 --- a/docs/core-api-verification.md +++ b/docs/core-api-verification.md @@ -99,6 +99,17 @@ the API version number in `CORE_API_VERSION.md`. If the API version numbers in `CORE_API_VERSION.md` and in `libkeymancore.symbols` don't match the API verification will fail. +## Skipping the API check + +Sometimes we have a PR that intentionally does not change the API version +number. To skip the API checks in this case, the trailer + +```text +Keyman-Api-Check: skip +``` + +can be added to the PR description. + ## How this works `.github/workflows/api-verification.yml` contains a `Verify API` step diff --git a/docs/minimum-versions.md b/docs/minimum-versions.md index 96b6e820a5..19e0bf4420 100644 --- a/docs/minimum-versions.md +++ b/docs/minimum-versions.md @@ -73,6 +73,6 @@ https://help.keyman.com/developer/engine/android/latest-version/ | KEYMAN_VERSION_JAVA | 21 | | KEYMAN_VERSION_LANGTAGS | 2025-02-18 | | KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY | 2025-03-10 | -| KEYMAN_VERSION_UNICODE | 16.0.0 | +| KEYMAN_VERSION_UNICODE | 17.0.0 | > ### This file is auto-generated by publish-minimum-versions.sh. Do not modify diff --git a/ios/tools/prepRelease.sh b/ios/tools/prepRelease.sh index 28adfbe1ff..8065aad2f8 100755 --- a/ios/tools/prepRelease.sh +++ b/ios/tools/prepRelease.sh @@ -29,8 +29,7 @@ fi . $KEYMAN_ROOT/resources/build/history/history-utils.sh #includes the following #. ../resources/build/utils.inc.sh -BUILD_NUMBER=`cat ../VERSION.md` -KEYMAN_TIER=`cat ../TIER.md` +BUILD_NUMBER="${KEYMAN_VERSION}" KEYMAN_CHANGELOG="changelog-${BUILD_NUMBER}-${KEYMAN_TIER}.txt" CHANGELOG_PATH="upload/$BUILD_NUMBER/$KEYMAN_CHANGELOG" @@ -59,7 +58,7 @@ mv -f "$CHANGELOG_PATH.1" "$CHANGELOG_PATH" # Keyman Engine # -KMEI_DST_NAME="keyman-engine-ios-${BUILD_NUMBER}.zip" +KMEI_DST_NAME="keyman-engine-ios-${KEYMAN_VERSION_FOR_FILENAME}.zip" KMEI_DST="${WORK_DIR}/${UPLOAD_DIR}/${KMEI_DST_NAME}" KMEI_FRAMEWORK_BASE="build/Build/Products/Release/" @@ -87,14 +86,14 @@ cd "$WORK_DIR" # KEYMANAPP_IPA="build/Build/Products/Release-iphoneos/Keyman.ipa" -KEYMANAPP_IPA_DST="keyman-ios-${BUILD_NUMBER}.ipa" +KEYMANAPP_IPA_DST="keyman-ios-${KEYMAN_VERSION_FOR_FILENAME}.ipa" echo "Copying Keyman IPA ${KEYMANAPP_IPA} => ${UPLOAD_DIR}/${KEYMANAPP_IPA_DST}..." cp "${KEYMANAPP_IPA}" "${WORK_DIR}/${UPLOAD_DIR}/${KEYMANAPP_IPA_DST}" KEYMANAPP_SIM_FOLDER="build/Build/Products/Release-iphonesimulator" KEYMANAPP_SIM_APP="$KEYMANAPP_SIM_FOLDER/Keyman.app" -KEYMANAPP_SIM_APP_DST="keyman-ios-simulator-${BUILD_NUMBER}.app.zip" +KEYMANAPP_SIM_APP_DST="keyman-ios-simulator-${KEYMAN_VERSION_FOR_FILENAME}.app.zip" echo "Zipping Keyman simulator artifact ${KEYMANAPP_SIM_APP} => ${UPLOAD_DIR}/${KEYMANAPP_SIM_APP_DST}..." cd "${KEYMANAPP_SIM_FOLDER}" @@ -108,14 +107,14 @@ cd "$WORK_DIR" if [ "${RELEASE_OEM_FIRSTVOICES}" = true ]; then FIRSTVOICESAPP_IPA="../oem/firstvoices/ios/build/Build/Products/Release-iphoneos/FirstVoices.ipa" - FIRSTVOICESAPP_IPA_DST="firstvoices-ios-${BUILD_NUMBER}.ipa" + FIRSTVOICESAPP_IPA_DST="firstvoices-ios-${KEYMAN_VERSION_FOR_FILENAME}.ipa" echo "Copying FirstVoices IPA ${FIRSTVOICESAPP_IPA} => ${UPLOAD_DIR}/${FIRSTVOICESAPP_IPA_DST}..." cp "${FIRSTVOICESAPP_IPA}" "${WORK_DIR}/${UPLOAD_DIR}/${FIRSTVOICESAPP_IPA_DST}" FIRSTVOICESAPP_SIM_FOLDER="../oem/firstvoices/ios/build/Build/Products/Release-iphonesimulator/" FIRSTVOICESAPP_SIM_APP="$FIRSTVOICESAPP_SIM_FOLDER/FirstVoices.app" - FIRSTVOICESAPP_SIM_APP_DST="firstvoices-ios-simulator-${BUILD_NUMBER}.app.zip" + FIRSTVOICESAPP_SIM_APP_DST="firstvoices-ios-simulator-${KEYMAN_VERSION_FOR_FILENAME}.app.zip" echo "Zipping FirstVoices simulator artifact ${FIRSTVOICESAPP_SIM_APP} => ${UPLOAD_DIR}/${KEYMANAPP_SIM_APP_DST}..." cd "${FIRSTVOICESAPP_SIM_FOLDER}" diff --git a/linux/Makefile b/linux/Makefile deleted file mode 100644 index 0df264e43d..0000000000 --- a/linux/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/make - -default: # builds only - BUILDONLY="yes" ./scripts/build.sh - -clean: - rm -rf build-* - -reconf: clean - ./scripts/reconf.sh - -configure: - CONFIGUREONLY="yes" ./scripts/build.sh - -fullbuild: # configure and build - ./scripts/build.sh - -tmpinstall: reconf - INSTALLDIR="/tmp/keyman" ./scripts/build.sh - INSTALLDIR="/tmp/keyman" ./scripts/install.sh - -install: # run as sudo - SUDOINSTALL="yes" ./scripts/install.sh - -uninstall: # run as sudo - SUDOINSTALL="uninstall" ./scripts/install.sh - -sources: - ./scripts/dist.sh - -tmpsources: - PKG_CONFIG_PATH="../../core/build/arch/release/meson-private" ./scripts/dist.sh - -origdist: - ./scripts/dist.sh origdist - -sourcepackage: reconf origdist - ./scripts/deb.sh - -nightlydist: reconf sources - echo "Built nightly source release in dist/" - -check: - cd keyman-config && ./run-tests.sh - cd ibus-keyman && ./build.sh test - -tmpcheck: check - -tests: check diff --git a/linux/keyman-config/keyman_config/install_kmp.py b/linux/keyman-config/keyman_config/install_kmp.py index cb69d596af..8b47ed7857 100755 --- a/linux/keyman-config/keyman_config/install_kmp.py +++ b/linux/keyman-config/keyman_config/install_kmp.py @@ -178,9 +178,9 @@ class InstallKmp(): logging.info("Converting %s to LDML and installing both as as keyman file", f['name']) name, ext = os.path.splitext(f['name']) - ldml = convert_kvk_to_ldml(name, fpath) - ldmlfile = os.path.join(self.packageDir, f"{name}.ldml") - output_ldml(ldmlfile, ldml) + if ldml := convert_kvk_to_ldml(name, fpath): + ldmlfile = os.path.join(self.packageDir, f"{name}.ldml") + output_ldml(ldmlfile, ldml) elif ftype == KMFileTypes.KM_ICON: # Special handling of icon to convert to PNG logging.info("Converting %s to PNG and installing both as keyman files", diff --git a/linux/keyman-config/keyman_config/kvk2ldml.py b/linux/keyman-config/keyman_config/kvk2ldml.py index ddef89241a..35f5bcd908 100755 --- a/linux/keyman-config/keyman_config/kvk2ldml.py +++ b/linux/keyman-config/keyman_config/kvk2ldml.py @@ -366,14 +366,17 @@ def convert_ldml(keyboardName, kvkData, kmpJsonFilename): info, system, options, keyboards, files = parsemetadata(kmpJsonFilename) + if not keyboards: + return None + ldml = etree.Element("keyboard", locale="zzz-keyman") + for keyboard in keyboards: if keyboard['id'] != keyboardName: continue if 'oskFont' in keyboard: fontFile = os.path.join(os.path.dirname(kmpJsonFilename), keyboard['oskFont']) - font = _fontFacename(fontFile) - if font: + if font := _fontFacename(fontFile): ldml.set('keymanFacename', font) break diff --git a/linux/keyman-config/tests/kvk2ldml_tests.py b/linux/keyman-config/tests/kvk2ldml_tests.py index fc8a34c28b..4bb9c337c0 100644 --- a/linux/keyman-config/tests/kvk2ldml_tests.py +++ b/linux/keyman-config/tests/kvk2ldml_tests.py @@ -8,31 +8,34 @@ import unittest from keyman_config.kvk2ldml import KVKData, NFont, convert_ldml class Kvk2LdmlTests(unittest.TestCase): - def _createKmpJson(self, packagedir): + def _createKmpJson(self, packagedir, add_keyboards=True): kmpJsonFilename = os.path.join(packagedir, 'kmp.json') + system = '''"system": { + "keymanDeveloperVersion": "18.0", + "fileVersion": "7.0" + }''' + files = '''"files": [ { + "name": "khmer_angkor.kmx", + "description": "Keyboard Khmer Angkor" + }, { + "name": "kmp.json", + "description": "Package information (JSON)" + } ]''' + if add_keyboards: + keyboards = '''"keyboards": [ { + "name": "Khmer Angkor", + "id": "khmer_angkor", + "version": "1.5", + "oskFont": "keymanweb-osk.ttf", + "languages": [ { + "name": "Central Khmer (Khmer, Cambodia)", + "id": "km" + } ]} + ]''' + else: + keyboards = '' with open(kmpJsonFilename, 'w') as file: - file.write('''{ - "system": { - "keymanDeveloperVersion": "18.0", - "fileVersion": "7.0" - }, - "files": [ { - "name": "khmer_angkor.kmx", - "description": "Keyboard Khmer Angkor" - }, { - "name": "kmp.json", - "description": "Package information (JSON)" - } ], - "keyboards": [ { - "name": "Khmer Angkor", - "id": "khmer_angkor", - "version": "1.5", - "oskFont": "keymanweb-osk.ttf", - "languages": [ { - "name": "Central Khmer (Khmer, Cambodia)", - "id": "km" - } ]} - ]}''') + file.write(f'{{ {system}, {files}, {keyboards} }}') return kmpJsonFilename def test_convert_ldml__adds_keymanFacename(self): @@ -56,3 +59,20 @@ class Kvk2LdmlTests(unittest.TestCase): self.assertEqual(ldml.get('keymanFacename'), 'SymChar') workdir.cleanup() + + def test_convert_ldml__no_crash_without_keyboards(self): + # Setup + keyboardName = 'khmer_angkor' + kvkData = KVKData() + kvkData.AssociatedKeyboard = keyboardName + + workdir = tempfile.TemporaryDirectory() + kmpJsonFilename = self._createKmpJson(workdir.name, add_keyboards=False) + + # Execute + ldml = convert_ldml(keyboardName, kvkData, kmpJsonFilename) + + # Verify + self.assertIsNone(ldml) + + workdir.cleanup() diff --git a/mac/Keyman4MacIM/make-km-dmg.sh b/mac/Keyman4MacIM/make-km-dmg.sh index 0ee1d7395b..d949a96af3 100755 --- a/mac/Keyman4MacIM/make-km-dmg.sh +++ b/mac/Keyman4MacIM/make-km-dmg.sh @@ -212,7 +212,7 @@ if (( DETACH_SUCCESS < 999 )); then fi # Step 5 - Convert image to a compressed readonly DMG image -DMG_FILE_PATH="$DEST_DIR/keyman-$KEYMAN_VERSION.dmg" +DMG_FILE_PATH="$DEST_DIR/keyman-$KEYMAN_VERSION_FOR_FILENAME.dmg" builder_echo info "Converting/compressing image to create \"$DMG_FILE_PATH\"" if [[ -e "$DMG_FILE_PATH" ]] ; then if [[ "$VERBOSITY" != "-quiet" ]] ; then diff --git a/mac/build.sh b/mac/build.sh index 875e4b5013..e032db998a 100755 --- a/mac/build.sh +++ b/mac/build.sh @@ -293,7 +293,7 @@ do_publish() { "${KM4MIM_BASE_PATH}/make-km-dmg.sh" local UPLOAD_PATH="${KM4MIM_BASE_PATH}/output/upload/${KEYMAN_VERSION}" - write_download_info "${UPLOAD_PATH}" "keyman-${KEYMAN_VERSION}.dmg" "Keyman4MacIM" dmg mac + write_download_info "${UPLOAD_PATH}" "keyman-${KEYMAN_VERSION_FOR_FILENAME}.dmg" "Keyman4MacIM" dmg mac if builder_is_ci_build && builder_is_ci_build_level_release; then do_sentry diff --git a/oem/firstvoices/android/build.sh b/oem/firstvoices/android/build.sh index 00de2d98ec..2d8d30141f 100755 --- a/oem/firstvoices/android/build.sh +++ b/oem/firstvoices/android/build.sh @@ -38,7 +38,7 @@ if builder_is_debug_build; then TEST_FLAGS="-x assembleDebug lintDebug testDebug" fi -ARTIFACT="firstvoices-$KEYMAN_VERSION.apk" +ARTIFACT="firstvoices-${KEYMAN_VERSION_FOR_FILENAME}.apk" KEYBOARD_PACKAGE_ID="fv_all" KEYBOARDS_TARGET="oem/firstvoices/android/app/src/main/assets/${KEYBOARD_PACKAGE_ID}.kmp" @@ -76,6 +76,8 @@ if builder_start_action build; then echo "BUILD_FLAGS: $BUILD_FLAGS" ./gradlew $DAEMON_FLAG clean $BUILD_FLAGS + mv "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/$CONFIG/firstvoices-${KEYMAN_VERSION}.apk" "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/$CONFIG/${ARTIFACT}" + builder_finish_action success build fi diff --git a/oem/firstvoices/windows/src/inst/build.sh b/oem/firstvoices/windows/src/inst/build.sh index ac245b72aa..cad414d39b 100755 --- a/oem/firstvoices/windows/src/inst/build.sh +++ b/oem/firstvoices/windows/src/inst/build.sh @@ -16,7 +16,7 @@ builder_describe "Installation files for FirstVoices Keyboards" \ # after all other builds complete builder_if_release_build_level builder_describe_outputs \ - publish /windows/release/${KEYMAN_VERSION}/firstvoices-${KEYMAN_VERSION}.exe + publish /windows/release/${KEYMAN_VERSION}/firstvoices-${KEYMAN_VERSION_FOR_FILENAME}.exe builder_parse "$@" @@ -103,7 +103,7 @@ function copy-installer() { mkdir -p "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}" cp firstvoices.msi "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/firstvoices.msi" - cp firstvoices.exe "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/firstvoices-${KEYMAN_VERSION}.exe" + cp firstvoices.exe "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/firstvoices-${KEYMAN_VERSION_FOR_FILENAME}.exe" verify-installer-signatures } diff --git a/resources/build/builder-basic.inc.sh b/resources/build/builder-basic.inc.sh index 83f77c3134..94441617b2 100644 --- a/resources/build/builder-basic.inc.sh +++ b/resources/build/builder-basic.inc.sh @@ -13,6 +13,7 @@ # KEYMAN_VERSION_GIT_TAG: Git tag for the release, "release@$KEYMAN_VERSION_WITH_TAG", e.g. "release@14.0.1-alpha-test-1234" # KEYMAN_ROOT: fully resolved root path of Keyman repository # KEYMAN_VERSION_ENVIRONMENT: One of: local, test, alpha, beta, stable +# KEYMAN_VERSION_FOR_FILENAME: KEYMAN_VERSION, for release builds, or KEYMAN_VERSION_WITH_TAG, for PR and local builds; see #10521 # UPLOAD_SENTRY: true - if KEYMAN_VERSION_ENVIRONMENT is one of alpha, beta, stable # false - if local, test. Indicates if debug artifacts should be uploaded to Sentry # BUILDER_OS: win|mac|linux -- current build environment @@ -115,6 +116,16 @@ function _builder_basic_find_version() { KEYMAN_VERSION_WITH_TAG="$KEYMAN_VERSION$KEYMAN_VERSION_TAG" KEYMAN_VERSION_GIT_TAG="release@$KEYMAN_VERSION_WITH_TAG" + # For local builds, and for PR builds, we will use a filename including the tag + # but for release builds, we omit the tag for now. In the future, we may include + # the tag but that involves changing a number of other websites as dealing with + # the rename there too. + if builder_is_ci_release_build; then + KEYMAN_VERSION_FOR_FILENAME="${KEYMAN_VERSION}" + else + KEYMAN_VERSION_FOR_FILENAME="${KEYMAN_VERSION_WITH_TAG}" + fi + readonly KEYMAN_VERSION readonly KEYMAN_VERSION_MAJOR readonly KEYMAN_VERSION_MINOR @@ -125,6 +136,7 @@ function _builder_basic_find_version() { readonly KEYMAN_VERSION_WITH_TAG readonly KEYMAN_VERSION_ENVIRONMENT readonly KEYMAN_VERSION_GIT_TAG + readonly KEYMAN_VERSION_FOR_FILENAME # Export version strings so places like version.gradle can access them export KEYMAN_VERSION @@ -137,6 +149,7 @@ function _builder_basic_find_version() { export KEYMAN_VERSION_WITH_TAG export KEYMAN_VERSION_ENVIRONMENT export KEYMAN_VERSION_GIT_TAG + export KEYMAN_VERSION_FOR_FILENAME } function _builder_basic_find_tier() { @@ -163,18 +176,19 @@ function _builder_basic_print_build_number_for_teamcity() { } function _builder_basic_print_version_utils_debug() { - echo "KEYMAN_ROOT: $KEYMAN_ROOT" - echo "KEYMAN_VERSION: $KEYMAN_VERSION" - echo "KEYMAN_VERSION_WIN: $KEYMAN_VERSION_WIN" - echo "KEYMAN_VERSION_RELEASE: $KEYMAN_VERSION_RELEASE" - echo "KEYMAN_VERSION_MAJOR: $KEYMAN_VERSION_MAJOR" - echo "KEYMAN_VERSION_MINOR: $KEYMAN_VERSION_MINOR" - echo "KEYMAN_VERSION_PATCH: $KEYMAN_VERSION_PATCH" - echo "KEYMAN_TIER: $KEYMAN_TIER" - echo "KEYMAN_VERSION_TAG: $KEYMAN_VERSION_TAG" - echo "KEYMAN_VERSION_WITH_TAG: $KEYMAN_VERSION_WITH_TAG" - echo "KEYMAN_VERSION_GIT_TAG: $KEYMAN_VERSION_GIT_TAG" - echo "KEYMAN_VERSION_ENVIRONMENT: $KEYMAN_VERSION_ENVIRONMENT" + echo "KEYMAN_ROOT: $KEYMAN_ROOT" + echo "KEYMAN_VERSION: $KEYMAN_VERSION" + echo "KEYMAN_VERSION_WIN: $KEYMAN_VERSION_WIN" + echo "KEYMAN_VERSION_RELEASE: $KEYMAN_VERSION_RELEASE" + echo "KEYMAN_VERSION_MAJOR: $KEYMAN_VERSION_MAJOR" + echo "KEYMAN_VERSION_MINOR: $KEYMAN_VERSION_MINOR" + echo "KEYMAN_VERSION_PATCH: $KEYMAN_VERSION_PATCH" + echo "KEYMAN_TIER: $KEYMAN_TIER" + echo "KEYMAN_VERSION_TAG: $KEYMAN_VERSION_TAG" + echo "KEYMAN_VERSION_WITH_TAG: $KEYMAN_VERSION_WITH_TAG" + echo "KEYMAN_VERSION_GIT_TAG: $KEYMAN_VERSION_GIT_TAG" + echo "KEYMAN_VERSION_ENVIRONMENT: $KEYMAN_VERSION_ENVIRONMENT" + echo "KEYMAN_VERSION_FOR_FILENAME: $KEYMAN_VERSION_FOR_FILENAME" } # TODO: consolidate with buildLevel, see #14285 @@ -209,6 +223,7 @@ replaceVersionStrings() { s/\$KEYMAN_VERSION_GIT_TAG/$KEYMAN_VERSION_GIT_TAG/g; s/\$KEYMAN_VERSION_ENVIRONMENT/$KEYMAN_VERSION_ENVIRONMENT/g; s/\$KEYMAN_VERSION/$KEYMAN_VERSION/g; + s/\$KEYMAN_VERSION_FOR_FILENAME/$KEYMAN_VERSION_FOR_FILENAME/g; " "$infile" > "$outfile" } @@ -249,6 +264,7 @@ replaceVersionStrings_Mkver() { s/\$KEYMAN_VERSION_WITH_TAG/$KEYMAN_VERSION_WITH_TAG/g; s/\$KEYMAN_VERSION_GIT_TAG/$KEYMAN_VERSION_GIT_TAG/g; s/\$KEYMAN_VERSION_ENVIRONMENT/$KEYMAN_VERSION_ENVIRONMENT/g; + s/\$KEYMAN_VERSION_FOR_FILENAME/$KEYMAN_VERSION_FOR_FILENAME/g; s/\$KEYMAN_VERSION/$KEYMAN_VERSION_WIN/g; diff --git a/resources/build/ci/trigger-build-bot.inc.sh b/resources/build/ci/trigger-build-bot.inc.sh index f7cdc815f8..d9fd38294f 100644 --- a/resources/build/ci/trigger-build-bot.inc.sh +++ b/resources/build/ci/trigger-build-bot.inc.sh @@ -39,6 +39,34 @@ function test_bot_check_pr_body() { fi } +# +# Check the Keyman-Api-Check command whether or not to run API checks. +# Any value other than 'skip' or no Keyman-Api-Check command will run +# the API checks. +# +# Parameters: +# 1: PR number (not currently used) +# 2: PR JSON data from api.github.com/repos/keymanapp/keyman/pulls/# +# +function is_skip_keyman_api_check() { + local PRNUM=$1 + local prinfo="$2" + local prbody prApiCheck + + set -o noglob + IFS=$'\n' + prbody="$(echo "${prinfo}" | "${JQ}" -r '.body')" + prApiCheck="$(echo -e "${prbody}" | grep '^Keyman-Api-Check:' | cut -d: -f 2 - | tr -d '[:space:]')" + unset IFS + set +o noglob + + if [[ "${prApiCheck}" == "skip" ]]; then + echo "true" + else + echo "false" + fi +} + # # Check PR commit messages for Build-bot commands. Later commands override # earlier ones. Also checks PR body for any overriding commands. Note, only diff --git a/resources/build/ci/trigger-builds.inc.sh b/resources/build/ci/trigger-builds.inc.sh index 0d54971b15..d1110a19a1 100644 --- a/resources/build/ci/trigger-builds.inc.sh +++ b/resources/build/ci/trigger-builds.inc.sh @@ -84,31 +84,34 @@ function triggerTeamCityBuild() { # 2: 'skip' - don't run build; 'build' - run build and unit tests; 'test' - run build, unit tests, and deploy # 3: Action name # 4: branch name in git +# 5: 'true' to skip API checks, 'false' to run them (optional, default 'false') # function triggerGitHubActionsBuild() { local IS_TEST_BUILD="$1" local BUILD_LEVEL="$2" local GITHUB_ACTION="$3" local GIT_BRANCH="$4" + local SKIP_API_CHECK="${5:-"false"}" local GIT_BASE_BRANCH="${GIT_BRANCH}" local GIT_USER="keyman-server" local GIT_BUILD_SHA GIT_BASE_REF JSON local GITHUB_SERVER=https://api.github.com/repos/keymanapp/keyman - if [ "${action:-""}" == "commit" ]; then + if [[ "${action:-""}" == "commit" ]]; then # This will only be true if we created and pushed a tag - GIT_BUILD_SHA="$(git rev-parse "refs/tags/release@$KEYMAN_VERSION_WITH_TAG")" + GIT_BUILD_SHA="$(git rev-parse "refs/tags/release@${KEYMAN_VERSION_WITH_TAG}")" GIT_BASE_REF="$(git rev-parse "${GIT_BUILD_SHA}^")" GIT_EVENT_TYPE="${GITHUB_ACTION}: release@${KEYMAN_VERSION_WITH_TAG}" - elif [[ $GIT_BRANCH =~ ^[0-9]+$ ]]; then + elif [[ ${GIT_BRANCH} =~ ^[0-9]+$ ]]; then # pull request - local JSON=$(call_curl "${GITHUB_SERVER}/pulls/${GIT_BRANCH}" --header "Authorization: token $GITHUB_TOKEN") - GIT_BUILD_SHA="$(echo "$JSON" | $JQ -r '.head.sha')" + local JSON + JSON=$(call_curl "${GITHUB_SERVER}/pulls/${GIT_BRANCH}" --header "Authorization: token ${GITHUB_TOKEN}") + GIT_BUILD_SHA="$(echo "${JSON}" | ${JQ} -r '.head.sha')" GIT_EVENT_TYPE="${GITHUB_ACTION}: PR #${GIT_BRANCH}" - GIT_USER="$(echo "$JSON" | $JQ -r '.user.login')" - GIT_BASE_BRANCH="$(echo "$JSON" | $JQ -r '.base.ref')" - GIT_BASE_REF="$(echo "$JSON" | $JQ -r '.base.sha')" + GIT_USER="$(echo "${JSON}" | ${JQ} -r '.user.login')" + GIT_BASE_BRANCH="$(echo "${JSON}" | ${JQ} -r '.base.ref')" + GIT_BASE_REF="$(echo "${JSON}" | ${JQ} -r '.base.sha')" GIT_BRANCH="PR-${GIT_BRANCH}" else # another branch: stable-x.y, beta, or master @@ -118,20 +121,21 @@ function triggerGitHubActionsBuild() { fi local DATA="{ - \"event_type\": \"$GIT_EVENT_TYPE\", \ + \"event_type\": \"${GIT_EVENT_TYPE}\", \ \"client_payload\": { \ - \"buildSha\": \"$GIT_BUILD_SHA\", \ - \"branch\": \"$GIT_BRANCH\", \ - \"baseBranch\": \"$GIT_BASE_BRANCH\", \ - \"baseRef\": \"$GIT_BASE_REF\", \ - \"user\": \"$GIT_USER\", \ - \"isTestBuild\": \"$IS_TEST_BUILD\", \ - \"buildLevel\": \"$BUILD_LEVEL\" \ + \"buildSha\": \"${GIT_BUILD_SHA}\", \ + \"branch\": \"${GIT_BRANCH}\", \ + \"baseBranch\": \"${GIT_BASE_BRANCH}\", \ + \"baseRef\": \"${GIT_BASE_REF}\", \ + \"user\": \"${GIT_USER}\", \ + \"isTestBuild\": \"${IS_TEST_BUILD}\", \ + \"buildLevel\": \"${BUILD_LEVEL}\", \ + \"skipApiCheck\": \"${SKIP_API_CHECK}\" \ }}" - echo "GitHub Action Data: $DATA" + echo "GitHub Action Data: ${DATA}" - if [[ "$GIT_BUILD_SHA" == null ]]; then + if [[ "${GIT_BUILD_SHA}" == null ]]; then echo "Invalid GIT_BUILD_SHA" exit 1 fi @@ -141,8 +145,8 @@ function triggerGitHubActionsBuild() { call_curl "${GITHUB_SERVER}/dispatches" \ --request POST \ --header "Accept: application/vnd.github+json" \ - --header "Authorization: token $GITHUB_TOKEN" \ - --data "$DATA" + --header "Authorization: token ${GITHUB_TOKEN}" \ + --data "${DATA}" echo } @@ -157,31 +161,32 @@ function call_curl() { local curl_result local http_code - local output_file=$(mktemp) + local output_file + output_file=$(mktemp) set +e if [[ $# -gt 0 ]]; then - http_code=$(curl --silent --output $output_file --no-progress-meter --write-out "%{http_code}" "$@" "$url") + http_code=$(curl --silent --output "${output_file}" --no-progress-meter --write-out "%{http_code}" "$@" "${url}") else - http_code=$(curl --silent --output $output_file --no-progress-meter --write-out "%{http_code}" "$url") + http_code=$(curl --silent --output "${output_file}" --no-progress-meter --write-out "%{http_code}" "${url}") fi curl_result=$? if [[ ${curl_result} != 0 ]]; then echo "curl call to '${url}' failed with code '${curl_result}'" >& 2 - >&2 cat $output_file - rm -f $output_file - exit $curl_result + >&2 cat "${output_file}" + rm -f "${output_file}" + exit ${curl_result} fi if [[ ${http_code} -lt 200 || ${http_code} -gt 299 ]] ; then echo "curl call to '${url}' failed with HTTP error code '${http_code}'" >& 2 - >&2 cat $output_file - rm -f $output_file + >&2 cat "${output_file}" + rm -f "${output_file}" exit 22 fi set -e - cat $output_file - rm -f $output_file + cat "${output_file}" + rm -f "${output_file}" } diff --git a/resources/build/ci/trigger-definitions.inc.sh b/resources/build/ci/trigger-definitions.inc.sh index 74380e7311..0666e05487 100644 --- a/resources/build/ci/trigger-definitions.inc.sh +++ b/resources/build/ci/trigger-definitions.inc.sh @@ -112,7 +112,7 @@ vcs_test=HttpsGithubComKeymanappKeymanPRs bc_master_android=(KeymanAndroid_Build) bc_master_ios=(Keyman_iOS_Master) -bc_master_linux=(KeymanLinux_Master deb-release-packaging_GitHub) +bc_master_linux=(KeymanLinux_Master deb-release-packaging_GitHub build-test-publish-docker_GitHub) bc_master_mac=(KeymanMac_Master) bc_master_windows=(Keyman_Build) bc_master_web=(Keymanweb_Build) diff --git a/resources/build/minimum-versions.inc.sh b/resources/build/minimum-versions.inc.sh index 5bcbf3cc12..8320c80954 100644 --- a/resources/build/minimum-versions.inc.sh +++ b/resources/build/minimum-versions.inc.sh @@ -3,7 +3,7 @@ # This is a list of minimum, maximum, and specific versions of any external # components or dependencies found in Keyman. # -# Original (outdated) reference +# Original (outdated) reference # https://docs.google.com/document/d/1Uy3U2YXeA4rCEbUbT7O6QzUGJDZBeViFecL8fjUOLkE/edit?usp=sharing # NOTE: After changing any values, you should run publish-minimum-versions.sh. @@ -50,4 +50,4 @@ KEYMAN_VERSION_CLDR=46 # LDML Keyboards version KEYMAN_VERSION_ISO639_3=2024-05-22 # Date of last import KEYMAN_VERSION_LANGTAGS=2025-02-18 # _version value KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY=2025-03-10 # Date from first line of language-subtag-registry -KEYMAN_VERSION_UNICODE=16.0.0 # UCD + related data +KEYMAN_VERSION_UNICODE=17.0.0 # UCD + related data diff --git a/resources/build/pr-build-status/build.sh b/resources/build/pr-build-status/build.sh index ca87de813d..42683bc3bc 100755 --- a/resources/build/pr-build-status/build.sh +++ b/resources/build/pr-build-status/build.sh @@ -19,7 +19,7 @@ function do_build() { local YML_PREFIX=$(cat "$YML_PREFIX_FILE") local SCRIPT_CLONE=$(sed -n '/CLONE:START/,/CLONE:END/{//!p}' < "$MJS_FILE") local SCRIPT_SUFFIX=$(sed -n '/CLONE-COMMENTED:START/,/CLONE-COMMENTED:END/{//!p}' < "$MJS_FILE" | sed 's/\/\/ //g') - echo -e "# GENERATED FILE - DO NOT EDIT!\n$YML_PREFIX\n$SCRIPT_CLONE\n$SCRIPT_SUFFIX\n" > "$YML_FILE" + echo $'# GENERATED FILE - DO NOT EDIT!\n'"$YML_PREFIX"$'\n'"$SCRIPT_CLONE"$'\n'"$SCRIPT_SUFFIX"$'\n' > "$YML_FILE" } builder_run_action build do_build diff --git a/resources/build/pr-build-status/fixtures/390a5bc6dba73cd32bc8c7290ac73321f6504deb-check-runs.json b/resources/build/pr-build-status/fixtures/390a5bc6dba73cd32bc8c7290ac73321f6504deb-check-runs.json new file mode 100644 index 0000000000..2544408133 --- /dev/null +++ b/resources/build/pr-build-status/fixtures/390a5bc6dba73cd32bc8c7290ac73321f6504deb-check-runs.json @@ -0,0 +1,660 @@ +[ + { + "id": 50109411782, + "name": "Build Outcome", + "node_id": "CR_kwDOAY2xT88AAAALqsDxxg", + "head_sha": "390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "external_id": "", + "url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50109411782", + "html_url": "https://github.com/keymanapp/keyman/runs/50109411782", + "details_url": "https://github.com/keymanapp/keyman/runs/50109411782", + "status": "completed", + "conclusion": "success", + "started_at": "2025-09-11T05:29:44Z", + "completed_at": "2025-09-11T05:29:45Z", + "output": { + "title": "21 checks completed successfully ✅", + "summary": "", + "text": null, + "annotations_count": 0, + "annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50109411782/annotations" + }, + "check_suite": { + "id": 45240794914 + }, + "app": { + "id": 15368, + "client_id": "Iv1.05c79e9ad1f6bdfa", + "slug": "github-actions", + "node_id": "MDM6QXBwMTUzNjg=", + "owner": { + "login": "github", + "id": 9919, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github", + "html_url": "https://github.com/github", + "followers_url": "https://api.github.com/users/github/followers", + "following_url": "https://api.github.com/users/github/following{/other_user}", + "gists_url": "https://api.github.com/users/github/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github/subscriptions", + "organizations_url": "https://api.github.com/users/github/orgs", + "repos_url": "https://api.github.com/users/github/repos", + "events_url": "https://api.github.com/users/github/events{/privacy}", + "received_events_url": "https://api.github.com/users/github/received_events", + "type": "Organization", + "user_view_type": "public", + "site_admin": false + }, + "name": "GitHub Actions", + "description": "Automate your workflow from idea to production", + "external_url": "https://help.github.com/en/actions", + "html_url": "https://github.com/apps/github-actions", + "created_at": "2018-07-30T09:30:17Z", + "updated_at": "2025-03-07T16:35:00Z", + "permissions": { + "actions": "write", + "administration": "read", + "attestations": "write", + "checks": "write", + "contents": "write", + "deployments": "write", + "discussions": "write", + "issues": "write", + "merge_queues": "write", + "metadata": "read", + "models": "read", + "packages": "write", + "pages": "write", + "pull_requests": "write", + "repository_hooks": "write", + "repository_projects": "write", + "security_events": "write", + "statuses": "write", + "vulnerability_alerts": "read" + }, + "events": [ + "branch_protection_rule", + "check_run", + "check_suite", + "create", + "delete", + "deployment", + "deployment_status", + "discussion", + "discussion_comment", + "fork", + "gollum", + "issues", + "issue_comment", + "label", + "merge_group", + "milestone", + "page_build", + "project", + "project_card", + "project_column", + "public", + "pull_request", + "pull_request_review", + "pull_request_review_comment", + "push", + "registry_package", + "release", + "repository", + "repository_dispatch", + "status", + "watch", + "workflow_dispatch", + "workflow_run" + ] + }, + "pull_requests": [ + { + "url": "https://api.github.com/repos/keymanapp/keyman/pulls/14728", + "id": 2817549354, + "number": 14728, + "head": { + "ref": "chore/common/unicode-17", + "sha": "ebeacc83d0b5cfd7ab523874631e5740721f1f2f", + "repo": { + "id": 26063183, + "url": "https://api.github.com/repos/keymanapp/keyman", + "name": "keyman" + } + }, + "base": { + "ref": "master", + "sha": "898cd340a33f29b766b08da66a519be7e3d79fe6", + "repo": { + "id": 26063183, + "url": "https://api.github.com/repos/keymanapp/keyman", + "name": "keyman" + } + } + } + ] + }, + { + "id": 50107239504, + "name": "GitGuardian Security Checks", + "node_id": "CR_kwDOAY2xT88AAAALqp_MUA", + "head_sha": "390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "external_id": "", + "url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107239504", + "html_url": "https://github.com/keymanapp/keyman/runs/50107239504", + "details_url": "https://dashboard.gitguardian.com", + "status": "completed", + "conclusion": "success", + "started_at": "2025-09-11T04:38:47Z", + "completed_at": "2025-09-11T04:38:48Z", + "output": { + "title": "No secrets detected ✅", + "summary": "**2** commits were scanned without uncovering any secrets.\n", + "text": "Commits scanned: **2**\n\n\n\n- Pull request #14728: `chore/common/unicode-17` 👉 `master`\n \n\n🦉 [GitGuardian](https://dashboard.gitguardian.com/auth/login/?utm_medium=checkruns&utm_source=github&utm_campaign=cr1) detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
\n", + "annotations_count": 0, + "annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107239504/annotations" + }, + "check_suite": { + "id": 45240794052 + }, + "app": { + "id": 46505, + "client_id": "Iv1.ec3c001966b4cc5a", + "slug": "gitguardian", + "node_id": "MDM6QXBwNDY1MDU=", + "owner": { + "login": "GitGuardian", + "id": 27360172, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI3MzYwMTcy", + "avatar_url": "https://avatars.githubusercontent.com/u/27360172?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GitGuardian", + "html_url": "https://github.com/GitGuardian", + "followers_url": "https://api.github.com/users/GitGuardian/followers", + "following_url": "https://api.github.com/users/GitGuardian/following{/other_user}", + "gists_url": "https://api.github.com/users/GitGuardian/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GitGuardian/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GitGuardian/subscriptions", + "organizations_url": "https://api.github.com/users/GitGuardian/orgs", + "repos_url": "https://api.github.com/users/GitGuardian/repos", + "events_url": "https://api.github.com/users/GitGuardian/events{/privacy}", + "received_events_url": "https://api.github.com/users/GitGuardian/received_events", + "type": "Organization", + "user_view_type": "public", + "site_admin": false + }, + "name": "GitGuardian", + "description": "# 🦉 What is GitGuardian?\r\n\r\nGitGuardian Secrets Detection detects and fixes vulnerabilities in source code at every step of the software development lifecycle, covering 350+ types of secrets like API keys, database connection strings, private keys, certificates, and more. The platform’s policy engine enables security teams to monitor and enforce rules across their VCS, DevOps tools, and infrastructure-as-code configurations. Our automated remediation playbooks and collaboration features bring security and development teams together to resolve incidents fast and in full.\r\n\r\n## 1. Scan your codebase for 350+ types of secrets\r\nGitGuardian scans your GitHub repositories and raises alerts only for critical secrets, such as API keys or other credentials. At scale, GitGuardian’s detection algorithm has been battle-tested on over three years of activity in all public GitHub repositories – totaling over 1 billion scanned commits!\r\n\r\n## 2. Quickly remediate your hard-coded secrets\r\nIf you ever experience a leak involving a credential, we have a complete remediation guide used by 100k+ developers each year. We’ll show you how to revoke the secret and remove it from your git history.\r\n\r\n## 3. Prevent secrets from reaching GitHub\r\nInstall ggshield, the GitGuardian CLI, and add secrets detection to your local development workflow using pre-commit and pre-push git hooks integrations.\r\n\r\n# 🙋‍♂️ FAQ\r\n\r\n**What is your pricing?**\r\nGitGuardian is free for teams under 25 developers and offers a 30-day trial for larger teams.\r\n\r\n**How can I be sure that GitGuardian won’t raise too many false positives?**\r\nWe have scanned billions of commits, sent millions of alerts since 2018, and integrated each feedback to improve our algorithm. Our alerts currently receive 91% “true positive” feedback from developers.\r\n\r\n**My repositories are private; why should I install automated secret detection?**\r\nImagine if there were a plain text file with all your credit card numbers inside, you wouldn’t put this file inside your company’s git repository. Secrets are just as sensitive and should be handled with special care.\r\n\r\n**Is GitGuardian available to be installed on-premise?**\r\nYes, you can contact one of our security specialists to look over the possibility of installing GitGuardian on-premise on your repositories.\r\n\r\n# ⚒️ Installation notes \r\n\r\nYou should install GitGuardian directly through your [GitGuardian](https://dashboard.gitguardian.com/) workspace on the Integration settings page. \r\n\r\nSo that you know, your GitHub organization or GitHub account can only be associated with a single GitGuardian workspace.\r\n\r\n# 👋 Support\r\n\r\nIf you experience any difficulties or have any questions, please reach out to us by email ([support@gitguardian.com](mailto:support@gitguardian.com)).", + "external_url": "https://dashboard.gitguardian.com", + "html_url": "https://github.com/apps/gitguardian", + "created_at": "2019-11-12T15:44:31Z", + "updated_at": "2023-07-18T09:06:22Z", + "permissions": { + "checks": "write", + "contents": "read", + "emails": "read", + "issues": "write", + "members": "read", + "metadata": "read", + "organization_hooks": "write", + "pull_requests": "write" + }, + "events": [ + "check_run", + "check_suite", + "commit_comment", + "create", + "delete", + "organization", + "public", + "pull_request", + "pull_request_review", + "pull_request_review_comment", + "push", + "repository" + ] + }, + "pull_requests": [ + { + "url": "https://api.github.com/repos/keymanapp/keyman/pulls/14728", + "id": 2817549354, + "number": 14728, + "head": { + "ref": "chore/common/unicode-17", + "sha": "ebeacc83d0b5cfd7ab523874631e5740721f1f2f", + "repo": { + "id": 26063183, + "url": "https://api.github.com/repos/keymanapp/keyman", + "name": "keyman" + } + }, + "base": { + "ref": "master", + "sha": "898cd340a33f29b766b08da66a519be7e3d79fe6", + "repo": { + "id": 26063183, + "url": "https://api.github.com/repos/keymanapp/keyman", + "name": "keyman" + } + } + } + ] + }, + { + "id": 50107238217, + "name": "build", + "node_id": "CR_kwDOAY2xT88AAAALqp_HSQ", + "head_sha": "390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "external_id": "584213e9-9caa-5e7a-be2b-4a4fb7afc574", + "url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107238217", + "html_url": "https://github.com/keymanapp/keyman/actions/runs/17634180116/job/50107238217", + "details_url": "https://github.com/keymanapp/keyman/actions/runs/17634180116/job/50107238217", + "status": "completed", + "conclusion": "skipped", + "started_at": "2025-09-11T04:38:46Z", + "completed_at": "2025-09-11T04:38:45Z", + "output": { + "title": null, + "summary": null, + "text": null, + "annotations_count": 0, + "annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107238217/annotations" + }, + "check_suite": { + "id": 45240899113 + }, + "app": { + "id": 15368, + "client_id": "Iv1.05c79e9ad1f6bdfa", + "slug": "github-actions", + "node_id": "MDM6QXBwMTUzNjg=", + "owner": { + "login": "github", + "id": 9919, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github", + "html_url": "https://github.com/github", + "followers_url": "https://api.github.com/users/github/followers", + "following_url": "https://api.github.com/users/github/following{/other_user}", + "gists_url": "https://api.github.com/users/github/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github/subscriptions", + "organizations_url": "https://api.github.com/users/github/orgs", + "repos_url": "https://api.github.com/users/github/repos", + "events_url": "https://api.github.com/users/github/events{/privacy}", + "received_events_url": "https://api.github.com/users/github/received_events", + "type": "Organization", + "user_view_type": "public", + "site_admin": false + }, + "name": "GitHub Actions", + "description": "Automate your workflow from idea to production", + "external_url": "https://help.github.com/en/actions", + "html_url": "https://github.com/apps/github-actions", + "created_at": "2018-07-30T09:30:17Z", + "updated_at": "2025-03-07T16:35:00Z", + "permissions": { + "actions": "write", + "administration": "read", + "attestations": "write", + "checks": "write", + "contents": "write", + "deployments": "write", + "discussions": "write", + "issues": "write", + "merge_queues": "write", + "metadata": "read", + "models": "read", + "packages": "write", + "pages": "write", + "pull_requests": "write", + "repository_hooks": "write", + "repository_projects": "write", + "security_events": "write", + "statuses": "write", + "vulnerability_alerts": "read" + }, + "events": [ + "branch_protection_rule", + "check_run", + "check_suite", + "create", + "delete", + "deployment", + "deployment_status", + "discussion", + "discussion_comment", + "fork", + "gollum", + "issues", + "issue_comment", + "label", + "merge_group", + "milestone", + "page_build", + "project", + "project_card", + "project_column", + "public", + "pull_request", + "pull_request_review", + "pull_request_review_comment", + "push", + "registry_package", + "release", + "repository", + "repository_dispatch", + "status", + "watch", + "workflow_dispatch", + "workflow_run" + ] + }, + "pull_requests": [ + { + "url": "https://api.github.com/repos/keymanapp/keyman/pulls/14728", + "id": 2817549354, + "number": 14728, + "head": { + "ref": "chore/common/unicode-17", + "sha": "ebeacc83d0b5cfd7ab523874631e5740721f1f2f", + "repo": { + "id": 26063183, + "url": "https://api.github.com/repos/keymanapp/keyman", + "name": "keyman" + } + }, + "base": { + "ref": "master", + "sha": "898cd340a33f29b766b08da66a519be7e3d79fe6", + "repo": { + "id": 26063183, + "url": "https://api.github.com/repos/keymanapp/keyman", + "name": "keyman" + } + } + } + ] + }, + { + "id": 50107238052, + "name": "triage", + "node_id": "CR_kwDOAY2xT88AAAALqp_GpA", + "head_sha": "390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "external_id": "90e392c4-1805-5238-9d01-622e4d746355", + "url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107238052", + "html_url": "https://github.com/keymanapp/keyman/actions/runs/17634180101/job/50107238052", + "details_url": "https://github.com/keymanapp/keyman/actions/runs/17634180101/job/50107238052", + "status": "completed", + "conclusion": "success", + "started_at": "2025-09-11T04:38:47Z", + "completed_at": "2025-09-11T04:38:52Z", + "output": { + "title": null, + "summary": null, + "text": null, + "annotations_count": 0, + "annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107238052/annotations" + }, + "check_suite": { + "id": 45240899080 + }, + "app": { + "id": 15368, + "client_id": "Iv1.05c79e9ad1f6bdfa", + "slug": "github-actions", + "node_id": "MDM6QXBwMTUzNjg=", + "owner": { + "login": "github", + "id": 9919, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github", + "html_url": "https://github.com/github", + "followers_url": "https://api.github.com/users/github/followers", + "following_url": "https://api.github.com/users/github/following{/other_user}", + "gists_url": "https://api.github.com/users/github/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github/subscriptions", + "organizations_url": "https://api.github.com/users/github/orgs", + "repos_url": "https://api.github.com/users/github/repos", + "events_url": "https://api.github.com/users/github/events{/privacy}", + "received_events_url": "https://api.github.com/users/github/received_events", + "type": "Organization", + "user_view_type": "public", + "site_admin": false + }, + "name": "GitHub Actions", + "description": "Automate your workflow from idea to production", + "external_url": "https://help.github.com/en/actions", + "html_url": "https://github.com/apps/github-actions", + "created_at": "2018-07-30T09:30:17Z", + "updated_at": "2025-03-07T16:35:00Z", + "permissions": { + "actions": "write", + "administration": "read", + "attestations": "write", + "checks": "write", + "contents": "write", + "deployments": "write", + "discussions": "write", + "issues": "write", + "merge_queues": "write", + "metadata": "read", + "models": "read", + "packages": "write", + "pages": "write", + "pull_requests": "write", + "repository_hooks": "write", + "repository_projects": "write", + "security_events": "write", + "statuses": "write", + "vulnerability_alerts": "read" + }, + "events": [ + "branch_protection_rule", + "check_run", + "check_suite", + "create", + "delete", + "deployment", + "deployment_status", + "discussion", + "discussion_comment", + "fork", + "gollum", + "issues", + "issue_comment", + "label", + "merge_group", + "milestone", + "page_build", + "project", + "project_card", + "project_column", + "public", + "pull_request", + "pull_request_review", + "pull_request_review_comment", + "push", + "registry_package", + "release", + "repository", + "repository_dispatch", + "status", + "watch", + "workflow_dispatch", + "workflow_run" + ] + }, + "pull_requests": [ + { + "url": "https://api.github.com/repos/keymanapp/keyman/pulls/14728", + "id": 2817549354, + "number": 14728, + "head": { + "ref": "chore/common/unicode-17", + "sha": "ebeacc83d0b5cfd7ab523874631e5740721f1f2f", + "repo": { + "id": 26063183, + "url": "https://api.github.com/repos/keymanapp/keyman", + "name": "keyman" + } + }, + "base": { + "ref": "master", + "sha": "898cd340a33f29b766b08da66a519be7e3d79fe6", + "repo": { + "id": 26063183, + "url": "https://api.github.com/repos/keymanapp/keyman", + "name": "keyman" + } + } + } + ] + }, + { + "id": 50107108425, + "name": "Summarize build status checks", + "node_id": "CR_kwDOAY2xT88AAAALqp3MSQ", + "head_sha": "390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "external_id": "7e5f7f3d-df60-5a09-9991-ce263cfb45c6", + "url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107108425", + "html_url": "https://github.com/keymanapp/keyman/actions/runs/17634132698/job/50107108425", + "details_url": "https://github.com/keymanapp/keyman/actions/runs/17634132698/job/50107108425", + "status": "completed", + "conclusion": "success", + "started_at": "2025-09-11T04:35:52Z", + "completed_at": "2025-09-11T04:35:55Z", + "output": { + "title": null, + "summary": null, + "text": null, + "annotations_count": 0, + "annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107108425/annotations" + }, + "check_suite": { + "id": 45240794914 + }, + "app": { + "id": 15368, + "client_id": "Iv1.05c79e9ad1f6bdfa", + "slug": "github-actions", + "node_id": "MDM6QXBwMTUzNjg=", + "owner": { + "login": "github", + "id": 9919, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github", + "html_url": "https://github.com/github", + "followers_url": "https://api.github.com/users/github/followers", + "following_url": "https://api.github.com/users/github/following{/other_user}", + "gists_url": "https://api.github.com/users/github/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github/subscriptions", + "organizations_url": "https://api.github.com/users/github/orgs", + "repos_url": "https://api.github.com/users/github/repos", + "events_url": "https://api.github.com/users/github/events{/privacy}", + "received_events_url": "https://api.github.com/users/github/received_events", + "type": "Organization", + "user_view_type": "public", + "site_admin": false + }, + "name": "GitHub Actions", + "description": "Automate your workflow from idea to production", + "external_url": "https://help.github.com/en/actions", + "html_url": "https://github.com/apps/github-actions", + "created_at": "2018-07-30T09:30:17Z", + "updated_at": "2025-03-07T16:35:00Z", + "permissions": { + "actions": "write", + "administration": "read", + "attestations": "write", + "checks": "write", + "contents": "write", + "deployments": "write", + "discussions": "write", + "issues": "write", + "merge_queues": "write", + "metadata": "read", + "models": "read", + "packages": "write", + "pages": "write", + "pull_requests": "write", + "repository_hooks": "write", + "repository_projects": "write", + "security_events": "write", + "statuses": "write", + "vulnerability_alerts": "read" + }, + "events": [ + "branch_protection_rule", + "check_run", + "check_suite", + "create", + "delete", + "deployment", + "deployment_status", + "discussion", + "discussion_comment", + "fork", + "gollum", + "issues", + "issue_comment", + "label", + "merge_group", + "milestone", + "page_build", + "project", + "project_card", + "project_column", + "public", + "pull_request", + "pull_request_review", + "pull_request_review_comment", + "push", + "registry_package", + "release", + "repository", + "repository_dispatch", + "status", + "watch", + "workflow_dispatch", + "workflow_run" + ] + }, + "pull_requests": [ + { + "url": "https://api.github.com/repos/keymanapp/keyman/pulls/14728", + "id": 2817549354, + "number": 14728, + "head": { + "ref": "chore/common/unicode-17", + "sha": "ebeacc83d0b5cfd7ab523874631e5740721f1f2f", + "repo": { + "id": 26063183, + "url": "https://api.github.com/repos/keymanapp/keyman", + "name": "keyman" + } + }, + "base": { + "ref": "master", + "sha": "898cd340a33f29b766b08da66a519be7e3d79fe6", + "repo": { + "id": 26063183, + "url": "https://api.github.com/repos/keymanapp/keyman", + "name": "keyman" + } + } + } + ] + } +] \ No newline at end of file diff --git a/resources/build/pr-build-status/fixtures/390a5bc6dba73cd32bc8c7290ac73321f6504deb-statuses.json b/resources/build/pr-build-status/fixtures/390a5bc6dba73cd32bc8c7290ac73321f6504deb-statuses.json new file mode 100644 index 0000000000..213931a7ed --- /dev/null +++ b/resources/build/pr-build-status/fixtures/390a5bc6dba73cd32bc8c7290ac73321f6504deb-statuses.json @@ -0,0 +1,3038 @@ +[ + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175572529, + "node_id": "SC_kwDOAY2xT88AAAAJHwvMMQ", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T05:29:36Z", + "updated_at": "2025-09-11T05:29:36Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175567982, + "node_id": "SC_kwDOAY2xT88AAAAJHwu6bg", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Developer_Test/576932", + "context": "Test - Windows (Keyman - Developer)", + "created_at": "2025-09-11T05:29:14Z", + "updated_at": "2025-09-11T05:29:14Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175162089, + "node_id": "SC_kwDOAY2xT88AAAAJHwWI6Q", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T05:00:11Z", + "updated_at": "2025-09-11T05:00:11Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175160705, + "node_id": "SC_kwDOAY2xT88AAAAJHwWDgQ", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Common_KPAPI_TestPullRequests_Windows/576928", + "context": "Test: Keyman Core - Windows (Common)", + "created_at": "2025-09-11T05:00:07Z", + "updated_at": "2025-09-11T05:00:07Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175159732, + "node_id": "SC_kwDOAY2xT88AAAAJHwV_tA", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Developer_Mac/576933", + "context": "Test - macOS (Keyman - Developer)", + "created_at": "2025-09-11T05:00:02Z", + "updated_at": "2025-09-11T05:00:02Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175118416, + "node_id": "SC_kwDOAY2xT88AAAAJHwTeUA", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:56:41Z", + "updated_at": "2025-09-11T04:56:41Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175115406, + "node_id": "SC_kwDOAY2xT88AAAAJHwTSjg", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Common_KPAPI_TestPullRequests_WASM/576918", + "context": "Test: Keyman Core - WASM (Common)", + "created_at": "2025-09-11T04:56:27Z", + "updated_at": "2025-09-11T04:56:27Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175107216, + "node_id": "SC_kwDOAY2xT88AAAAJHwSykA", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:55:44Z", + "updated_at": "2025-09-11T04:55:44Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "id": 39175106689, + "node_id": "SC_kwDOAY2xT88AAAAJHwSwgQ", + "state": "success", + "description": "API verification succeeded", + "target_url": "https://github.com/keymanapp/keyman/actions/runs/17634394358", + "context": "API Verification", + "created_at": "2025-09-11T04:55:42Z", + "updated_at": "2025-09-11T04:55:42Z", + "creator": { + "login": "github-actions[bot]", + "id": 41898282, + "node_id": "MDM6Qm90NDE4OTgyODI=", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github-actions%5Bbot%5D", + "html_url": "https://github.com/apps/github-actions", + "followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175098588, + "node_id": "SC_kwDOAY2xT88AAAAJHwSQ3A", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:55:00Z", + "updated_at": "2025-09-11T04:55:00Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175098146, + "node_id": "SC_kwDOAY2xT88AAAAJHwSPIg", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:54:58Z", + "updated_at": "2025-09-11T04:54:58Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175098035, + "node_id": "SC_kwDOAY2xT88AAAAJHwSOsw", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Linux_Test_Integration/576920", + "context": "Test:Integration (Keyman - Linux)", + "created_at": "2025-09-11T04:54:58Z", + "updated_at": "2025-09-11T04:54:58Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175097199, + "node_id": "SC_kwDOAY2xT88AAAAJHwSLbw", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Common_KPAPI_TestPullRequests_macOS/576925", + "context": "Test: Keyman Core - macOS (Common)", + "created_at": "2025-09-11T04:54:54Z", + "updated_at": "2025-09-11T04:54:54Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175096032, + "node_id": "SC_kwDOAY2xT88AAAAJHwSG4A", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:54:47Z", + "updated_at": "2025-09-11T04:54:47Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175095550, + "node_id": "SC_kwDOAY2xT88AAAAJHwSE_g", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Developer_Mac/576933", + "context": "Test - macOS (Keyman - Developer)", + "created_at": "2025-09-11T04:54:45Z", + "updated_at": "2025-09-11T04:54:45Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175095475, + "node_id": "SC_kwDOAY2xT88AAAAJHwSEsw", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_iOS_TestPullRequests/576915", + "context": "Test (Keyman - iOS)", + "created_at": "2025-09-11T04:54:44Z", + "updated_at": "2025-09-11T04:54:44Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175080948, + "node_id": "SC_kwDOAY2xT88AAAAJHwRL9A", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:53:36Z", + "updated_at": "2025-09-11T04:53:36Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175080759, + "node_id": "SC_kwDOAY2xT88AAAAJHwRLNw", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:53:35Z", + "updated_at": "2025-09-11T04:53:35Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175078800, + "node_id": "SC_kwDOAY2xT88AAAAJHwRDkA", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanAndroid_TestSamplesAndTestProjects/576931", + "context": "Test: Samples and Test projects (Keyman - Android)", + "created_at": "2025-09-11T04:53:25Z", + "updated_at": "2025-09-11T04:53:25Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "id": 39175078429, + "node_id": "SC_kwDOAY2xT88AAAAJHwRCHQ", + "state": "pending", + "description": "API verification started", + "target_url": "https://github.com/keymanapp/keyman/actions/runs/17634394358", + "context": "API Verification", + "created_at": "2025-09-11T04:53:23Z", + "updated_at": "2025-09-11T04:53:23Z", + "creator": { + "login": "github-actions[bot]", + "id": 41898282, + "node_id": "MDM6Qm90NDE4OTgyODI=", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github-actions%5Bbot%5D", + "html_url": "https://github.com/apps/github-actions", + "followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "id": 39175076234, + "node_id": "SC_kwDOAY2xT88AAAAJHwQ5ig", + "state": "success", + "description": "Package build succeeded", + "target_url": "https://github.com/keymanapp/keyman/actions/runs/17634187943", + "context": "Ubuntu Packaging", + "created_at": "2025-09-11T04:53:11Z", + "updated_at": "2025-09-11T04:53:11Z", + "creator": { + "login": "github-actions[bot]", + "id": 41898282, + "node_id": "MDM6Qm90NDE4OTgyODI=", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github-actions%5Bbot%5D", + "html_url": "https://github.com/apps/github-actions", + "followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175068238, + "node_id": "SC_kwDOAY2xT88AAAAJHwQaTg", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Linux_Test_Integration/576920", + "context": "Test:Integration (Keyman - Linux)", + "created_at": "2025-09-11T04:52:31Z", + "updated_at": "2025-09-11T04:52:31Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175063216, + "node_id": "SC_kwDOAY2xT88AAAAJHwQGsA", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:52:10Z", + "updated_at": "2025-09-11T04:52:10Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175058340, + "node_id": "SC_kwDOAY2xT88AAAAJHwPzpA", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Developer_Linux/576934", + "context": "Test - Linux (Keyman - Developer)", + "created_at": "2025-09-11T04:51:48Z", + "updated_at": "2025-09-11T04:51:48Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175051151, + "node_id": "SC_kwDOAY2xT88AAAAJHwPXjw", + "state": "failure", + "description": "TeamCity build failed", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Linux/576935", + "context": "Test: Common - Linux (Common)", + "created_at": "2025-09-11T04:51:21Z", + "updated_at": "2025-09-11T04:51:21Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175051020, + "node_id": "SC_kwDOAY2xT88AAAAJHwPXDA", + "state": "failure", + "description": "TeamCity build failed", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Linux/576935", + "context": "Test: Common - Linux (Common)", + "created_at": "2025-09-11T04:51:21Z", + "updated_at": "2025-09-11T04:51:21Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175049448, + "node_id": "SC_kwDOAY2xT88AAAAJHwPQ6A", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:51:13Z", + "updated_at": "2025-09-11T04:51:13Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175048733, + "node_id": "SC_kwDOAY2xT88AAAAJHwPOHQ", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:51:09Z", + "updated_at": "2025-09-11T04:51:09Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175044363, + "node_id": "SC_kwDOAY2xT88AAAAJHwO9Cw", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Linux/576935", + "context": "Test: Common - Linux (Common)", + "created_at": "2025-09-11T04:50:47Z", + "updated_at": "2025-09-11T04:50:47Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175044256, + "node_id": "SC_kwDOAY2xT88AAAAJHwO8oA", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanAndroid_TestPullRequests/576930", + "context": "Test (Keyman - Android)", + "created_at": "2025-09-11T04:50:46Z", + "updated_at": "2025-09-11T04:50:46Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175038088, + "node_id": "SC_kwDOAY2xT88AAAAJHwOkiA", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:50:14Z", + "updated_at": "2025-09-11T04:50:14Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175033010, + "node_id": "SC_kwDOAY2xT88AAAAJHwOQsg", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Common_KPAPI_TestPullRequests_macOS/576925", + "context": "Test: Keyman Core - macOS (Common)", + "created_at": "2025-09-11T04:49:50Z", + "updated_at": "2025-09-11T04:49:50Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175032935, + "node_id": "SC_kwDOAY2xT88AAAAJHwOQZw", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_KeymanMac_PullRequests/576924", + "context": "Test (Keyman - macOS)", + "created_at": "2025-09-11T04:49:49Z", + "updated_at": "2025-09-11T04:49:49Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175026018, + "node_id": "SC_kwDOAY2xT88AAAAJHwN1Yg", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:49:12Z", + "updated_at": "2025-09-11T04:49:12Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175005335, + "node_id": "SC_kwDOAY2xT88AAAAJHwMklw", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:47:30Z", + "updated_at": "2025-09-11T04:47:30Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39175005126, + "node_id": "SC_kwDOAY2xT88AAAAJHwMjxg", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:47:29Z", + "updated_at": "2025-09-11T04:47:29Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175001040, + "node_id": "SC_kwDOAY2xT88AAAAJHwMT0A", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanAndroid_TestSamplesAndTestProjects/576931", + "context": "Test: Samples and Test projects (Keyman - Android)", + "created_at": "2025-09-11T04:47:09Z", + "updated_at": "2025-09-11T04:47:09Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39175000940, + "node_id": "SC_kwDOAY2xT88AAAAJHwMTbA", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanDesktop_TestPullRequests/576926", + "context": "Test (Keyman - Windows)", + "created_at": "2025-09-11T04:47:09Z", + "updated_at": "2025-09-11T04:47:09Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174999511, + "node_id": "SC_kwDOAY2xT88AAAAJHwMN1w", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Developer_Test/576932", + "context": "Test - Windows (Keyman - Developer)", + "created_at": "2025-09-11T04:47:02Z", + "updated_at": "2025-09-11T04:47:02Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174999409, + "node_id": "SC_kwDOAY2xT88AAAAJHwMNcQ", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Web/576929", + "context": "Test: Common - Web (Common)", + "created_at": "2025-09-11T04:47:02Z", + "updated_at": "2025-09-11T04:47:02Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174993947, + "node_id": "SC_kwDOAY2xT88AAAAJHwL4Gw", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_KeymanMac_PullRequests/576924", + "context": "Test (Keyman - macOS)", + "created_at": "2025-09-11T04:46:36Z", + "updated_at": "2025-09-11T04:46:36Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174993870, + "node_id": "SC_kwDOAY2xT88AAAAJHwL3zg", + "state": "failure", + "description": "TeamCity build failed", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Mac/576922", + "context": "Test: Common - macOS (Common)", + "created_at": "2025-09-11T04:46:35Z", + "updated_at": "2025-09-11T04:46:35Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174993783, + "node_id": "SC_kwDOAY2xT88AAAAJHwL3dw", + "state": "failure", + "description": "TeamCity build failed", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Mac/576922", + "context": "Test: Common - macOS (Common)", + "created_at": "2025-09-11T04:46:35Z", + "updated_at": "2025-09-11T04:46:35Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39174990184, + "node_id": "SC_kwDOAY2xT88AAAAJHwLpaA", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:46:18Z", + "updated_at": "2025-09-11T04:46:18Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39174989998, + "node_id": "SC_kwDOAY2xT88AAAAJHwLorg", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:46:17Z", + "updated_at": "2025-09-11T04:46:17Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39174989781, + "node_id": "SC_kwDOAY2xT88AAAAJHwLn1Q", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:46:16Z", + "updated_at": "2025-09-11T04:46:16Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39174989098, + "node_id": "SC_kwDOAY2xT88AAAAJHwLlKg", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:46:12Z", + "updated_at": "2025-09-11T04:46:12Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174985891, + "node_id": "SC_kwDOAY2xT88AAAAJHwLYow", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Developer_Linux/576934", + "context": "Test - Linux (Keyman - Developer)", + "created_at": "2025-09-11T04:45:56Z", + "updated_at": "2025-09-11T04:45:56Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174985810, + "node_id": "SC_kwDOAY2xT88AAAAJHwLYUg", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keymanweb_TestPullRequests/576917", + "context": "Test (Keyman - Web)", + "created_at": "2025-09-11T04:45:56Z", + "updated_at": "2025-09-11T04:45:56Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174985723, + "node_id": "SC_kwDOAY2xT88AAAAJHwLX-w", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanAndroid_TestPullRequests/576930", + "context": "Test (Keyman - Android)", + "created_at": "2025-09-11T04:45:55Z", + "updated_at": "2025-09-11T04:45:55Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174985645, + "node_id": "SC_kwDOAY2xT88AAAAJHwLXrQ", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Common_KPAPI_TestPullRequests_Linux/576921", + "context": "Test: Keyman Core - Linux (Common)", + "created_at": "2025-09-11T04:45:55Z", + "updated_at": "2025-09-11T04:45:55Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174985556, + "node_id": "SC_kwDOAY2xT88AAAAJHwLXVA", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Mac/576922", + "context": "Test: Common - macOS (Common)", + "created_at": "2025-09-11T04:45:54Z", + "updated_at": "2025-09-11T04:45:54Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174985505, + "node_id": "SC_kwDOAY2xT88AAAAJHwLXIQ", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_iOS_TestSamplesAndTestProjects/576916", + "context": "Test: Samples and Test Projects (Keyman - iOS)", + "created_at": "2025-09-11T04:45:54Z", + "updated_at": "2025-09-11T04:45:54Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174984029, + "node_id": "SC_kwDOAY2xT88AAAAJHwLRXQ", + "state": "success", + "description": "⚠️ Warning: keymanweb.js is 170 bytes (0%) larger than 19.0.116, now 432,283 bytes", + "target_url": null, + "context": "check/web/file-size", + "created_at": "2025-09-11T04:45:46Z", + "updated_at": "2025-09-11T04:45:46Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174958855, + "node_id": "SC_kwDOAY2xT88AAAAJHwJvBw", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Web/576929", + "context": "Test: Common - Web (Common)", + "created_at": "2025-09-11T04:43:34Z", + "updated_at": "2025-09-11T04:43:34Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174958775, + "node_id": "SC_kwDOAY2xT88AAAAJHwJutw", + "state": "failure", + "description": "TeamCity build failed", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Windows/576923", + "context": "Test: Common - Windows (Common)", + "created_at": "2025-09-11T04:43:33Z", + "updated_at": "2025-09-11T04:43:33Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174958372, + "node_id": "SC_kwDOAY2xT88AAAAJHwJtJA", + "state": "failure", + "description": "TeamCity build failed", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Windows/576923", + "context": "Test: Common - Windows (Common)", + "created_at": "2025-09-11T04:43:31Z", + "updated_at": "2025-09-11T04:43:31Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174938339, + "node_id": "SC_kwDOAY2xT88AAAAJHwIe4w", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Common_KPAPI_TestPullRequests_Linux/576921", + "context": "Test: Keyman Core - Linux (Common)", + "created_at": "2025-09-11T04:41:54Z", + "updated_at": "2025-09-11T04:41:54Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174938237, + "node_id": "SC_kwDOAY2xT88AAAAJHwIefQ", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanLinux_TestPullRequests/576919", + "context": "Test (Keyman - Linux)", + "created_at": "2025-09-11T04:41:53Z", + "updated_at": "2025-09-11T04:41:53Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174932962, + "node_id": "SC_kwDOAY2xT88AAAAJHwIJ4g", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Common_KPAPI_TestPullRequests_Windows/576928", + "context": "Test: Keyman Core - Windows (Common)", + "created_at": "2025-09-11T04:41:25Z", + "updated_at": "2025-09-11T04:41:25Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174932882, + "node_id": "SC_kwDOAY2xT88AAAAJHwIJkg", + "state": "success", + "description": "TeamCity build finished", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanDesktop_TestPrRenderOnScreenKeyboards/576927", + "context": "Test: Render On Screen Keyboards (Keyman - Windows)", + "created_at": "2025-09-11T04:41:24Z", + "updated_at": "2025-09-11T04:41:24Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "id": 39174908967, + "node_id": "SC_kwDOAY2xT88AAAAJHwGsJw", + "state": "pending", + "description": "Ubuntu packaging started", + "target_url": "https://github.com/keymanapp/keyman/actions/runs/17634187943", + "context": "Ubuntu Packaging", + "created_at": "2025-09-11T04:39:26Z", + "updated_at": "2025-09-11T04:39:26Z", + "creator": { + "login": "github-actions[bot]", + "id": 41898282, + "node_id": "MDM6Qm90NDE4OTgyODI=", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github-actions%5Bbot%5D", + "html_url": "https://github.com/apps/github-actions", + "followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174908607, + "node_id": "SC_kwDOAY2xT88AAAAJHwGqvw", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanLinux_TestPullRequests/576919", + "context": "Test (Keyman - Linux)", + "created_at": "2025-09-11T04:39:24Z", + "updated_at": "2025-09-11T04:39:24Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174908507, + "node_id": "SC_kwDOAY2xT88AAAAJHwGqWw", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576935", + "context": "Test: Common - Linux (Common)", + "created_at": "2025-09-11T04:39:24Z", + "updated_at": "2025-09-11T04:39:24Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174908427, + "node_id": "SC_kwDOAY2xT88AAAAJHwGqCw", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576934", + "context": "Test - Linux (Keyman - Developer)", + "created_at": "2025-09-11T04:39:23Z", + "updated_at": "2025-09-11T04:39:23Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174908273, + "node_id": "SC_kwDOAY2xT88AAAAJHwGpcQ", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576933", + "context": "Test - macOS (Keyman - Developer)", + "created_at": "2025-09-11T04:39:22Z", + "updated_at": "2025-09-11T04:39:22Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174908169, + "node_id": "SC_kwDOAY2xT88AAAAJHwGpCQ", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576932", + "context": "Test - Windows (Keyman - Developer)", + "created_at": "2025-09-11T04:39:22Z", + "updated_at": "2025-09-11T04:39:22Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174908061, + "node_id": "SC_kwDOAY2xT88AAAAJHwGonQ", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576931", + "context": "Test: Samples and Test projects (Keyman - Android)", + "created_at": "2025-09-11T04:39:22Z", + "updated_at": "2025-09-11T04:39:22Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174907908, + "node_id": "SC_kwDOAY2xT88AAAAJHwGoBA", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576930", + "context": "Test (Keyman - Android)", + "created_at": "2025-09-11T04:39:21Z", + "updated_at": "2025-09-11T04:39:21Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174907798, + "node_id": "SC_kwDOAY2xT88AAAAJHwGnlg", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576929", + "context": "Test: Common - Web (Common)", + "created_at": "2025-09-11T04:39:20Z", + "updated_at": "2025-09-11T04:39:20Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174907673, + "node_id": "SC_kwDOAY2xT88AAAAJHwGnGQ", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576928", + "context": "Test: Keyman Core - Windows (Common)", + "created_at": "2025-09-11T04:39:20Z", + "updated_at": "2025-09-11T04:39:20Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174907586, + "node_id": "SC_kwDOAY2xT88AAAAJHwGmwg", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanDesktop_TestPrRenderOnScreenKeyboards/576927", + "context": "Test: Render On Screen Keyboards (Keyman - Windows)", + "created_at": "2025-09-11T04:39:19Z", + "updated_at": "2025-09-11T04:39:19Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174907492, + "node_id": "SC_kwDOAY2xT88AAAAJHwGmZA", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanDesktop_TestPrRenderOnScreenKeyboards/576927", + "context": "Test: Render On Screen Keyboards (Keyman - Windows)", + "created_at": "2025-09-11T04:39:19Z", + "updated_at": "2025-09-11T04:39:19Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174907382, + "node_id": "SC_kwDOAY2xT88AAAAJHwGl9g", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanDesktop_TestPullRequests/576926", + "context": "Test (Keyman - Windows)", + "created_at": "2025-09-11T04:39:19Z", + "updated_at": "2025-09-11T04:39:19Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174907255, + "node_id": "SC_kwDOAY2xT88AAAAJHwGldw", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/buildConfiguration/KeymanDesktop_TestPullRequests/576926", + "context": "Test (Keyman - Windows)", + "created_at": "2025-09-11T04:39:18Z", + "updated_at": "2025-09-11T04:39:18Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174907127, + "node_id": "SC_kwDOAY2xT88AAAAJHwGk9w", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576925", + "context": "Test: Keyman Core - macOS (Common)", + "created_at": "2025-09-11T04:39:17Z", + "updated_at": "2025-09-11T04:39:17Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174907022, + "node_id": "SC_kwDOAY2xT88AAAAJHwGkjg", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576924", + "context": "Test (Keyman - macOS)", + "created_at": "2025-09-11T04:39:17Z", + "updated_at": "2025-09-11T04:39:17Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174906933, + "node_id": "SC_kwDOAY2xT88AAAAJHwGkNQ", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Windows/576923", + "context": "Test: Common - Windows (Common)", + "created_at": "2025-09-11T04:39:16Z", + "updated_at": "2025-09-11T04:39:16Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174906844, + "node_id": "SC_kwDOAY2xT88AAAAJHwGj3A", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Test_Common_Windows/576923", + "context": "Test: Common - Windows (Common)", + "created_at": "2025-09-11T04:39:16Z", + "updated_at": "2025-09-11T04:39:16Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174906746, + "node_id": "SC_kwDOAY2xT88AAAAJHwGjeg", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576922", + "context": "Test: Common - macOS (Common)", + "created_at": "2025-09-11T04:39:16Z", + "updated_at": "2025-09-11T04:39:16Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174906635, + "node_id": "SC_kwDOAY2xT88AAAAJHwGjCw", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576921", + "context": "Test: Keyman Core - Linux (Common)", + "created_at": "2025-09-11T04:39:15Z", + "updated_at": "2025-09-11T04:39:15Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174906548, + "node_id": "SC_kwDOAY2xT88AAAAJHwGitA", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576920", + "context": "Test:Integration (Keyman - Linux)", + "created_at": "2025-09-11T04:39:14Z", + "updated_at": "2025-09-11T04:39:14Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174906463, + "node_id": "SC_kwDOAY2xT88AAAAJHwGiXw", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/build/576919", + "context": "Test (Keyman - Linux)", + "created_at": "2025-09-11T04:39:14Z", + "updated_at": "2025-09-11T04:39:14Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174906355, + "node_id": "SC_kwDOAY2xT88AAAAJHwGh8w", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Common_KPAPI_TestPullRequests_WASM/576918", + "context": "Test: Keyman Core - WASM (Common)", + "created_at": "2025-09-11T04:39:14Z", + "updated_at": "2025-09-11T04:39:14Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174906251, + "node_id": "SC_kwDOAY2xT88AAAAJHwGhiw", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_Common_KPAPI_TestPullRequests_WASM/576918", + "context": "Test: Keyman Core - WASM (Common)", + "created_at": "2025-09-11T04:39:13Z", + "updated_at": "2025-09-11T04:39:13Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174906139, + "node_id": "SC_kwDOAY2xT88AAAAJHwGhGw", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keymanweb_TestPullRequests/576917", + "context": "Test (Keyman - Web)", + "created_at": "2025-09-11T04:39:12Z", + "updated_at": "2025-09-11T04:39:12Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174906037, + "node_id": "SC_kwDOAY2xT88AAAAJHwGgtQ", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/buildConfiguration/Keymanweb_TestPullRequests/576917", + "context": "Test (Keyman - Web)", + "created_at": "2025-09-11T04:39:12Z", + "updated_at": "2025-09-11T04:39:12Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174905929, + "node_id": "SC_kwDOAY2xT88AAAAJHwGgSQ", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_iOS_TestPullRequests/576915", + "context": "Test (Keyman - iOS)", + "created_at": "2025-09-11T04:39:12Z", + "updated_at": "2025-09-11T04:39:12Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174905842, + "node_id": "SC_kwDOAY2xT88AAAAJHwGf8g", + "state": "pending", + "description": "TeamCity build started", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_iOS_TestSamplesAndTestProjects/576916", + "context": "Test: Samples and Test Projects (Keyman - iOS)", + "created_at": "2025-09-11T04:39:11Z", + "updated_at": "2025-09-11T04:39:11Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174905756, + "node_id": "SC_kwDOAY2xT88AAAAJHwGfnA", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_iOS_TestSamplesAndTestProjects/576916", + "context": "Test: Samples and Test Projects (Keyman - iOS)", + "created_at": "2025-09-11T04:39:11Z", + "updated_at": "2025-09-11T04:39:11Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "id": 39174905667, + "node_id": "SC_kwDOAY2xT88AAAAJHwGfQw", + "state": "pending", + "description": "TeamCity build was queued", + "target_url": "https://build.palaso.org/buildConfiguration/Keyman_iOS_TestPullRequests/576915", + "context": "Test (Keyman - iOS)", + "created_at": "2025-09-11T04:39:10Z", + "updated_at": "2025-09-11T04:39:10Z", + "creator": { + "login": "keyman-server", + "id": 7018967, + "node_id": "MDQ6VXNlcjcwMTg5Njc=", + "avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keyman-server", + "html_url": "https://github.com/keyman-server", + "followers_url": "https://api.github.com/users/keyman-server/followers", + "following_url": "https://api.github.com/users/keyman-server/following{/other_user}", + "gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions", + "organizations_url": "https://api.github.com/users/keyman-server/orgs", + "repos_url": "https://api.github.com/users/keyman-server/repos", + "events_url": "https://api.github.com/users/keyman-server/events{/privacy}", + "received_events_url": "https://api.github.com/users/keyman-server/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "url": "https://api.github.com/repos/keymanapp/keyman/statuses/390a5bc6dba73cd32bc8c7290ac73321f6504deb", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "id": 39174900789, + "node_id": "SC_kwDOAY2xT88AAAAJHwGMNQ", + "state": "success", + "description": "User tests are not required", + "target_url": "https://github.com/keymanapp/keyman/pull/14728#issuecomment-3277442589", + "context": "user_testing", + "created_at": "2025-09-11T04:38:46Z", + "updated_at": "2025-09-11T04:38:46Z", + "creator": { + "login": "keymanapp-test-bot[bot]", + "id": 89363325, + "node_id": "MDM6Qm90ODkzNjMzMjU=", + "avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D", + "html_url": "https://github.com/apps/keymanapp-test-bot", + "followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + } + } +] \ No newline at end of file diff --git a/resources/build/pr-build-status/pr-build-status.mjs b/resources/build/pr-build-status/pr-build-status.mjs index 091377d942..f8f1b62c1c 100644 --- a/resources/build/pr-build-status/pr-build-status.mjs +++ b/resources/build/pr-build-status/pr-build-status.mjs @@ -27,43 +27,61 @@ // check/web/file-size // + function addLog(message) { + console.log(message); + return message + String.fromCharCode(13); + } + + function addStatus(o, type, context, state) { + if(!o[context]) { + o[context] = {type, state}; + return addLog(`Found ${context}: ${state}`); + } else { + return addLog(`Skipping repeated ${context}: ${state}`); + } + } + function reduceStatuses(statuses) { + let summary = ''; const filtered_statuses = statuses.reduce((o, status) => { if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) { - if(!o[status.context]) o[status.context] = {type: 'build', state: status.state}; + summary += addStatus(o, 'build', status.context, status.state); } else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') { - if(!o[status.context]) o[status.context] = {type: 'user-test', state: status.state};; + summary += addStatus(o, 'user-test', status.context, status.state); } else if(status.context == 'API Verification') { - if(!o[status.context]) o[status.context] = {type: 'check', state: status.state}; + summary += addStatus(o, 'check', status.context, status.state); } else if(status.context == 'Ubuntu Packaging') { - if(!o[status.context]) o[status.context] = {type: 'build', state: status.state}; + summary += addStatus(o, 'build', status.context, status.state); } else if(status.context == 'check/web/file-size') { // Ignore check/web/file-size -- we won't block automerge for this at this point + summary += addLog(`Skipping ${status.context}`); } else { // We fail with an 'unknown status' response if we get a new status check // so we can be sure we are not skipping known status checks + summary += addLog(`Found unknown ${status.context}: ${status.state}`); o[status.context] = {type: 'unknown', state: status.state}; } return o; }, {}); - return filtered_statuses; + return { filtered_statuses, summary }; } // // Given the collection of status checks we care about, return - // an aggregate status -- error, failed, pending, or success, + // an aggregate status -- error, failure, pending, or success, // and a summary description // - function calculateFinalStatus(filtered_statuses) { + function calculateFinalStatus(filtered_statuses, summary) { const counts = {}; let hasBuilds = false; for(const context of Object.keys(filtered_statuses)) { const { state, type } = filtered_statuses[context]; if(type == 'unknown') { // We special-case for unknown status checks, and never permit them + console.error(`An unknown context ${context} was found, cannot calculate build status.`); return [ - 'error', `An unknown context ${context} was found, cannot calculate build status.` + 'error', `An unknown context ${context} was found, cannot calculate build status.`, summary ]; } if(type == 'build') { @@ -74,12 +92,13 @@ // If we do not have any statuses yet, we wait if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) { - return ['pending', 'Builds have not yet been triggered ⌛']; + console.log(`Builds not yet triggered`); + return ['pending', 'Builds have not yet been triggered ⌛', summary]; } const state = counts.error ? 'error' : - counts.failed ? 'failed' : + counts.failure ? 'failure' : counts.pending ? 'pending' : 'success'; @@ -90,14 +109,16 @@ description += `${count} check${count == 1 ? '' : 's'} ${state}`; } appendDescription(counts.error, 'in an error state ❌'); - appendDescription(counts.failed, 'failed ❌'); + appendDescription(counts.failure, 'failed ❌'); appendDescription(counts.pending, 'pending ⌛'); appendDescription(counts.success, 'completed successfully ✅'); - return [ state, description ]; + console.log(`Finished filtering and reviewing status checks: ${state}: ${description}`); + return [ state, description, summary ]; } async function getCommitStatuses(github, owner, repo, sha) { + console.log(`Getting commit statuses for ${sha}`); const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', { owner, repo, @@ -110,6 +131,7 @@ } async function getCommitCheckRuns(github, owner, repo, sha) { + console.log(`Getting commit check runs for ${sha}`); const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', { owner, repo, @@ -123,13 +145,11 @@ function calculateCheckResult(statuses) { if(!Array.isArray(statuses)) { - return ['error', 'Failed to retrieve status checks from GitHub ❌']; + return ['error', 'Failed to retrieve status checks from GitHub ❌', 'statuses is not an array']; } - const filtered_statuses = reduceStatuses(statuses); - - const result = calculateFinalStatus(filtered_statuses); - return result; + const { filtered_statuses, summary } = reduceStatuses(statuses); + return calculateFinalStatus(filtered_statuses, summary); } async function test(github, owner, repo, sha) { @@ -149,7 +169,7 @@ return check.data.id; } - async function updateCheck(github, owner, repo, checkRunId, status, description) { + async function updateCheck(github, owner, repo, checkRunId, status, description, summary) { const checkStatus = status == 'pending' ? 'in_progress' : 'completed'; const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure'); @@ -161,7 +181,7 @@ conclusion, output: { title: description, - summary: '' + summary } }); } @@ -179,7 +199,7 @@ // const checkRunId = await createCheck(github, owner, repo, sha); // const res = await test(github, owner, repo, sha); - // await updateCheck(github, owner, repo, checkRunId, res[0], res[1]); + // await updateCheck(github, owner, repo, checkRunId, res[0], res[1], res[2]); // CLONE-COMMENTED:END diff --git a/resources/build/pr-build-status/pr-build-status.tests.mjs b/resources/build/pr-build-status/pr-build-status.tests.mjs index bfb49fe64b..fae7f65628 100644 --- a/resources/build/pr-build-status/pr-build-status.tests.mjs +++ b/resources/build/pr-build-status/pr-build-status.tests.mjs @@ -1,6 +1,7 @@ import * as fs from 'node:fs'; import 'mocha'; import { assert } from 'chai'; +import sinon from 'sinon'; import { unitTestEndpoints } from './pr-build-status.mjs'; @@ -9,9 +10,10 @@ import { getOctokit } from '@actions/github'; import * as process from 'node:process'; const commits = { + '390a5bc6dba73cd32bc8c7290ac73321f6504deb': [ 'failure', '3 checks failed ❌; 21 checks completed successfully ✅'], '825f7d2df0069fa56a8f2a534b42a49ee1f21ca1': [ 'success', '24 checks completed successfully ✅' ], - '870d37f56b489ef5067b4352a06505369d9038e0': [ 'error', '6 checks in an error state ❌; 17 checks completed successfully ✅' ], - '11a558e926370253db37026c95b7ff5a6aa1e8fc': [ 'success', '22 checks completed successfully ✅' ], + '870d37f56b489ef5067b4352a06505369d9038e0': [ 'error', '6 checks in an error state ❌; 1 check failed ❌; 17 checks completed successfully ✅' ], + '11a558e926370253db37026c95b7ff5a6aa1e8fc': [ 'failure', '2 checks failed ❌; 22 checks completed successfully ✅' ], '8fd5ccc9026b85d6a780d8bcf862380e7a89aefb': [ 'pending', 'Builds have not yet been triggered ⌛' ], '153683cfb007c6066c9dcf71d25afa4c66efa17f': [ 'pending', 'Builds have not yet been triggered ⌛' ], }; @@ -32,13 +34,23 @@ if(debug) { } describe('pr-build-status', function () { + beforeEach(() => { + // just silencing the logging from pr-build-status bot + // in order to make the test results clearer + sinon.stub(console, 'log'); + }); + + afterEach(() => { + console.log.restore(); + }); + for(const sha of Object.keys(commits)) { it(`should return '${commits[sha][1]}' for sha ${sha}`, function () { const json = JSON.parse(fs.readFileSync(`fixtures/${sha}-statuses.json`,'utf-8')); const status = unitTestEndpoints.calculateCheckResult(json); assert.isNotNull(status); assert.isArray(status); - assert.deepStrictEqual(status, commits[sha]); + assert.deepStrictEqual(status.splice(0, 2), commits[sha]); }); } }); diff --git a/resources/build/publish-minimum-versions.sh b/resources/build/publish-minimum-versions.sh index 5da012737a..a9d6098c72 100755 --- a/resources/build/publish-minimum-versions.sh +++ b/resources/build/publish-minimum-versions.sh @@ -35,7 +35,7 @@ generate_table() { KEYMAN_VERSION | KEYMAN_VERSION_MAJOR | KEYMAN_VERSION_MINOR | \ KEYMAN_VERSION_PATCH | KEYMAN_VERSION_RELEASE | KEYMAN_VERSION_WIN | \ KEYMAN_VERSION_TAG | KEYMAN_VERSION_WITH_TAG | \ - KEYMAN_VERSION_ENVIRONMENT | KEYMAN_VERSION_GIT_TAG) + KEYMAN_VERSION_ENVIRONMENT | KEYMAN_VERSION_GIT_TAG | KEYMAN_VERSION_FOR_FILENAME) # ignore the variables that contain the Keyman version ;; *) diff --git a/resources/build/test/build-bot/trigger-build-bot.test.sh b/resources/build/test/build-bot/trigger-build-bot.test.sh index a39b8361ae..f7ca8f91bd 100755 --- a/resources/build/test/build-bot/trigger-build-bot.test.sh +++ b/resources/build/test/build-bot/trigger-build-bot.test.sh @@ -216,7 +216,25 @@ _do_test_test_bot_check_pr_body() { #---------------------------------------------------------------------------------------------------- +test_is_skip_keyman_api_check() { + builder_echo start test_is_skip_keyman_api_check 'START TEST: test_is_skip_keyman_api_check' + result=$(is_skip_keyman_api_check 1 '{ "body": "Keyman-Api-Check: skip" }') + assert-equal "${result}" "true" "PR #1: 'Keyman-Api-Check: skip' should return true" + result=$(is_skip_keyman_api_check 2 '{ "body": "PR message" }') + assert-equal "${result}" "false" "PR #2: omitted 'Keyman-Api-Check' should return false" + result=$(is_skip_keyman_api_check 3 '{ "body": "Keyman-Api-Check: invalid" }') + assert-equal "${result}" "false" "PR #3: invalid data for 'Keyman-Api-Check' should return false" + result=$(is_skip_keyman_api_check 4 '{ "body": "Some message\nKeyman-Api-Check: skip" }') + assert-equal "${result}" "true" "PR #4: 'Keyman-Api-Check: skip' at start of line should return true" + result=$(is_skip_keyman_api_check 5 '{ "body": "Here Keyman-Api-Check: skip\nis in the middle of the line" }') + assert-equal "${result}" "false" "PR #5: 'Keyman-Api-Check: skip' in the middle of the line should return false" + builder_echo end test_is_skip_keyman_api_check success 'SUCCESS: test_is_skip_keyman_api_check' +} + +#---------------------------------------------------------------------------------------------------- + test_build_bot_verify_platforms test_build_bot_update_commands test_build_bot_check_messages test_test_bot_check_pr_body +test_is_skip_keyman_api_check diff --git a/resources/docker-images/README.md b/resources/docker-images/README.md index cc1b9f1b38..6c962b407c 100644 --- a/resources/docker-images/README.md +++ b/resources/docker-images/README.md @@ -107,3 +107,38 @@ Running tests locally with the `run.sh` looks like: resources/docker-images/run.sh :web --registry 'myregistry:5678' --username 'user' --password 'password' -- web/build.sh test ``` +## Running GitHub Actions locally + +There exist [GitHub actions for building the Docker images](../../.github/workflows/build-test-publish-docker.yml). +For testing and developing the workflow can be started locally. +As a prerequisite [act](https://nektosact.com/) must be installed. + +This is an exmaple for running the workflow with act: + +```bash +#!/bin/bash +# runs GitHub workflow locally for building, testing and publishing Docker images +# requires act (https://nektosact.com/) +# requires a GitHub token with repo permissions (e.g. GITHUB_TOKEN from a personal access token) +# requires Docker with buildx support + +tempfile=/tmp/event.json + +cat > ${tempfile} < -# - Use imperative, present tense ('attach' not 'attaches', 'attached' etc) -# - Don't include a period at the end of the title -# - Always include a blank line before trailers -# - More: https://github.com/keymanapp/keyman/wiki/Pull-Request-and-Commit-workflow-notes +\n${commentchar} Keyman Conventional Commit suggestions: +${commentchar} +${commentchar} - Link to a Sentry issue with git trailer: +${commentchar} Fixes: _MODULE_-_ID_ +${commentchar} - Give credit to co-authors: +${commentchar} Co-authored-by: _Name_ <_email_> +${commentchar} - Use imperative, present tense ('attach' not 'attaches', 'attached' etc) +${commentchar} - Don't include a period at the end of the title +${commentchar} - Always include a blank line before trailers +${commentchar} - More: https://github.com/keymanapp/keyman/wiki/Pull-Request-and-Commit-workflow-notes EOF ) @@ -170,4 +177,4 @@ case $COMMIT_SOURCE in # Known possibilities: message (-m), template (-t) exit 0 ;; -esac \ No newline at end of file +esac diff --git a/resources/standards-data/readme.md b/resources/standards-data/readme.md index 51d2079929..caeff56d19 100644 --- a/resources/standards-data/readme.md +++ b/resources/standards-data/readme.md @@ -57,13 +57,9 @@ See also [ldml-keyboards/readme.md](ldml-keyboards/readme.md). ### unicode-character-database -Data from : +Data from : -```bash -UNICODE_VERSION=15.1.0 -wget https://www.unicode.org/Public/${UNICODE_VERSION}/ucd/Blocks.txt -O unicode-character-database/Blocks.txt -wget https://www.unicode.org/Public/${UNICODE_VERSION}/ucd/UnicodeData.txt -O unicode-character-database/UnicodeData.txt -``` +See [download.sh](./unicode-character-database/download.sh) ### windows-lcid-to-bcp-47 diff --git a/resources/standards-data/unicode-character-database/Blocks.txt b/resources/standards-data/unicode-character-database/Blocks.txt index 1517dde0ce..5c24ab60cb 100644 --- a/resources/standards-data/unicode-character-database/Blocks.txt +++ b/resources/standards-data/unicode-character-database/Blocks.txt @@ -1,6 +1,6 @@ -# Blocks-16.0.0.txt -# Date: 2024-02-02 -# © 2024 Unicode®, Inc. +# Blocks-17.0.0.txt +# Date: 2025-08-01 +# © 2025 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. # For terms of use and license, see https://www.unicode.org/terms_of_use.html # @@ -228,6 +228,7 @@ FFF0..FFFF; Specials 108E0..108FF; Hatran 10900..1091F; Phoenician 10920..1093F; Lydian +10940..1095F; Sidetic 10980..1099F; Meroitic Hieroglyphs 109A0..109FF; Meroitic Cursive 10A00..10A5F; Kharoshthi @@ -279,11 +280,13 @@ FFF0..FFFF; Specials 11AB0..11ABF; Unified Canadian Aboriginal Syllabics Extended-A 11AC0..11AFF; Pau Cin Hau 11B00..11B5F; Devanagari Extended-A +11B60..11B7F; Sharada Supplement 11BC0..11BFF; Sunuwar 11C00..11C6F; Bhaiksuki 11C70..11CBF; Marchen 11D00..11D5F; Masaram Gondi 11D60..11DAF; Gunjala Gondi +11DB0..11DEF; Tolong Siki 11EE0..11EFF; Makasar 11F00..11F5F; Kawi 11FB0..11FBF; Lisu Supplement @@ -304,12 +307,14 @@ FFF0..FFFF; Specials 16B00..16B8F; Pahawh Hmong 16D40..16D7F; Kirat Rai 16E40..16E9F; Medefaidrin +16EA0..16EDF; Beria Erfe 16F00..16F9F; Miao 16FE0..16FFF; Ideographic Symbols and Punctuation 17000..187FF; Tangut 18800..18AFF; Tangut Components 18B00..18CFF; Khitan Small Script 18D00..18D7F; Tangut Supplement +18D80..18DFF; Tangut Components Supplement 1AFF0..1AFFF; Kana Extended-B 1B000..1B0FF; Kana Supplement 1B100..1B12F; Kana Extended-A @@ -318,6 +323,7 @@ FFF0..FFFF; Specials 1BC00..1BC9F; Duployan 1BCA0..1BCAF; Shorthand Format Controls 1CC00..1CEBF; Symbols for Legacy Computing Supplement +1CEC0..1CEFF; Miscellaneous Symbols Supplement 1CF00..1CFCF; Znamenny Musical Notation 1D000..1D0FF; Byzantine Musical Symbols 1D100..1D1FF; Musical Symbols @@ -336,6 +342,7 @@ FFF0..FFFF; Specials 1E2C0..1E2FF; Wancho 1E4D0..1E4FF; Nag Mundari 1E5D0..1E5FF; Ol Onal +1E6C0..1E6FF; Tai Yo 1E7E0..1E7FF; Ethiopic Extended-B 1E800..1E8DF; Mende Kikakui 1E900..1E95F; Adlam @@ -367,6 +374,7 @@ FFF0..FFFF; Specials 2F800..2FA1F; CJK Compatibility Ideographs Supplement 30000..3134F; CJK Unified Ideographs Extension G 31350..323AF; CJK Unified Ideographs Extension H +323B0..3347F; CJK Unified Ideographs Extension J E0000..E007F; Tags E0100..E01EF; Variation Selectors Supplement F0000..FFFFF; Supplementary Private Use Area-A diff --git a/resources/standards-data/unicode-character-database/UnicodeData.txt b/resources/standards-data/unicode-character-database/UnicodeData.txt index 64258a3739..fca68e3e15 100644 --- a/resources/standards-data/unicode-character-database/UnicodeData.txt +++ b/resources/standards-data/unicode-character-database/UnicodeData.txt @@ -659,7 +659,7 @@ 0292;LATIN SMALL LETTER EZH;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH;;01B7;;01B7 0293;LATIN SMALL LETTER EZH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH CURL;;;; 0294;LATIN LETTER GLOTTAL STOP;Lo;0;L;;;;;N;;;;; -0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;; +0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Lo;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;; 0296;LATIN LETTER INVERTED GLOTTAL STOP;Ll;0;L;;;;;N;;;;; 0297;LATIN LETTER STRETCHED C;Ll;0;L;;;;;N;;;;; 0298;LATIN LETTER BILABIAL CLICK;Ll;0;L;;;;;N;LATIN LETTER BULLSEYE;;;; @@ -2121,6 +2121,7 @@ 088C;ARABIC LETTER TAH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; 088D;ARABIC LETTER KEHEH WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;; 088E;ARABIC VERTICAL TAIL;Lo;0;AL;;;;;N;;;;; +088F;ARABIC LETTER NOON WITH RING ABOVE;Lo;0;AL;;;;;N;;;;; 0890;ARABIC POUND MARK ABOVE;Cf;0;AN;;;;;N;;;;; 0891;ARABIC PIASTRE MARK ABOVE;Cf;0;AN;;;;;N;;;;; 0897;ARABIC PEPET;Mn;230;NSM;;;;;N;;;;; @@ -2862,6 +2863,7 @@ 0C58;TELUGU LETTER TSA;Lo;0;L;;;;;N;;;;; 0C59;TELUGU LETTER DZA;Lo;0;L;;;;;N;;;;; 0C5A;TELUGU LETTER RRRA;Lo;0;L;;;;;N;;;;; +0C5C;TELUGU ARCHAIC SHRII;Lo;0;L;;;;;N;;;;; 0C5D;TELUGU LETTER NAKAARA POLLU;Lo;0;L;;;;;N;;;;; 0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; @@ -2958,6 +2960,7 @@ 0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;; 0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;; +0CDC;KANNADA ARCHAIC SHRII;Lo;0;L;;;;;N;;;;; 0CDD;KANNADA LETTER NAKAARA POLLU;Lo;0;L;;;;;N;;;;; 0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;; 0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; @@ -6137,6 +6140,33 @@ 1ACC;COMBINING LATIN SMALL LETTER INSULAR G;Mn;230;NSM;;;;;N;;;;; 1ACD;COMBINING LATIN SMALL LETTER INSULAR R;Mn;230;NSM;;;;;N;;;;; 1ACE;COMBINING LATIN SMALL LETTER INSULAR T;Mn;230;NSM;;;;;N;;;;; +1ACF;COMBINING DOUBLE CARON;Mn;230;NSM;;;;;N;;;;; +1AD0;COMBINING VERTICAL-LINE-ACUTE;Mn;230;NSM;;;;;N;;;;; +1AD1;COMBINING GRAVE-VERTICAL-LINE;Mn;230;NSM;;;;;N;;;;; +1AD2;COMBINING VERTICAL-LINE-GRAVE;Mn;230;NSM;;;;;N;;;;; +1AD3;COMBINING ACUTE-VERTICAL-LINE;Mn;230;NSM;;;;;N;;;;; +1AD4;COMBINING VERTICAL-LINE-MACRON;Mn;230;NSM;;;;;N;;;;; +1AD5;COMBINING MACRON-VERTICAL-LINE;Mn;230;NSM;;;;;N;;;;; +1AD6;COMBINING VERTICAL-LINE-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;; +1AD7;COMBINING VERTICAL-LINE-GRAVE-ACUTE;Mn;230;NSM;;;;;N;;;;; +1AD8;COMBINING MACRON-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;; +1AD9;COMBINING SHARP SIGN;Mn;230;NSM;;;;;N;;;;; +1ADA;COMBINING FLAT SIGN;Mn;230;NSM;;;;;N;;;;; +1ADB;COMBINING DOWN TACK ABOVE;Mn;230;NSM;;;;;N;;;;; +1ADC;COMBINING DIAERESIS WITH RAISED LEFT DOT;Mn;230;NSM;;;;;N;;;;; +1ADD;COMBINING DOT-AND-RING BELOW;Mn;220;NSM;;;;;N;;;;; +1AE0;COMBINING LEFT TACK ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE1;COMBINING RIGHT TACK ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE2;COMBINING MINUS SIGN ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE3;COMBINING INVERTED BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE4;COMBINING SQUARE ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE5;COMBINING SEAGULL ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE6;COMBINING DOUBLE ARCH BELOW;Mn;220;NSM;;;;;N;;;;; +1AE7;COMBINING DOUBLE ARCH ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE8;COMBINING EQUALS SIGN ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE9;COMBINING LEFT ANGLE CENTRED ABOVE;Mn;230;NSM;;;;;N;;;;; +1AEA;COMBINING UPWARDS ARROW ABOVE;Mn;230;NSM;;;;;N;;;;; +1AEB;COMBINING DOUBLE RIGHTWARDS ARROW ABOVE;Mn;234;NSM;;;;;N;;;;; 1B00;BALINESE SIGN ULU RICEM;Mn;0;NSM;;;;;N;;;;; 1B01;BALINESE SIGN ULU CANDRA;Mn;0;NSM;;;;;N;;;;; 1B02;BALINESE SIGN CECEK;Mn;0;NSM;;;;;N;;;;; @@ -7545,6 +7575,7 @@ 20BE;LARI SIGN;Sc;0;ET;;;;;N;;;;; 20BF;BITCOIN SIGN;Sc;0;ET;;;;;N;;;;; 20C0;SOM SIGN;Sc;0;ET;;;;;N;;;;; +20C1;SAUDI RIYAL SIGN;Sc;0;ET;;;;;N;;;;; 20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;; 20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;; 20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;; @@ -10239,6 +10270,7 @@ 2B93;NEWLINE RIGHT;So;0;ON;;;;;N;;;;; 2B94;FOUR CORNER ARROWS CIRCLING ANTICLOCKWISE;So;0;ON;;;;;N;;;;; 2B95;RIGHTWARDS BLACK ARROW;So;0;ON;;;;;N;;;;; +2B96;EQUALS SIGN WITH INFINITY ABOVE;So;0;ON;;;;;N;;;;; 2B97;SYMBOL FOR TYPE A ELECTRONICS;So;0;ON;;;;;N;;;;; 2B98;THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 2B99;THREE-D RIGHT-LIGHTED UPWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; @@ -14274,10 +14306,14 @@ A7CA;LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY;Ll;0;L;;;;;N;;;A7C9;;A7C9 A7CB;LATIN CAPITAL LETTER RAMS HORN;Lu;0;L;;;;;N;;;;0264; A7CC;LATIN CAPITAL LETTER S WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A7CD; A7CD;LATIN SMALL LETTER S WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A7CC;;A7CC +A7CE;LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE;Lu;0;L;;;;;N;;;;A7CF; +A7CF;LATIN SMALL LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;;;A7CE;;A7CE A7D0;LATIN CAPITAL LETTER CLOSED INSULAR G;Lu;0;L;;;;;N;;;;A7D1; A7D1;LATIN SMALL LETTER CLOSED INSULAR G;Ll;0;L;;;;;N;;;A7D0;;A7D0 -A7D3;LATIN SMALL LETTER DOUBLE THORN;Ll;0;L;;;;;N;;;;; -A7D5;LATIN SMALL LETTER DOUBLE WYNN;Ll;0;L;;;;;N;;;;; +A7D2;LATIN CAPITAL LETTER DOUBLE THORN;Lu;0;L;;;;;N;;;;A7D3; +A7D3;LATIN SMALL LETTER DOUBLE THORN;Ll;0;L;;;;;N;;;A7D2;;A7D2 +A7D4;LATIN CAPITAL LETTER DOUBLE WYNN;Lu;0;L;;;;;N;;;;A7D5; +A7D5;LATIN SMALL LETTER DOUBLE WYNN;Ll;0;L;;;;;N;;;A7D4;;A7D4 A7D6;LATIN CAPITAL LETTER MIDDLE SCOTS S;Lu;0;L;;;;;N;;;;A7D7; A7D7;LATIN SMALL LETTER MIDDLE SCOTS S;Ll;0;L;;;;;N;;;A7D6;;A7D6 A7D8;LATIN CAPITAL LETTER SIGMOID S;Lu;0;L;;;;;N;;;;A7D9; @@ -14285,6 +14321,7 @@ A7D9;LATIN SMALL LETTER SIGMOID S;Ll;0;L;;;;;N;;;A7D8;;A7D8 A7DA;LATIN CAPITAL LETTER LAMBDA;Lu;0;L;;;;;N;;;;A7DB; A7DB;LATIN SMALL LETTER LAMBDA;Ll;0;L;;;;;N;;;A7DA;;A7DA A7DC;LATIN CAPITAL LETTER LAMBDA WITH STROKE;Lu;0;L;;;;;N;;;;019B; +A7F1;MODIFIER LETTER CAPITAL S;Lm;0;L; 0053;;;;N;;;;; A7F2;MODIFIER LETTER CAPITAL C;Lm;0;L; 0043;;;;N;;;;; A7F3;MODIFIER LETTER CAPITAL F;Lm;0;L; 0046;;;;N;;;;; A7F4;MODIFIER LETTER CAPITAL Q;Lm;0;L; 0051;;;;N;;;;; @@ -15925,6 +15962,22 @@ FBBF;ARABIC SYMBOL RING;Sk;0;AL;;;;;N;;;;; FBC0;ARABIC SYMBOL SMALL TAH ABOVE;Sk;0;AL;;;;;N;;;;; FBC1;ARABIC SYMBOL SMALL TAH BELOW;Sk;0;AL;;;;;N;;;;; FBC2;ARABIC SYMBOL WASLA ABOVE;Sk;0;AL;;;;;N;;;;; +FBC3;ARABIC LIGATURE JALLA WA-ALAA;So;0;ON;;;;;N;;;;; +FBC4;ARABIC LIGATURE DAAMAT BARAKAATUHUM;So;0;ON;;;;;N;;;;; +FBC5;ARABIC LIGATURE RAHMATU ALLAAHI TAAALAA ALAYH;So;0;ON;;;;;N;;;;; +FBC6;ARABIC LIGATURE RAHMATU ALLAAHI ALAYHIM;So;0;ON;;;;;N;;;;; +FBC7;ARABIC LIGATURE RAHMATU ALLAAHI ALAYHIMAA;So;0;ON;;;;;N;;;;; +FBC8;ARABIC LIGATURE RAHIMAHUM ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;; +FBC9;ARABIC LIGATURE RAHIMAHUMAA ALLAAH;So;0;ON;;;;;N;;;;; +FBCA;ARABIC LIGATURE RAHIMAHUMAA ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;; +FBCB;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANHUM;So;0;ON;;;;;N;;;;; +FBCC;ARABIC LIGATURE HAFIZAHU ALLAAH;So;0;ON;;;;;N;;;;; +FBCD;ARABIC LIGATURE HAFIZAHU ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;; +FBCE;ARABIC LIGATURE HAFIZAHUM ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;; +FBCF;ARABIC LIGATURE HAFIZAHUMAA ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;; +FBD0;ARABIC LIGATURE SALLALLAAHU TAAALAA ALAYHI WA-SALLAM;So;0;ON;;;;;N;;;;; +FBD1;ARABIC LIGATURE AJJAL ALLAAHU FARAJAHU ASH-SHAREEF;So;0;ON;;;;;N;;;;; +FBD2;ARABIC LIGATURE ALAYHI AR-RAHMAH;So;0;ON;;;;;N;;;;; FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL; 06AD;;;;N;;;;; FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL; 06AD;;;;N;;;;; FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL; 06AD;;;;N;;;;; @@ -16370,6 +16423,8 @@ FD8C;ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL; 0645 FD8D;ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0645 062C 0645;;;;N;;;;; FD8E;ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM;Lo;0;AL; 0645 062E 062C;;;;N;;;;; FD8F;ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL; 0645 062E 0645;;;;N;;;;; +FD90;ARABIC LIGATURE RAHMATU ALLAAHI ALAYH;So;0;ON;;;;;N;;;;; +FD91;ARABIC LIGATURE RAHMATU ALLAAHI ALAYHAA;So;0;ON;;;;;N;;;;; FD92;ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM;Lo;0;AL; 0645 062C 062E;;;;N;;;;; FD93;ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL; 0647 0645 062C;;;;N;;;;; FD94;ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0647 0645 0645;;;;N;;;;; @@ -16424,6 +16479,13 @@ FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0639 FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0635 0645 0645;;;;N;;;;; FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL; 0633 062E 064A;;;;N;;;;; FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL; 0646 062C 064A;;;;N;;;;; +FDC8;ARABIC LIGATURE RAHIMAHU ALLAAH TAAALAA;So;0;ON;;;;;N;;;;; +FDC9;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANH;So;0;ON;;;;;N;;;;; +FDCA;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANHAA;So;0;ON;;;;;N;;;;; +FDCB;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANHUMAA;So;0;ON;;;;;N;;;;; +FDCC;ARABIC LIGATURE SALLALLAHU ALAYHI WA-ALAA AALIHEE WA-SALLAM;So;0;ON;;;;;N;;;;; +FDCD;ARABIC LIGATURE AJJAL ALLAAHU TAAALAA FARAJAHU ASH-SHAREEF;So;0;ON;;;;;N;;;;; +FDCE;ARABIC LIGATURE KARRAMA ALLAAHU WAJHAH;So;0;ON;;;;;N;;;;; FDCF;ARABIC LIGATURE SALAAMUHU ALAYNAA;So;0;ON;;;;;N;;;;; FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL; 0635 0644 06D2;;;;N;;;;; FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL; 0642 0644 06D2;;;;N;;;;; @@ -18708,6 +18770,32 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 10938;LYDIAN LETTER NN;Lo;0;R;;;;;N;;;;; 10939;LYDIAN LETTER C;Lo;0;R;;;;;N;;;;; 1093F;LYDIAN TRIANGULAR MARK;Po;0;R;;;;;N;;;;; +10940;SIDETIC LETTER N01;Lo;0;R;;;;;N;;;;; +10941;SIDETIC LETTER N02;Lo;0;R;;;;;N;;;;; +10942;SIDETIC LETTER N03;Lo;0;R;;;;;N;;;;; +10943;SIDETIC LETTER N04;Lo;0;R;;;;;N;;;;; +10944;SIDETIC LETTER N05;Lo;0;R;;;;;N;;;;; +10945;SIDETIC LETTER N06;Lo;0;R;;;;;N;;;;; +10946;SIDETIC LETTER N07;Lo;0;R;;;;;N;;;;; +10947;SIDETIC LETTER N08;Lo;0;R;;;;;N;;;;; +10948;SIDETIC LETTER N09;Lo;0;R;;;;;N;;;;; +10949;SIDETIC LETTER N10;Lo;0;R;;;;;N;;;;; +1094A;SIDETIC LETTER N11;Lo;0;R;;;;;N;;;;; +1094B;SIDETIC LETTER N12;Lo;0;R;;;;;N;;;;; +1094C;SIDETIC LETTER N13;Lo;0;R;;;;;N;;;;; +1094D;SIDETIC LETTER N14;Lo;0;R;;;;;N;;;;; +1094E;SIDETIC LETTER N15;Lo;0;R;;;;;N;;;;; +1094F;SIDETIC LETTER N16;Lo;0;R;;;;;N;;;;; +10950;SIDETIC LETTER N17;Lo;0;R;;;;;N;;;;; +10951;SIDETIC LETTER N18;Lo;0;R;;;;;N;;;;; +10952;SIDETIC LETTER N19;Lo;0;R;;;;;N;;;;; +10953;SIDETIC LETTER N20;Lo;0;R;;;;;N;;;;; +10954;SIDETIC LETTER N21;Lo;0;R;;;;;N;;;;; +10955;SIDETIC LETTER N22;Lo;0;R;;;;;N;;;;; +10956;SIDETIC LETTER N23;Lo;0;R;;;;;N;;;;; +10957;SIDETIC LETTER N24;Lo;0;R;;;;;N;;;;; +10958;SIDETIC LETTER N25;Lo;0;R;;;;;N;;;;; +10959;SIDETIC LETTER N26;Lo;0;R;;;;;N;;;;; 10980;MEROITIC HIEROGLYPHIC LETTER A;Lo;0;R;;;;;N;;;;; 10981;MEROITIC HIEROGLYPHIC LETTER E;Lo;0;R;;;;;N;;;;; 10982;MEROITIC HIEROGLYPHIC LETTER I;Lo;0;R;;;;;N;;;;; @@ -19541,6 +19629,20 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 10EC2;ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;; 10EC3;ARABIC LETTER TAH WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;; 10EC4;ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;; +10EC5;ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW;Lm;0;AL;;;;;N;;;;; +10EC6;ARABIC LETTER THIN NOON;Lo;0;AL;;;;;N;;;;; +10EC7;ARABIC LETTER YEH WITH FOUR DOTS BELOW;Lo;0;AL;;;;;N;;;;; +10ED0;ARABIC BIBLICAL END OF VERSE;Po;0;ON;;;;;N;;;;; +10ED1;ARABIC LIGATURE ALAYHAA AS-SALAATU WAS-SALAAM;So;0;ON;;;;;N;;;;; +10ED2;ARABIC LIGATURE ALAYHIM AS-SALAATU WAS-SALAAM;So;0;ON;;;;;N;;;;; +10ED3;ARABIC LIGATURE ALAYHIMAA AS-SALAATU WAS-SALAAM;So;0;ON;;;;;N;;;;; +10ED4;ARABIC LIGATURE QADDASA ALLAAHU SIRRAH;So;0;ON;;;;;N;;;;; +10ED5;ARABIC LIGATURE QUDDISA SIRRUHUM;So;0;ON;;;;;N;;;;; +10ED6;ARABIC LIGATURE QUDDISA SIRRUHUMAA;So;0;ON;;;;;N;;;;; +10ED7;ARABIC LIGATURE QUDDISAT ASRAARUHUM;So;0;ON;;;;;N;;;;; +10ED8;ARABIC LIGATURE NAWWARA ALLAAHU MARQADAH;So;0;ON;;;;;N;;;;; +10EFA;ARABIC DOUBLE VERTICAL BAR BELOW;Mn;220;NSM;;;;;N;;;;; +10EFB;ARABIC SMALL LOW NOON;Mn;220;NSM;;;;;N;;;;; 10EFC;ARABIC COMBINING ALEF OVERLAY;Mn;0;NSM;;;;;N;;;;; 10EFD;ARABIC SMALL LOW WORD SAKTA;Mn;220;NSM;;;;;N;;;;; 10EFE;ARABIC SMALL LOW WORD QASR;Mn;220;NSM;;;;;N;;;;; @@ -21521,6 +21623,14 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 11B07;DEVANAGARI SIGN WESTERN NINE-LIKE BHALE;Po;0;L;;;;;N;;;;; 11B08;DEVANAGARI SIGN REVERSED NINE-LIKE BHALE;Po;0;L;;;;;N;;;;; 11B09;DEVANAGARI SIGN MINDU;Po;0;L;;;;;N;;;;; +11B60;SHARADA VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;; +11B61;SHARADA VOWEL SIGN OOE;Mc;0;L;;;;;N;;;;; +11B62;SHARADA VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;; +11B63;SHARADA VOWEL SIGN UUE;Mn;0;NSM;;;;;N;;;;; +11B64;SHARADA VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;; +11B65;SHARADA VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;; +11B66;SHARADA VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;; +11B67;SHARADA VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;; 11BC0;SUNUWAR LETTER DEVI;Lo;0;L;;;;;N;;;;; 11BC1;SUNUWAR LETTER TASLA;Lo;0;L;;;;;N;;;;; 11BC2;SUNUWAR LETTER EKO;Lo;0;L;;;;;N;;;;; @@ -21868,6 +21978,60 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 11DA7;GUNJALA GONDI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 11DA8;GUNJALA GONDI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 11DA9;GUNJALA GONDI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11DB0;TOLONG SIKI LETTER I;Lo;0;L;;;;;N;;;;; +11DB1;TOLONG SIKI LETTER E;Lo;0;L;;;;;N;;;;; +11DB2;TOLONG SIKI LETTER U;Lo;0;L;;;;;N;;;;; +11DB3;TOLONG SIKI LETTER O;Lo;0;L;;;;;N;;;;; +11DB4;TOLONG SIKI LETTER A;Lo;0;L;;;;;N;;;;; +11DB5;TOLONG SIKI LETTER AA;Lo;0;L;;;;;N;;;;; +11DB6;TOLONG SIKI LETTER P;Lo;0;L;;;;;N;;;;; +11DB7;TOLONG SIKI LETTER PH;Lo;0;L;;;;;N;;;;; +11DB8;TOLONG SIKI LETTER B;Lo;0;L;;;;;N;;;;; +11DB9;TOLONG SIKI LETTER BH;Lo;0;L;;;;;N;;;;; +11DBA;TOLONG SIKI LETTER M;Lo;0;L;;;;;N;;;;; +11DBB;TOLONG SIKI LETTER T;Lo;0;L;;;;;N;;;;; +11DBC;TOLONG SIKI LETTER TH;Lo;0;L;;;;;N;;;;; +11DBD;TOLONG SIKI LETTER D;Lo;0;L;;;;;N;;;;; +11DBE;TOLONG SIKI LETTER DH;Lo;0;L;;;;;N;;;;; +11DBF;TOLONG SIKI LETTER N;Lo;0;L;;;;;N;;;;; +11DC0;TOLONG SIKI LETTER TT;Lo;0;L;;;;;N;;;;; +11DC1;TOLONG SIKI LETTER TTH;Lo;0;L;;;;;N;;;;; +11DC2;TOLONG SIKI LETTER DD;Lo;0;L;;;;;N;;;;; +11DC3;TOLONG SIKI LETTER DDH;Lo;0;L;;;;;N;;;;; +11DC4;TOLONG SIKI LETTER NN;Lo;0;L;;;;;N;;;;; +11DC5;TOLONG SIKI LETTER C;Lo;0;L;;;;;N;;;;; +11DC6;TOLONG SIKI LETTER CH;Lo;0;L;;;;;N;;;;; +11DC7;TOLONG SIKI LETTER J;Lo;0;L;;;;;N;;;;; +11DC8;TOLONG SIKI LETTER JH;Lo;0;L;;;;;N;;;;; +11DC9;TOLONG SIKI LETTER NY;Lo;0;L;;;;;N;;;;; +11DCA;TOLONG SIKI LETTER K;Lo;0;L;;;;;N;;;;; +11DCB;TOLONG SIKI LETTER KH;Lo;0;L;;;;;N;;;;; +11DCC;TOLONG SIKI LETTER G;Lo;0;L;;;;;N;;;;; +11DCD;TOLONG SIKI LETTER GH;Lo;0;L;;;;;N;;;;; +11DCE;TOLONG SIKI LETTER NG;Lo;0;L;;;;;N;;;;; +11DCF;TOLONG SIKI LETTER Y;Lo;0;L;;;;;N;;;;; +11DD0;TOLONG SIKI LETTER R;Lo;0;L;;;;;N;;;;; +11DD1;TOLONG SIKI LETTER L;Lo;0;L;;;;;N;;;;; +11DD2;TOLONG SIKI LETTER V;Lo;0;L;;;;;N;;;;; +11DD3;TOLONG SIKI LETTER NNY;Lo;0;L;;;;;N;;;;; +11DD4;TOLONG SIKI LETTER S;Lo;0;L;;;;;N;;;;; +11DD5;TOLONG SIKI LETTER H;Lo;0;L;;;;;N;;;;; +11DD6;TOLONG SIKI LETTER X;Lo;0;L;;;;;N;;;;; +11DD7;TOLONG SIKI LETTER RR;Lo;0;L;;;;;N;;;;; +11DD8;TOLONG SIKI LETTER RRH;Lo;0;L;;;;;N;;;;; +11DD9;TOLONG SIKI SIGN SELA;Lm;0;L;;;;;N;;;;; +11DDA;TOLONG SIKI SIGN HECAKA;Lo;0;L;;;;;N;;;;; +11DDB;TOLONG SIKI UNGGA;Lo;0;L;;;;;N;;;;; +11DE0;TOLONG SIKI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +11DE1;TOLONG SIKI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +11DE2;TOLONG SIKI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +11DE3;TOLONG SIKI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +11DE4;TOLONG SIKI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +11DE5;TOLONG SIKI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +11DE6;TOLONG SIKI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +11DE7;TOLONG SIKI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +11DE8;TOLONG SIKI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +11DE9;TOLONG SIKI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 11EE0;MAKASAR LETTER KA;Lo;0;L;;;;;N;;;;; 11EE1;MAKASAR LETTER GA;Lo;0;L;;;;;N;;;;; 11EE2;MAKASAR LETTER NGA;Lo;0;L;;;;;N;;;;; @@ -22088,8 +22252,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 12035;CUNEIFORM SIGN ARAD TIMES KUR;Lo;0;L;;;;;N;;;;; 12036;CUNEIFORM SIGN ARKAB;Lo;0;L;;;;;N;;;;; 12037;CUNEIFORM SIGN ASAL2;Lo;0;L;;;;;N;;;;; -12038;CUNEIFORM SIGN ASH;Lo;0;L;;;;;N;;;;; -12039;CUNEIFORM SIGN ASH ZIDA TENU;Lo;0;L;;;;;N;;;;; +12038;CUNEIFORM SIGN ASH;Lo;0;L;;;;1;N;;;;; +12039;CUNEIFORM SIGN ASH ZIDA TENU;Lo;0;L;;;;1;N;;;;; 1203A;CUNEIFORM SIGN ASH KABA TENU;Lo;0;L;;;;;N;;;;; 1203B;CUNEIFORM SIGN ASH OVER ASH TUG2 OVER TUG2 TUG2 OVER TUG2 PAP;Lo;0;L;;;;;N;;;;; 1203C;CUNEIFORM SIGN ASH OVER ASH OVER ASH;Lo;0;L;;;;;N;;;;; @@ -22153,7 +22317,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 12076;CUNEIFORM SIGN DIM2;Lo;0;L;;;;;N;;;;; 12077;CUNEIFORM SIGN DIN;Lo;0;L;;;;;N;;;;; 12078;CUNEIFORM SIGN DIN KASKAL U GUNU DISH;Lo;0;L;;;;;N;;;;; -12079;CUNEIFORM SIGN DISH;Lo;0;L;;;;;N;;;;; +12079;CUNEIFORM SIGN DISH;Lo;0;L;;;;1;N;;;;; 1207A;CUNEIFORM SIGN DU;Lo;0;L;;;;;N;;;;; 1207B;CUNEIFORM SIGN DU OVER DU;Lo;0;L;;;;;N;;;;; 1207C;CUNEIFORM SIGN DU GUNU;Lo;0;L;;;;;N;;;;; @@ -22582,12 +22746,12 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 12223;CUNEIFORM SIGN MA2;Lo;0;L;;;;;N;;;;; 12224;CUNEIFORM SIGN MAH;Lo;0;L;;;;;N;;;;; 12225;CUNEIFORM SIGN MAR;Lo;0;L;;;;;N;;;;; -12226;CUNEIFORM SIGN MASH;Lo;0;L;;;;;N;;;;; +12226;CUNEIFORM SIGN MASH;Lo;0;L;;;;1/2;N;;;;; 12227;CUNEIFORM SIGN MASH2;Lo;0;L;;;;;N;;;;; 12228;CUNEIFORM SIGN ME;Lo;0;L;;;;;N;;;;; 12229;CUNEIFORM SIGN MES;Lo;0;L;;;;;N;;;;; 1222A;CUNEIFORM SIGN MI;Lo;0;L;;;;;N;;;;; -1222B;CUNEIFORM SIGN MIN;Lo;0;L;;;;;N;;;;; +1222B;CUNEIFORM SIGN MIN;Lo;0;L;;;;2;N;;;;; 1222C;CUNEIFORM SIGN MU;Lo;0;L;;;;;N;;;;; 1222D;CUNEIFORM SIGN MU OVER MU;Lo;0;L;;;;;N;;;;; 1222E;CUNEIFORM SIGN MUG;Lo;0;L;;;;;N;;;;; @@ -22811,9 +22975,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 12308;CUNEIFORM SIGN TUM;Lo;0;L;;;;;N;;;;; 12309;CUNEIFORM SIGN TUR;Lo;0;L;;;;;N;;;;; 1230A;CUNEIFORM SIGN TUR OVER TUR ZA OVER ZA;Lo;0;L;;;;;N;;;;; -1230B;CUNEIFORM SIGN U;Lo;0;L;;;;;N;;;;; +1230B;CUNEIFORM SIGN U;Lo;0;L;;;;1;N;;;;; 1230C;CUNEIFORM SIGN U GUD;Lo;0;L;;;;;N;;;;; -1230D;CUNEIFORM SIGN U U U;Lo;0;L;;;;;N;;;;; +1230D;CUNEIFORM SIGN U U U;Lo;0;L;;;;3;N;;;;; 1230E;CUNEIFORM SIGN U OVER U PA OVER PA GAR OVER GAR;Lo;0;L;;;;;N;;;;; 1230F;CUNEIFORM SIGN U OVER U SUR OVER SUR;Lo;0;L;;;;;N;;;;; 12310;CUNEIFORM SIGN U OVER U U REVERSED OVER U REVERSED;Lo;0;L;;;;;N;;;;; @@ -22953,7 +23117,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 12396;CUNEIFORM SIGN SAG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 12397;CUNEIFORM SIGN TI2;Lo;0;L;;;;;N;;;;; 12398;CUNEIFORM SIGN UM TIMES ME;Lo;0;L;;;;;N;;;;; -12399;CUNEIFORM SIGN U U;Lo;0;L;;;;;N;;;;; +12399;CUNEIFORM SIGN U U;Lo;0;L;;;;2;N;;;;; 12400;CUNEIFORM NUMERIC SIGN TWO ASH;Nl;0;L;;;;2;N;;;;; 12401;CUNEIFORM NUMERIC SIGN THREE ASH;Nl;0;L;;;;3;N;;;;; 12402;CUNEIFORM NUMERIC SIGN FOUR ASH;Nl;0;L;;;;4;N;;;;; @@ -30124,6 +30288,56 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 16E98;MEDEFAIDRIN FULL STOP;Po;0;L;;;;;N;;;;; 16E99;MEDEFAIDRIN SYMBOL AIVA;Po;0;L;;;;;N;;;;; 16E9A;MEDEFAIDRIN EXCLAMATION OH;Po;0;L;;;;;N;;;;; +16EA0;BERIA ERFE CAPITAL LETTER ARKAB;Lu;0;L;;;;;N;;;;16EBB; +16EA1;BERIA ERFE CAPITAL LETTER BASIGNA;Lu;0;L;;;;;N;;;;16EBC; +16EA2;BERIA ERFE CAPITAL LETTER DARBAI;Lu;0;L;;;;;N;;;;16EBD; +16EA3;BERIA ERFE CAPITAL LETTER EH;Lu;0;L;;;;;N;;;;16EBE; +16EA4;BERIA ERFE CAPITAL LETTER FITKO;Lu;0;L;;;;;N;;;;16EBF; +16EA5;BERIA ERFE CAPITAL LETTER GOWAY;Lu;0;L;;;;;N;;;;16EC0; +16EA6;BERIA ERFE CAPITAL LETTER HIRDEABO;Lu;0;L;;;;;N;;;;16EC1; +16EA7;BERIA ERFE CAPITAL LETTER I;Lu;0;L;;;;;N;;;;16EC2; +16EA8;BERIA ERFE CAPITAL LETTER DJAI;Lu;0;L;;;;;N;;;;16EC3; +16EA9;BERIA ERFE CAPITAL LETTER KOBO;Lu;0;L;;;;;N;;;;16EC4; +16EAA;BERIA ERFE CAPITAL LETTER LAKKO;Lu;0;L;;;;;N;;;;16EC5; +16EAB;BERIA ERFE CAPITAL LETTER MERI;Lu;0;L;;;;;N;;;;16EC6; +16EAC;BERIA ERFE CAPITAL LETTER NINI;Lu;0;L;;;;;N;;;;16EC7; +16EAD;BERIA ERFE CAPITAL LETTER GNA;Lu;0;L;;;;;N;;;;16EC8; +16EAE;BERIA ERFE CAPITAL LETTER NGAY;Lu;0;L;;;;;N;;;;16EC9; +16EAF;BERIA ERFE CAPITAL LETTER OI;Lu;0;L;;;;;N;;;;16ECA; +16EB0;BERIA ERFE CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;16ECB; +16EB1;BERIA ERFE CAPITAL LETTER ERIGO;Lu;0;L;;;;;N;;;;16ECC; +16EB2;BERIA ERFE CAPITAL LETTER ERIGO TAMURA;Lu;0;L;;;;;N;;;;16ECD; +16EB3;BERIA ERFE CAPITAL LETTER SERI;Lu;0;L;;;;;N;;;;16ECE; +16EB4;BERIA ERFE CAPITAL LETTER SHEP;Lu;0;L;;;;;N;;;;16ECF; +16EB5;BERIA ERFE CAPITAL LETTER TATASOUE;Lu;0;L;;;;;N;;;;16ED0; +16EB6;BERIA ERFE CAPITAL LETTER UI;Lu;0;L;;;;;N;;;;16ED1; +16EB7;BERIA ERFE CAPITAL LETTER WASSE;Lu;0;L;;;;;N;;;;16ED2; +16EB8;BERIA ERFE CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;16ED3; +16EBB;BERIA ERFE SMALL LETTER ARKAB;Ll;0;L;;;;;N;;;16EA0;;16EA0 +16EBC;BERIA ERFE SMALL LETTER BASIGNA;Ll;0;L;;;;;N;;;16EA1;;16EA1 +16EBD;BERIA ERFE SMALL LETTER DARBAI;Ll;0;L;;;;;N;;;16EA2;;16EA2 +16EBE;BERIA ERFE SMALL LETTER EH;Ll;0;L;;;;;N;;;16EA3;;16EA3 +16EBF;BERIA ERFE SMALL LETTER FITKO;Ll;0;L;;;;;N;;;16EA4;;16EA4 +16EC0;BERIA ERFE SMALL LETTER GOWAY;Ll;0;L;;;;;N;;;16EA5;;16EA5 +16EC1;BERIA ERFE SMALL LETTER HIRDEABO;Ll;0;L;;;;;N;;;16EA6;;16EA6 +16EC2;BERIA ERFE SMALL LETTER I;Ll;0;L;;;;;N;;;16EA7;;16EA7 +16EC3;BERIA ERFE SMALL LETTER DJAI;Ll;0;L;;;;;N;;;16EA8;;16EA8 +16EC4;BERIA ERFE SMALL LETTER KOBO;Ll;0;L;;;;;N;;;16EA9;;16EA9 +16EC5;BERIA ERFE SMALL LETTER LAKKO;Ll;0;L;;;;;N;;;16EAA;;16EAA +16EC6;BERIA ERFE SMALL LETTER MERI;Ll;0;L;;;;;N;;;16EAB;;16EAB +16EC7;BERIA ERFE SMALL LETTER NINI;Ll;0;L;;;;;N;;;16EAC;;16EAC +16EC8;BERIA ERFE SMALL LETTER GNA;Ll;0;L;;;;;N;;;16EAD;;16EAD +16EC9;BERIA ERFE SMALL LETTER NGAY;Ll;0;L;;;;;N;;;16EAE;;16EAE +16ECA;BERIA ERFE SMALL LETTER OI;Ll;0;L;;;;;N;;;16EAF;;16EAF +16ECB;BERIA ERFE SMALL LETTER PI;Ll;0;L;;;;;N;;;16EB0;;16EB0 +16ECC;BERIA ERFE SMALL LETTER ERIGO;Ll;0;L;;;;;N;;;16EB1;;16EB1 +16ECD;BERIA ERFE SMALL LETTER ERIGO TAMURA;Ll;0;L;;;;;N;;;16EB2;;16EB2 +16ECE;BERIA ERFE SMALL LETTER SERI;Ll;0;L;;;;;N;;;16EB3;;16EB3 +16ECF;BERIA ERFE SMALL LETTER SHEP;Ll;0;L;;;;;N;;;16EB4;;16EB4 +16ED0;BERIA ERFE SMALL LETTER TATASOUE;Ll;0;L;;;;;N;;;16EB5;;16EB5 +16ED1;BERIA ERFE SMALL LETTER UI;Ll;0;L;;;;;N;;;16EB6;;16EB6 +16ED2;BERIA ERFE SMALL LETTER WASSE;Ll;0;L;;;;;N;;;16EB7;;16EB7 +16ED3;BERIA ERFE SMALL LETTER AY;Ll;0;L;;;;;N;;;16EB8;;16EB8 16F00;MIAO LETTER PA;Lo;0;L;;;;;N;;;;; 16F01;MIAO LETTER BA;Lo;0;L;;;;;N;;;;; 16F02;MIAO LETTER YI PA;Lo;0;L;;;;;N;;;;; @@ -30280,8 +30494,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 16FE4;KHITAN SMALL SCRIPT FILLER;Mn;0;NSM;;;;;N;;;;; 16FF0;VIETNAMESE ALTERNATE READING MARK CA;Mc;6;L;;;;;N;;;;; 16FF1;VIETNAMESE ALTERNATE READING MARK NHAY;Mc;6;L;;;;;N;;;;; +16FF2;CHINESE SMALL SIMPLIFIED ER;Lm;0;L;;;;;N;;;;; +16FF3;CHINESE SMALL TRADITIONAL ER;Lm;0;L;;;;;N;;;;; +16FF4;YANGQIN SIGN SLOW ONE BEAT;Nl;0;L;;;;1;N;;;;; +16FF5;YANGQIN SIGN SLOW THREE HALF BEATS;Nl;0;L;;;;3/2;N;;;;; +16FF6;YANGQIN SIGN SLOW TWO BEATS;Nl;0;L;;;;2;N;;;;; 17000;;Lo;0;L;;;;;N;;;;; -187F7;;Lo;0;L;;;;;N;;;;; +187FF;;Lo;0;L;;;;;N;;;;; 18800;TANGUT COMPONENT-001;Lo;0;L;;;;;N;;;;; 18801;TANGUT COMPONENT-002;Lo;0;L;;;;;N;;;;; 18802;TANGUT COMPONENT-003;Lo;0;L;;;;;N;;;;; @@ -31522,7 +31741,122 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 18CD5;KHITAN SMALL SCRIPT CHARACTER-18CD5;Lo;0;L;;;;;N;;;;; 18CFF;KHITAN SMALL SCRIPT CHARACTER-18CFF;Lo;0;L;;;;;N;;;;; 18D00;;Lo;0;L;;;;;N;;;;; -18D08;;Lo;0;L;;;;;N;;;;; +18D1E;;Lo;0;L;;;;;N;;;;; +18D80;TANGUT COMPONENT-769;Lo;0;L;;;;;N;;;;; +18D81;TANGUT COMPONENT-770;Lo;0;L;;;;;N;;;;; +18D82;TANGUT COMPONENT-771;Lo;0;L;;;;;N;;;;; +18D83;TANGUT COMPONENT-772;Lo;0;L;;;;;N;;;;; +18D84;TANGUT COMPONENT-773;Lo;0;L;;;;;N;;;;; +18D85;TANGUT COMPONENT-774;Lo;0;L;;;;;N;;;;; +18D86;TANGUT COMPONENT-775;Lo;0;L;;;;;N;;;;; +18D87;TANGUT COMPONENT-776;Lo;0;L;;;;;N;;;;; +18D88;TANGUT COMPONENT-777;Lo;0;L;;;;;N;;;;; +18D89;TANGUT COMPONENT-778;Lo;0;L;;;;;N;;;;; +18D8A;TANGUT COMPONENT-779;Lo;0;L;;;;;N;;;;; +18D8B;TANGUT COMPONENT-780;Lo;0;L;;;;;N;;;;; +18D8C;TANGUT COMPONENT-781;Lo;0;L;;;;;N;;;;; +18D8D;TANGUT COMPONENT-782;Lo;0;L;;;;;N;;;;; +18D8E;TANGUT COMPONENT-783;Lo;0;L;;;;;N;;;;; +18D8F;TANGUT COMPONENT-784;Lo;0;L;;;;;N;;;;; +18D90;TANGUT COMPONENT-785;Lo;0;L;;;;;N;;;;; +18D91;TANGUT COMPONENT-786;Lo;0;L;;;;;N;;;;; +18D92;TANGUT COMPONENT-787;Lo;0;L;;;;;N;;;;; +18D93;TANGUT COMPONENT-788;Lo;0;L;;;;;N;;;;; +18D94;TANGUT COMPONENT-789;Lo;0;L;;;;;N;;;;; +18D95;TANGUT COMPONENT-790;Lo;0;L;;;;;N;;;;; +18D96;TANGUT COMPONENT-791;Lo;0;L;;;;;N;;;;; +18D97;TANGUT COMPONENT-792;Lo;0;L;;;;;N;;;;; +18D98;TANGUT COMPONENT-793;Lo;0;L;;;;;N;;;;; +18D99;TANGUT COMPONENT-794;Lo;0;L;;;;;N;;;;; +18D9A;TANGUT COMPONENT-795;Lo;0;L;;;;;N;;;;; +18D9B;TANGUT COMPONENT-796;Lo;0;L;;;;;N;;;;; +18D9C;TANGUT COMPONENT-797;Lo;0;L;;;;;N;;;;; +18D9D;TANGUT COMPONENT-798;Lo;0;L;;;;;N;;;;; +18D9E;TANGUT COMPONENT-799;Lo;0;L;;;;;N;;;;; +18D9F;TANGUT COMPONENT-800;Lo;0;L;;;;;N;;;;; +18DA0;TANGUT COMPONENT-801;Lo;0;L;;;;;N;;;;; +18DA1;TANGUT COMPONENT-802;Lo;0;L;;;;;N;;;;; +18DA2;TANGUT COMPONENT-803;Lo;0;L;;;;;N;;;;; +18DA3;TANGUT COMPONENT-804;Lo;0;L;;;;;N;;;;; +18DA4;TANGUT COMPONENT-805;Lo;0;L;;;;;N;;;;; +18DA5;TANGUT COMPONENT-806;Lo;0;L;;;;;N;;;;; +18DA6;TANGUT COMPONENT-807;Lo;0;L;;;;;N;;;;; +18DA7;TANGUT COMPONENT-808;Lo;0;L;;;;;N;;;;; +18DA8;TANGUT COMPONENT-809;Lo;0;L;;;;;N;;;;; +18DA9;TANGUT COMPONENT-810;Lo;0;L;;;;;N;;;;; +18DAA;TANGUT COMPONENT-811;Lo;0;L;;;;;N;;;;; +18DAB;TANGUT COMPONENT-812;Lo;0;L;;;;;N;;;;; +18DAC;TANGUT COMPONENT-813;Lo;0;L;;;;;N;;;;; +18DAD;TANGUT COMPONENT-814;Lo;0;L;;;;;N;;;;; +18DAE;TANGUT COMPONENT-815;Lo;0;L;;;;;N;;;;; +18DAF;TANGUT COMPONENT-816;Lo;0;L;;;;;N;;;;; +18DB0;TANGUT COMPONENT-817;Lo;0;L;;;;;N;;;;; +18DB1;TANGUT COMPONENT-818;Lo;0;L;;;;;N;;;;; +18DB2;TANGUT COMPONENT-819;Lo;0;L;;;;;N;;;;; +18DB3;TANGUT COMPONENT-820;Lo;0;L;;;;;N;;;;; +18DB4;TANGUT COMPONENT-821;Lo;0;L;;;;;N;;;;; +18DB5;TANGUT COMPONENT-822;Lo;0;L;;;;;N;;;;; +18DB6;TANGUT COMPONENT-823;Lo;0;L;;;;;N;;;;; +18DB7;TANGUT COMPONENT-824;Lo;0;L;;;;;N;;;;; +18DB8;TANGUT COMPONENT-825;Lo;0;L;;;;;N;;;;; +18DB9;TANGUT COMPONENT-826;Lo;0;L;;;;;N;;;;; +18DBA;TANGUT COMPONENT-827;Lo;0;L;;;;;N;;;;; +18DBB;TANGUT COMPONENT-828;Lo;0;L;;;;;N;;;;; +18DBC;TANGUT COMPONENT-829;Lo;0;L;;;;;N;;;;; +18DBD;TANGUT COMPONENT-830;Lo;0;L;;;;;N;;;;; +18DBE;TANGUT COMPONENT-831;Lo;0;L;;;;;N;;;;; +18DBF;TANGUT COMPONENT-832;Lo;0;L;;;;;N;;;;; +18DC0;TANGUT COMPONENT-833;Lo;0;L;;;;;N;;;;; +18DC1;TANGUT COMPONENT-834;Lo;0;L;;;;;N;;;;; +18DC2;TANGUT COMPONENT-835;Lo;0;L;;;;;N;;;;; +18DC3;TANGUT COMPONENT-836;Lo;0;L;;;;;N;;;;; +18DC4;TANGUT COMPONENT-837;Lo;0;L;;;;;N;;;;; +18DC5;TANGUT COMPONENT-838;Lo;0;L;;;;;N;;;;; +18DC6;TANGUT COMPONENT-839;Lo;0;L;;;;;N;;;;; +18DC7;TANGUT COMPONENT-840;Lo;0;L;;;;;N;;;;; +18DC8;TANGUT COMPONENT-841;Lo;0;L;;;;;N;;;;; +18DC9;TANGUT COMPONENT-842;Lo;0;L;;;;;N;;;;; +18DCA;TANGUT COMPONENT-843;Lo;0;L;;;;;N;;;;; +18DCB;TANGUT COMPONENT-844;Lo;0;L;;;;;N;;;;; +18DCC;TANGUT COMPONENT-845;Lo;0;L;;;;;N;;;;; +18DCD;TANGUT COMPONENT-846;Lo;0;L;;;;;N;;;;; +18DCE;TANGUT COMPONENT-847;Lo;0;L;;;;;N;;;;; +18DCF;TANGUT COMPONENT-848;Lo;0;L;;;;;N;;;;; +18DD0;TANGUT COMPONENT-849;Lo;0;L;;;;;N;;;;; +18DD1;TANGUT COMPONENT-850;Lo;0;L;;;;;N;;;;; +18DD2;TANGUT COMPONENT-851;Lo;0;L;;;;;N;;;;; +18DD3;TANGUT COMPONENT-852;Lo;0;L;;;;;N;;;;; +18DD4;TANGUT COMPONENT-853;Lo;0;L;;;;;N;;;;; +18DD5;TANGUT COMPONENT-854;Lo;0;L;;;;;N;;;;; +18DD6;TANGUT COMPONENT-855;Lo;0;L;;;;;N;;;;; +18DD7;TANGUT COMPONENT-856;Lo;0;L;;;;;N;;;;; +18DD8;TANGUT COMPONENT-857;Lo;0;L;;;;;N;;;;; +18DD9;TANGUT COMPONENT-858;Lo;0;L;;;;;N;;;;; +18DDA;TANGUT COMPONENT-859;Lo;0;L;;;;;N;;;;; +18DDB;TANGUT COMPONENT-860;Lo;0;L;;;;;N;;;;; +18DDC;TANGUT COMPONENT-861;Lo;0;L;;;;;N;;;;; +18DDD;TANGUT COMPONENT-862;Lo;0;L;;;;;N;;;;; +18DDE;TANGUT COMPONENT-863;Lo;0;L;;;;;N;;;;; +18DDF;TANGUT COMPONENT-864;Lo;0;L;;;;;N;;;;; +18DE0;TANGUT COMPONENT-865;Lo;0;L;;;;;N;;;;; +18DE1;TANGUT COMPONENT-866;Lo;0;L;;;;;N;;;;; +18DE2;TANGUT COMPONENT-867;Lo;0;L;;;;;N;;;;; +18DE3;TANGUT COMPONENT-868;Lo;0;L;;;;;N;;;;; +18DE4;TANGUT COMPONENT-869;Lo;0;L;;;;;N;;;;; +18DE5;TANGUT COMPONENT-870;Lo;0;L;;;;;N;;;;; +18DE6;TANGUT COMPONENT-871;Lo;0;L;;;;;N;;;;; +18DE7;TANGUT COMPONENT-872;Lo;0;L;;;;;N;;;;; +18DE8;TANGUT COMPONENT-873;Lo;0;L;;;;;N;;;;; +18DE9;TANGUT COMPONENT-874;Lo;0;L;;;;;N;;;;; +18DEA;TANGUT COMPONENT-875;Lo;0;L;;;;;N;;;;; +18DEB;TANGUT COMPONENT-876;Lo;0;L;;;;;N;;;;; +18DEC;TANGUT COMPONENT-877;Lo;0;L;;;;;N;;;;; +18DED;TANGUT COMPONENT-878;Lo;0;L;;;;;N;;;;; +18DEE;TANGUT COMPONENT-879;Lo;0;L;;;;;N;;;;; +18DEF;TANGUT COMPONENT-880;Lo;0;L;;;;;N;;;;; +18DF0;TANGUT COMPONENT-881;Lo;0;L;;;;;N;;;;; +18DF1;TANGUT COMPONENT-882;Lo;0;L;;;;;N;;;;; +18DF2;TANGUT COMPONENT-883;Lo;0;L;;;;;N;;;;; 1AFF0;KATAKANA LETTER MINNAN TONE-2;Lm;0;L;;;;;N;;;;; 1AFF1;KATAKANA LETTER MINNAN TONE-3;Lm;0;L;;;;;N;;;;; 1AFF2;KATAKANA LETTER MINNAN TONE-4;Lm;0;L;;;;;N;;;;; @@ -32629,6 +32963,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1CCF7;OUTLINED DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; 1CCF8;OUTLINED DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; 1CCF9;OUTLINED DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; +1CCFA;SNAKE SYMBOL;So;0;ON;;;;;N;;;;; +1CCFB;FLYING SAUCER SYMBOL;So;0;ON;;;;;N;;;;; +1CCFC;NOSE SYMBOL;So;0;ON;;;;;N;;;;; 1CD00;BLOCK OCTANT-3;So;0;ON;;;;;N;;;;; 1CD01;BLOCK OCTANT-23;So;0;ON;;;;;N;;;;; 1CD02;BLOCK OCTANT-123;So;0;ON;;;;;N;;;;; @@ -33065,6 +33402,46 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1CEB1;KEYHOLE;So;0;ON;;;;;N;;;;; 1CEB2;OLD PERSONAL COMPUTER WITH MONITOR IN PORTRAIT ORIENTATION;So;0;ON;;;;;N;;;;; 1CEB3;BLACK RIGHT TRIANGLE CARET;So;0;ON;;;;;N;;;;; +1CEBA;FRAGILE SYMBOL;So;0;ON;;;;;N;;;;; +1CEBB;OFFICE BUILDING SYMBOL;So;0;ON;;;;;N;;;;; +1CEBC;TREE SYMBOL;So;0;ON;;;;;N;;;;; +1CEBD;APPLE SYMBOL;So;0;ON;;;;;N;;;;; +1CEBE;CHERRY SYMBOL;So;0;ON;;;;;N;;;;; +1CEBF;STRAWBERRY SYMBOL;So;0;ON;;;;;N;;;;; +1CEC0;HEBE;So;0;ON;;;;;N;;;;; +1CEC1;IRIS;So;0;ON;;;;;N;;;;; +1CEC2;FLORA;So;0;ON;;;;;N;;;;; +1CEC3;METIS;So;0;ON;;;;;N;;;;; +1CEC4;PARTHENOPE;So;0;ON;;;;;N;;;;; +1CEC5;VICTORIA;So;0;ON;;;;;N;;;;; +1CEC6;EGERIA;So;0;ON;;;;;N;;;;; +1CEC7;IRENE;So;0;ON;;;;;N;;;;; +1CEC8;EUNOMIA;So;0;ON;;;;;N;;;;; +1CEC9;PSYCHE;So;0;ON;;;;;N;;;;; +1CECA;THETIS;So;0;ON;;;;;N;;;;; +1CECB;MELPOMENE;So;0;ON;;;;;N;;;;; +1CECC;FORTUNA;So;0;ON;;;;;N;;;;; +1CECD;ASTRONOMICAL SYMBOL FOR ASTEROID PROSERPINA;So;0;ON;;;;;N;;;;; +1CECE;BELLONA;So;0;ON;;;;;N;;;;; +1CECF;AMPHITRITE;So;0;ON;;;;;N;;;;; +1CED0;LEUKOTHEA;So;0;ON;;;;;N;;;;; +1CEE0;GEOMANTIC FIGURE POPULUS;So;0;ON;;;;;N;;;;; +1CEE1;GEOMANTIC FIGURE TRISTITIA;So;0;ON;;;;;N;;;;; +1CEE2;GEOMANTIC FIGURE ALBUS;So;0;ON;;;;;N;;;;; +1CEE3;GEOMANTIC FIGURE FORTUNA MAJOR;So;0;ON;;;;;N;;;;; +1CEE4;GEOMANTIC FIGURE RUBEUS;So;0;ON;;;;;N;;;;; +1CEE5;GEOMANTIC FIGURE ACQUISITIO;So;0;ON;;;;;N;;;;; +1CEE6;GEOMANTIC FIGURE CONJUNCTIO;So;0;ON;;;;;N;;;;; +1CEE7;GEOMANTIC FIGURE CAPUT DRACONIS;So;0;ON;;;;;N;;;;; +1CEE8;GEOMANTIC FIGURE LAETITIA;So;0;ON;;;;;N;;;;; +1CEE9;GEOMANTIC FIGURE CARCER;So;0;ON;;;;;N;;;;; +1CEEA;GEOMANTIC FIGURE AMISSIO;So;0;ON;;;;;N;;;;; +1CEEB;GEOMANTIC FIGURE PUELLA;So;0;ON;;;;;N;;;;; +1CEEC;GEOMANTIC FIGURE FORTUNA MINOR;So;0;ON;;;;;N;;;;; +1CEED;GEOMANTIC FIGURE PUER;So;0;ON;;;;;N;;;;; +1CEEE;GEOMANTIC FIGURE CAUDA DRACONIS;So;0;ON;;;;;N;;;;; +1CEEF;GEOMANTIC FIGURE VIA;So;0;ON;;;;;N;;;;; +1CEF0;MEDIUM SMALL WHITE CIRCLE WITH HORIZONTAL BAR;Sm;0;ON;;;;;N;;;;; 1CF00;ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT;Mn;0;NSM;;;;;N;;;;; 1CF01;ZNAMENNY COMBINING MARK NIZKO S KRYZHEM ON LEFT;Mn;0;NSM;;;;;N;;;;; 1CF02;ZNAMENNY COMBINING MARK TSATA ON LEFT;Mn;0;NSM;;;;;N;;;;; @@ -36004,6 +36381,61 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1E5F9;OL ONAL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1E5FA;OL ONAL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1E5FF;OL ONAL ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +1E6C0;TAI YO LETTER LOW KO;Lo;0;L;;;;;N;;;;; +1E6C1;TAI YO LETTER HIGH KO;Lo;0;L;;;;;N;;;;; +1E6C2;TAI YO LETTER LOW KHO;Lo;0;L;;;;;N;;;;; +1E6C3;TAI YO LETTER HIGH KHO;Lo;0;L;;;;;N;;;;; +1E6C4;TAI YO LETTER GO;Lo;0;L;;;;;N;;;;; +1E6C5;TAI YO LETTER NGO;Lo;0;L;;;;;N;;;;; +1E6C6;TAI YO LETTER CO;Lo;0;L;;;;;N;;;;; +1E6C7;TAI YO LETTER LOW XO;Lo;0;L;;;;;N;;;;; +1E6C8;TAI YO LETTER HIGH XO;Lo;0;L;;;;;N;;;;; +1E6C9;TAI YO LETTER LOW NYO;Lo;0;L;;;;;N;;;;; +1E6CA;TAI YO LETTER HIGH NYO;Lo;0;L;;;;;N;;;;; +1E6CB;TAI YO LETTER DO;Lo;0;L;;;;;N;;;;; +1E6CC;TAI YO LETTER LOW TO;Lo;0;L;;;;;N;;;;; +1E6CD;TAI YO LETTER HIGH TO;Lo;0;L;;;;;N;;;;; +1E6CE;TAI YO LETTER THO;Lo;0;L;;;;;N;;;;; +1E6CF;TAI YO LETTER NO;Lo;0;L;;;;;N;;;;; +1E6D0;TAI YO LETTER BO;Lo;0;L;;;;;N;;;;; +1E6D1;TAI YO LETTER LOW PO;Lo;0;L;;;;;N;;;;; +1E6D2;TAI YO LETTER HIGH PO;Lo;0;L;;;;;N;;;;; +1E6D3;TAI YO LETTER PHO;Lo;0;L;;;;;N;;;;; +1E6D4;TAI YO LETTER LOW FO;Lo;0;L;;;;;N;;;;; +1E6D5;TAI YO LETTER HIGH FO;Lo;0;L;;;;;N;;;;; +1E6D6;TAI YO LETTER MO;Lo;0;L;;;;;N;;;;; +1E6D7;TAI YO LETTER YO;Lo;0;L;;;;;N;;;;; +1E6D8;TAI YO LETTER LO;Lo;0;L;;;;;N;;;;; +1E6D9;TAI YO LETTER VO;Lo;0;L;;;;;N;;;;; +1E6DA;TAI YO LETTER LOW HO;Lo;0;L;;;;;N;;;;; +1E6DB;TAI YO LETTER HIGH HO;Lo;0;L;;;;;N;;;;; +1E6DC;TAI YO LETTER QO;Lo;0;L;;;;;N;;;;; +1E6DD;TAI YO LETTER LOW KVO;Lo;0;L;;;;;N;;;;; +1E6DE;TAI YO LETTER HIGH KVO;Lo;0;L;;;;;N;;;;; +1E6E0;TAI YO LETTER AA;Lo;0;L;;;;;N;;;;; +1E6E1;TAI YO LETTER I;Lo;0;L;;;;;N;;;;; +1E6E2;TAI YO LETTER UE;Lo;0;L;;;;;N;;;;; +1E6E3;TAI YO SIGN UE;Mn;230;NSM;;;;;N;;;;; +1E6E4;TAI YO LETTER U;Lo;0;L;;;;;N;;;;; +1E6E5;TAI YO LETTER AE;Lo;0;L;;;;;N;;;;; +1E6E6;TAI YO SIGN AU;Mn;230;NSM;;;;;N;;;;; +1E6E7;TAI YO LETTER O;Lo;0;L;;;;;N;;;;; +1E6E8;TAI YO LETTER E;Lo;0;L;;;;;N;;;;; +1E6E9;TAI YO LETTER IA;Lo;0;L;;;;;N;;;;; +1E6EA;TAI YO LETTER UEA;Lo;0;L;;;;;N;;;;; +1E6EB;TAI YO LETTER UA;Lo;0;L;;;;;N;;;;; +1E6EC;TAI YO LETTER OO;Lo;0;L;;;;;N;;;;; +1E6ED;TAI YO LETTER AUE;Lo;0;L;;;;;N;;;;; +1E6EE;TAI YO SIGN AY;Mn;230;NSM;;;;;N;;;;; +1E6EF;TAI YO SIGN ANG;Mn;230;NSM;;;;;N;;;;; +1E6F0;TAI YO LETTER AN;Lo;0;L;;;;;N;;;;; +1E6F1;TAI YO LETTER AM;Lo;0;L;;;;;N;;;;; +1E6F2;TAI YO LETTER AK;Lo;0;L;;;;;N;;;;; +1E6F3;TAI YO LETTER AT;Lo;0;L;;;;;N;;;;; +1E6F4;TAI YO LETTER AP;Lo;0;L;;;;;N;;;;; +1E6F5;TAI YO SIGN OM;Mn;230;NSM;;;;;N;;;;; +1E6FE;TAI YO SYMBOL MUEANG;Lo;0;L;;;;;N;;;;; +1E6FF;TAI YO XAM LAI;Lm;0;L;;;;;N;;;;; 1E7E0;ETHIOPIC SYLLABLE HHYA;Lo;0;L;;;;;N;;;;; 1E7E1;ETHIOPIC SYLLABLE HHYU;Lo;0;L;;;;;N;;;;; 1E7E2;ETHIOPIC SYLLABLE HHYI;Lo;0;L;;;;;N;;;;; @@ -38079,6 +38511,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F6D5;HINDU TEMPLE;So;0;ON;;;;;N;;;;; 1F6D6;HUT;So;0;ON;;;;;N;;;;; 1F6D7;ELEVATOR;So;0;ON;;;;;N;;;;; +1F6D8;LANDSLIDE;So;0;ON;;;;;N;;;;; 1F6DC;WIRELESS;So;0;ON;;;;;N;;;;; 1F6DD;PLAYGROUND SLIDE;So;0;ON;;;;;N;;;;; 1F6DE;WHEEL;So;0;ON;;;;;N;;;;; @@ -38228,6 +38661,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F774;LOT OF FORTUNE;So;0;ON;;;;;N;;;;; 1F775;OCCULTATION;So;0;ON;;;;;N;;;;; 1F776;LUNAR ECLIPSE;So;0;ON;;;;;N;;;;; +1F777;VESTA FORM TWO;So;0;ON;;;;;N;;;;; +1F778;ASTRAEA FORM TWO;So;0;ON;;;;;N;;;;; +1F779;HYGIEA FORM TWO;So;0;ON;;;;;N;;;;; +1F77A;PARTHENOPE FORM TWO;So;0;ON;;;;;N;;;;; 1F77B;HAUMEA;So;0;ON;;;;;N;;;;; 1F77C;MAKEMAKE;So;0;ON;;;;;N;;;;; 1F77D;GONGGONG;So;0;ON;;;;;N;;;;; @@ -38498,6 +38935,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F8BB;SOUTH WEST ARROW FROM BAR;So;0;ON;;;;;N;;;;; 1F8C0;LEFTWARDS ARROW FROM DOWNWARDS ARROW;So;0;ON;;;;;N;;;;; 1F8C1;RIGHTWARDS ARROW FROM DOWNWARDS ARROW;So;0;ON;;;;;N;;;;; +1F8D0;LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +1F8D1;LONG RIGHTWARDS HARPOON OVER LONG LEFTWARDS HARPOON;Sm;0;ON;;;;;N;;;;; +1F8D2;LONG RIGHTWARDS HARPOON ABOVE SHORT LEFTWARDS HARPOON;Sm;0;ON;;;;;N;;;;; +1F8D3;SHORT RIGHTWARDS HARPOON ABOVE LONG LEFTWARDS HARPOON;Sm;0;ON;;;;;N;;;;; +1F8D4;LONG LEFTWARDS HARPOON ABOVE SHORT RIGHTWARDS HARPOON;Sm;0;ON;;;;;N;;;;; +1F8D5;SHORT LEFTWARDS HARPOON ABOVE LONG RIGHTWARDS HARPOON;Sm;0;ON;;;;;N;;;;; +1F8D6;LONG RIGHTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;; +1F8D7;LONG RIGHTWARDS ARROW WITH DOUBLE SLASH;Sm;0;ON;;;;;N;;;;; +1F8D8;LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE;Sm;0;ON;;;;;N;;;;; 1F900;CIRCLED CROSS FORMEE WITH FOUR DOTS;So;0;ON;;;;;N;;;;; 1F901;CIRCLED CROSS FORMEE WITH TWO DOTS;So;0;ON;;;;;N;;;;; 1F902;CIRCLED CROSS FORMEE;So;0;ON;;;;;N;;;;; @@ -38838,6 +39284,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1FA51;BLACK CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;; 1FA52;BLACK CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;; 1FA53;BLACK CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;; +1FA54;WHITE CHESS FERZ;So;0;ON;;;;;N;;;;; +1FA55;WHITE CHESS ALFIL;So;0;ON;;;;;N;;;;; +1FA56;BLACK CHESS FERZ;So;0;ON;;;;;N;;;;; +1FA57;BLACK CHESS ALFIL;So;0;ON;;;;;N;;;;; 1FA60;XIANGQI RED GENERAL;So;0;ON;;;;;N;;;;; 1FA61;XIANGQI RED MANDARIN;So;0;ON;;;;;N;;;;; 1FA62;XIANGQI RED ELEPHANT;So;0;ON;;;;;N;;;;; @@ -38875,6 +39325,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1FA87;MARACAS;So;0;ON;;;;;N;;;;; 1FA88;FLUTE;So;0;ON;;;;;N;;;;; 1FA89;HARP;So;0;ON;;;;;N;;;;; +1FA8A;TROMBONE;So;0;ON;;;;;N;;;;; +1FA8E;TREASURE CHEST;So;0;ON;;;;;N;;;;; 1FA8F;SHOVEL;So;0;ON;;;;;N;;;;; 1FA90;RINGED PLANET;So;0;ON;;;;;N;;;;; 1FA91;CHAIR;So;0;ON;;;;;N;;;;; @@ -38931,6 +39383,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1FAC4;PREGNANT PERSON;So;0;ON;;;;;N;;;;; 1FAC5;PERSON WITH CROWN;So;0;ON;;;;;N;;;;; 1FAC6;FINGERPRINT;So;0;ON;;;;;N;;;;; +1FAC8;HAIRY CREATURE;So;0;ON;;;;;N;;;;; +1FACD;ORCA;So;0;ON;;;;;N;;;;; 1FACE;MOOSE;So;0;ON;;;;;N;;;;; 1FACF;DONKEY;So;0;ON;;;;;N;;;;; 1FAD0;BLUEBERRIES;So;0;ON;;;;;N;;;;; @@ -38957,6 +39411,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1FAE7;BUBBLES;So;0;ON;;;;;N;;;;; 1FAE8;SHAKING FACE;So;0;ON;;;;;N;;;;; 1FAE9;FACE WITH BAGS UNDER EYES;So;0;ON;;;;;N;;;;; +1FAEA;DISTORTED FACE;So;0;ON;;;;;N;;;;; +1FAEF;FIGHT CLOUD;So;0;ON;;;;;N;;;;; 1FAF0;HAND WITH INDEX FINGER AND THUMB CROSSED;So;0;ON;;;;;N;;;;; 1FAF1;RIGHTWARDS HAND;So;0;ON;;;;;N;;;;; 1FAF2;LEFTWARDS HAND;So;0;ON;;;;;N;;;;; @@ -39215,14 +39671,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1FBF7;SEGMENTED DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; 1FBF8;SEGMENTED DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; 1FBF9;SEGMENTED DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; +1FBFA;ALARM BELL SYMBOL;So;0;ON;;;;;N;;;;; 20000;;Lo;0;L;;;;;N;;;;; 2A6DF;;Lo;0;L;;;;;N;;;;; 2A700;;Lo;0;L;;;;;N;;;;; -2B739;;Lo;0;L;;;;;N;;;;; +2B73F;;Lo;0;L;;;;;N;;;;; 2B740;;Lo;0;L;;;;;N;;;;; 2B81D;;Lo;0;L;;;;;N;;;;; 2B820;;Lo;0;L;;;;;N;;;;; -2CEA1;;Lo;0;L;;;;;N;;;;; +2CEAD;;Lo;0;L;;;;;N;;;;; 2CEB0;;Lo;0;L;;;;;N;;;;; 2EBE0;;Lo;0;L;;;;;N;;;;; 2EBF0;;Lo;0;L;;;;;N;;;;; @@ -39773,6 +40230,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 3134A;;Lo;0;L;;;;;N;;;;; 31350;;Lo;0;L;;;;;N;;;;; 323AF;;Lo;0;L;;;;;N;;;;; +323B0;;Lo;0;L;;;;;N;;;;; +33479;;Lo;0;L;;;;;N;;;;; E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;; E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;; E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;; diff --git a/resources/standards-data/unicode-character-database/WordBreakProperty.txt b/resources/standards-data/unicode-character-database/WordBreakProperty.txt index e557c3d0de..20fa24e375 100644 --- a/resources/standards-data/unicode-character-database/WordBreakProperty.txt +++ b/resources/standards-data/unicode-character-database/WordBreakProperty.txt @@ -1,6 +1,6 @@ -# WordBreakProperty-16.0.0.txt -# Date: 2024-07-29, 16:27:36 GMT -# © 2024 Unicode®, Inc. +# WordBreakProperty-17.0.0.txt +# Date: 2025-06-30, 06:20:49 GMT +# © 2025 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. # For terms of use and license, see https://www.unicode.org/terms_of_use.html # @@ -283,7 +283,8 @@ FB46..FB4F ; Hebrew_Letter # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW 1A7F ; Extend # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT 1AB0..1ABD ; Extend # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW 1ABE ; Extend # Me COMBINING PARENTHESES OVERLAY -1ABF..1ACE ; Extend # Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T +1ABF..1ADD ; Extend # Mn [31] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOT-AND-RING BELOW +1AE0..1AEB ; Extend # Mn [12] COMBINING LEFT TACK ABOVE..COMBINING DOUBLE RIGHTWARDS ARROW ABOVE 1B00..1B03 ; Extend # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG 1B04 ; Extend # Mc BALINESE SIGN BISAH 1B34 ; Extend # Mn BALINESE SIGN REREKAN @@ -409,7 +410,7 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT 10D24..10D27 ; Extend # Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI 10D69..10D6D ; Extend # Mn [5] GARAY VOWEL SIGN E..GARAY CONSONANT NASALIZATION MARK 10EAB..10EAC ; Extend # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK -10EFC..10EFF ; Extend # Mn [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA +10EFA..10EFF ; Extend # Mn [6] ARABIC DOUBLE VERTICAL BAR BELOW..ARABIC SMALL LOW WORD MADDA 10F46..10F50 ; Extend # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW 10F82..10F85 ; Extend # Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW 11000 ; Extend # Mc BRAHMI SIGN CANDRABINDU @@ -544,6 +545,12 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT 11A8A..11A96 ; Extend # Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA 11A97 ; Extend # Mc SOYOMBO SIGN VISARGA 11A98..11A99 ; Extend # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER +11B60 ; Extend # Mn SHARADA VOWEL SIGN OE +11B61 ; Extend # Mc SHARADA VOWEL SIGN OOE +11B62..11B64 ; Extend # Mn [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E +11B65 ; Extend # Mc SHARADA VOWEL SIGN SHORT O +11B66 ; Extend # Mn SHARADA VOWEL SIGN CANDRA E +11B67 ; Extend # Mc SHARADA VOWEL SIGN CANDRA O 11C2F ; Extend # Mc BHAIKSUKI VOWEL SIGN AA 11C30..11C36 ; Extend # Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L 11C38..11C3D ; Extend # Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA @@ -617,13 +624,17 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT 1E2EC..1E2EF ; Extend # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI 1E4EC..1E4EF ; Extend # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH 1E5EE..1E5EF ; Extend # Mn [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR +1E6E3 ; Extend # Mn TAI YO SIGN UE +1E6E6 ; Extend # Mn TAI YO SIGN AU +1E6EE..1E6EF ; Extend # Mn [2] TAI YO SIGN AY..TAI YO SIGN ANG +1E6F5 ; Extend # Mn TAI YO SIGN OM 1E8D0..1E8D6 ; Extend # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS 1E944..1E94A ; Extend # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA 1F3FB..1F3FF ; Extend # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 E0020..E007F ; Extend # Cf [96] TAG SPACE..CANCEL TAG E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 -# Total code points: 2605 +# Total code points: 2647 # ================================================ @@ -679,6 +690,7 @@ FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAK 0061..007A ; ALetter # L& [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z 00AA ; ALetter # Lo FEMININE ORDINAL INDICATOR 00B5 ; ALetter # L& MICRO SIGN +00B8 ; ALetter # Sk CEDILLA 00BA ; ALetter # Lo MASCULINE ORDINAL INDICATOR 00C0..00D6 ; ALetter # L& [23] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER O WITH DIAERESIS 00D8..00F6 ; ALetter # L& [31] LATIN CAPITAL LETTER O WITH STROKE..LATIN SMALL LETTER O WITH DIAERESIS @@ -687,8 +699,8 @@ FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAK 01BC..01BF ; ALetter # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN 01C0..01C3 ; ALetter # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK 01C4..0293 ; ALetter # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL -0294 ; ALetter # Lo LATIN LETTER GLOTTAL STOP -0295..02AF ; ALetter # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL +0294..0295 ; ALetter # Lo [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE +0296..02AF ; ALetter # L& [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02C1 ; ALetter # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP 02C2..02C5 ; ALetter # Sk [4] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER DOWN ARROWHEAD 02C6..02D1 ; ALetter # Lm [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON @@ -745,7 +757,7 @@ FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAK 0840..0858 ; ALetter # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN 0860..086A ; ALetter # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA 0870..0887 ; ALetter # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT -0889..088E ; ALetter # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL +0889..088F ; ALetter # Lo [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE 08A0..08C8 ; ALetter # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF 08C9 ; ALetter # Lm ARABIC SMALL FARSI YEH 0904..0939 ; ALetter # Lo [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA @@ -813,7 +825,7 @@ FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAK 0C2A..0C39 ; ALetter # Lo [16] TELUGU LETTER PA..TELUGU LETTER HA 0C3D ; ALetter # Lo TELUGU SIGN AVAGRAHA 0C58..0C5A ; ALetter # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA -0C5D ; ALetter # Lo TELUGU LETTER NAKAARA POLLU +0C5C..0C5D ; ALetter # Lo [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU 0C60..0C61 ; ALetter # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL 0C80 ; ALetter # Lo KANNADA SIGN SPACING CANDRABINDU 0C85..0C8C ; ALetter # Lo [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L @@ -822,7 +834,7 @@ FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAK 0CAA..0CB3 ; ALetter # Lo [10] KANNADA LETTER PA..KANNADA LETTER LLA 0CB5..0CB9 ; ALetter # Lo [5] KANNADA LETTER VA..KANNADA LETTER HA 0CBD ; ALetter # Lo KANNADA SIGN AVAGRAHA -0CDD..0CDE ; ALetter # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA +0CDC..0CDE ; ALetter # Lo [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA 0CE0..0CE1 ; ALetter # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL 0CF1..0CF2 ; ALetter # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA 0D04..0D0C ; ALetter # Lo [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L @@ -1002,11 +1014,8 @@ A788 ; ALetter # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT A789..A78A ; ALetter # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN A78B..A78E ; ALetter # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT A78F ; ALetter # Lo LATIN LETTER SINOLOGICAL DOT -A790..A7CD ; ALetter # L& [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE -A7D0..A7D1 ; ALetter # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G -A7D3 ; ALetter # L& LATIN SMALL LETTER DOUBLE THORN -A7D5..A7DC ; ALetter # L& [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE -A7F2..A7F4 ; ALetter # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A790..A7DC ; ALetter # L& [77] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER LAMBDA WITH STROKE +A7F1..A7F4 ; ALetter # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q A7F5..A7F6 ; ALetter # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H A7F7 ; ALetter # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I A7F8..A7F9 ; ALetter # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE @@ -1115,6 +1124,7 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 108F4..108F5 ; ALetter # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW 10900..10915 ; ALetter # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU 10920..10939 ; ALetter # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C +10940..10959 ; ALetter # Lo [26] SIDETIC LETTER N01..SIDETIC LETTER N26 10980..109B7 ; ALetter # Lo [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA 109BE..109BF ; ALetter # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN 10A00 ; ALetter # Lo KHAROSHTHI LETTER A @@ -1142,6 +1152,8 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 10E80..10EA9 ; ALetter # Lo [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET 10EB0..10EB1 ; ALetter # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE 10EC2..10EC4 ; ALetter # Lo [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW +10EC5 ; ALetter # Lm ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW +10EC6..10EC7 ; ALetter # Lo [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW 10F00..10F1C ; ALetter # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL 10F27 ; ALetter # Lo OLD SOGDIAN LIGATURE AYIN-DALETH 10F30..10F45 ; ALetter # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN @@ -1232,6 +1244,9 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 11D67..11D68 ; ALetter # Lo [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI 11D6A..11D89 ; ALetter # Lo [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA 11D98 ; ALetter # Lo GUNJALA GONDI OM +11DB0..11DD8 ; ALetter # Lo [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH +11DD9 ; ALetter # Lm TOLONG SIKI SIGN SELA +11DDA..11DDB ; ALetter # Lo [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA 11EE0..11EF2 ; ALetter # Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA 11F02 ; ALetter # Lo KAWI SIGN REPHA 11F04..11F10 ; ALetter # Lo [13] KAWI LETTER A..KAWI LETTER O @@ -1258,6 +1273,8 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 16D43..16D6A ; ALetter # Lo [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU 16D6B..16D6C ; ALetter # Lm [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT 16E40..16E7F ; ALetter # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EA0..16EB8 ; ALetter # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY +16EBB..16ED3 ; ALetter # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 16F00..16F4A ; ALetter # Lo [75] MIAO LETTER PA..MIAO LETTER RTE 16F50 ; ALetter # Lo MIAO LETTER NASALIZATION 16F93..16F9F ; ALetter # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8 @@ -1311,6 +1328,13 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 1E4EB ; ALetter # Lm NAG MUNDARI SIGN OJOD 1E5D0..1E5ED ; ALetter # Lo [30] OL ONAL LETTER O..OL ONAL LETTER EG 1E5F0 ; ALetter # Lo OL ONAL SIGN HODDOND +1E6C0..1E6DE ; ALetter # Lo [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO +1E6E0..1E6E2 ; ALetter # Lo [3] TAI YO LETTER AA..TAI YO LETTER UE +1E6E4..1E6E5 ; ALetter # Lo [2] TAI YO LETTER U..TAI YO LETTER AE +1E6E7..1E6ED ; ALetter # Lo [7] TAI YO LETTER O..TAI YO LETTER AUE +1E6F0..1E6F4 ; ALetter # Lo [5] TAI YO LETTER AN..TAI YO LETTER AP +1E6FE ; ALetter # Lo TAI YO SYMBOL MUEANG +1E6FF ; ALetter # Lm TAI YO XAM LAI 1E7E0..1E7E6 ; ALetter # Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO 1E7E8..1E7EB ; ALetter # Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE 1E7ED..1E7EE ; ALetter # Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE @@ -1355,7 +1379,7 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 1F150..1F169 ; ALetter # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z 1F170..1F189 ; ALetter # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z -# Total code points: 33791 +# Total code points: 33973 # ================================================ @@ -1467,6 +1491,7 @@ FF10..FF19 ; Numeric # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE 11C50..11C59 ; Numeric # Nd [10] BHAIKSUKI DIGIT ZERO..BHAIKSUKI DIGIT NINE 11D50..11D59 ; Numeric # Nd [10] MASARAM GONDI DIGIT ZERO..MASARAM GONDI DIGIT NINE 11DA0..11DA9 ; Numeric # Nd [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE +11DE0..11DE9 ; Numeric # Nd [10] TOLONG SIKI DIGIT ZERO..TOLONG SIKI DIGIT NINE 11F50..11F59 ; Numeric # Nd [10] KAWI DIGIT ZERO..KAWI DIGIT NINE 16130..16139 ; Numeric # Nd [10] GURUNG KHEMA DIGIT ZERO..GURUNG KHEMA DIGIT NINE 16A60..16A69 ; Numeric # Nd [10] MRO DIGIT ZERO..MRO DIGIT NINE @@ -1482,7 +1507,7 @@ FF10..FF19 ; Numeric # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE 1E950..1E959 ; Numeric # Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE 1FBF0..1FBF9 ; Numeric # Nd [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE -# Total code points: 774 +# Total code points: 784 # ================================================ diff --git a/resources/standards-data/unicode-character-database/emoji-data.txt b/resources/standards-data/unicode-character-database/emoji-data.txt index ff99028248..450252c4df 100644 --- a/resources/standards-data/unicode-character-database/emoji-data.txt +++ b/resources/standards-data/unicode-character-database/emoji-data.txt @@ -1,11 +1,11 @@ # emoji-data.txt -# Date: 2024-05-01, 21:25:24 GMT -# © 2024 Unicode®, Inc. +# Date: 2025-07-25, 17:54:31 GMT +# © 2025 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. # For terms of use and license, see https://www.unicode.org/terms_of_use.html # # Emoji Data for UTS #51 -# Used with Emoji Version 16.0 and subsequent minor revisions (if any) +# Version: 17.0 # # For documentation and usage, see https://www.unicode.org/reports/tr51 # @@ -340,6 +340,7 @@ 1F6D1..1F6D2 ; Emoji # E3.0 [2] (🛑..🛒) stop sign..shopping cart 1F6D5 ; Emoji # E12.0 [1] (🛕) hindu temple 1F6D6..1F6D7 ; Emoji # E13.0 [2] (🛖..🛗) hut..elevator +1F6D8 ; Emoji # E17.0 [1] (🛘) landslide 1F6DC ; Emoji # E15.0 [1] (🛜) wireless 1F6DD..1F6DF ; Emoji # E14.0 [3] (🛝..🛟) playground slide..ring buoy 1F6E0..1F6E5 ; Emoji # E0.7 [6] (🛠️..🛥️) hammer and wrench..motor boat @@ -408,6 +409,8 @@ 1FA83..1FA86 ; Emoji # E13.0 [4] (🪃..🪆) boomerang..nesting dolls 1FA87..1FA88 ; Emoji # E15.0 [2] (🪇..🪈) maracas..flute 1FA89 ; Emoji # E16.0 [1] (🪉) harp +1FA8A ; Emoji # E17.0 [1] (🪊) trombone +1FA8E ; Emoji # E17.0 [1] (🪎) treasure chest 1FA8F ; Emoji # E16.0 [1] (🪏) shovel 1FA90..1FA95 ; Emoji # E12.0 [6] (🪐..🪕) ringed planet..banjo 1FA96..1FAA8 ; Emoji # E13.0 [19] (🪖..🪨) military helmet..rock @@ -421,6 +424,8 @@ 1FAC0..1FAC2 ; Emoji # E13.0 [3] (🫀..🫂) anatomical heart..people hugging 1FAC3..1FAC5 ; Emoji # E14.0 [3] (🫃..🫅) pregnant man..person with crown 1FAC6 ; Emoji # E16.0 [1] (🫆) fingerprint +1FAC8 ; Emoji # E17.0 [1] (🫈) hairy creature +1FACD ; Emoji # E17.0 [1] (🫍) orca 1FACE..1FACF ; Emoji # E15.0 [2] (🫎..🫏) moose..donkey 1FAD0..1FAD6 ; Emoji # E13.0 [7] (🫐..🫖) blueberries..teapot 1FAD7..1FAD9 ; Emoji # E14.0 [3] (🫗..🫙) pouring liquid..jar @@ -430,10 +435,12 @@ 1FAE0..1FAE7 ; Emoji # E14.0 [8] (🫠..🫧) melting face..bubbles 1FAE8 ; Emoji # E15.0 [1] (🫨) shaking face 1FAE9 ; Emoji # E16.0 [1] (🫩) face with bags under eyes +1FAEA ; Emoji # E17.0 [1] (🫪) distorted face +1FAEF ; Emoji # E17.0 [1] (🫯) fight cloud 1FAF0..1FAF6 ; Emoji # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands 1FAF7..1FAF8 ; Emoji # E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand -# Total elements: 1431 +# Total elements: 1438 # ================================================ @@ -640,6 +647,7 @@ 1F6D1..1F6D2 ; Emoji_Presentation # E3.0 [2] (🛑..🛒) stop sign..shopping cart 1F6D5 ; Emoji_Presentation # E12.0 [1] (🛕) hindu temple 1F6D6..1F6D7 ; Emoji_Presentation # E13.0 [2] (🛖..🛗) hut..elevator +1F6D8 ; Emoji_Presentation # E17.0 [1] (🛘) landslide 1F6DC ; Emoji_Presentation # E15.0 [1] (🛜) wireless 1F6DD..1F6DF ; Emoji_Presentation # E14.0 [3] (🛝..🛟) playground slide..ring buoy 1F6EB..1F6EC ; Emoji_Presentation # E1.0 [2] (🛫..🛬) airplane departure..airplane arrival @@ -704,6 +712,8 @@ 1FA83..1FA86 ; Emoji_Presentation # E13.0 [4] (🪃..🪆) boomerang..nesting dolls 1FA87..1FA88 ; Emoji_Presentation # E15.0 [2] (🪇..🪈) maracas..flute 1FA89 ; Emoji_Presentation # E16.0 [1] (🪉) harp +1FA8A ; Emoji_Presentation # E17.0 [1] (🪊) trombone +1FA8E ; Emoji_Presentation # E17.0 [1] (🪎) treasure chest 1FA8F ; Emoji_Presentation # E16.0 [1] (🪏) shovel 1FA90..1FA95 ; Emoji_Presentation # E12.0 [6] (🪐..🪕) ringed planet..banjo 1FA96..1FAA8 ; Emoji_Presentation # E13.0 [19] (🪖..🪨) military helmet..rock @@ -717,6 +727,8 @@ 1FAC0..1FAC2 ; Emoji_Presentation # E13.0 [3] (🫀..🫂) anatomical heart..people hugging 1FAC3..1FAC5 ; Emoji_Presentation # E14.0 [3] (🫃..🫅) pregnant man..person with crown 1FAC6 ; Emoji_Presentation # E16.0 [1] (🫆) fingerprint +1FAC8 ; Emoji_Presentation # E17.0 [1] (🫈) hairy creature +1FACD ; Emoji_Presentation # E17.0 [1] (🫍) orca 1FACE..1FACF ; Emoji_Presentation # E15.0 [2] (🫎..🫏) moose..donkey 1FAD0..1FAD6 ; Emoji_Presentation # E13.0 [7] (🫐..🫖) blueberries..teapot 1FAD7..1FAD9 ; Emoji_Presentation # E14.0 [3] (🫗..🫙) pouring liquid..jar @@ -726,10 +738,12 @@ 1FAE0..1FAE7 ; Emoji_Presentation # E14.0 [8] (🫠..🫧) melting face..bubbles 1FAE8 ; Emoji_Presentation # E15.0 [1] (🫨) shaking face 1FAE9 ; Emoji_Presentation # E16.0 [1] (🫩) face with bags under eyes +1FAEA ; Emoji_Presentation # E17.0 [1] (🫪) distorted face +1FAEF ; Emoji_Presentation # E17.0 [1] (🫯) fight cloud 1FAF0..1FAF6 ; Emoji_Presentation # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands 1FAF7..1FAF8 ; Emoji_Presentation # E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand -# Total elements: 1212 +# Total elements: 1219 # ================================================ @@ -827,7 +841,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 21A9..21AA ; Extended_Pictographic# E0.6 [2] (↩️..↪️) right arrow curving left..left arrow curving right 231A..231B ; Extended_Pictographic# E0.6 [2] (⌚..⌛) watch..hourglass done 2328 ; Extended_Pictographic# E1.0 [1] (⌨️) keyboard -2388 ; Extended_Pictographic# E0.0 [1] (⎈) HELM SYMBOL 23CF ; Extended_Pictographic# E1.0 [1] (⏏️) eject button 23E9..23EC ; Extended_Pictographic# E0.6 [4] (⏩..⏬) fast-forward button..fast down button 23ED..23EE ; Extended_Pictographic# E0.7 [2] (⏭️..⏮️) next track button..last track button @@ -844,106 +857,63 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 2600..2601 ; Extended_Pictographic# E0.6 [2] (☀️..☁️) sun..cloud 2602..2603 ; Extended_Pictographic# E0.7 [2] (☂️..☃️) umbrella..snowman 2604 ; Extended_Pictographic# E1.0 [1] (☄️) comet -2605 ; Extended_Pictographic# E0.0 [1] (★) BLACK STAR -2607..260D ; Extended_Pictographic# E0.0 [7] (☇..☍) LIGHTNING..OPPOSITION 260E ; Extended_Pictographic# E0.6 [1] (☎️) telephone -260F..2610 ; Extended_Pictographic# E0.0 [2] (☏..☐) WHITE TELEPHONE..BALLOT BOX 2611 ; Extended_Pictographic# E0.6 [1] (☑️) check box with check -2612 ; Extended_Pictographic# E0.0 [1] (☒) BALLOT BOX WITH X 2614..2615 ; Extended_Pictographic# E0.6 [2] (☔..☕) umbrella with rain drops..hot beverage -2616..2617 ; Extended_Pictographic# E0.0 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE 2618 ; Extended_Pictographic# E1.0 [1] (☘️) shamrock -2619..261C ; Extended_Pictographic# E0.0 [4] (☙..☜) REVERSED ROTATED FLORAL HEART BULLET..WHITE LEFT POINTING INDEX 261D ; Extended_Pictographic# E0.6 [1] (☝️) index pointing up -261E..261F ; Extended_Pictographic# E0.0 [2] (☞..☟) WHITE RIGHT POINTING INDEX..WHITE DOWN POINTING INDEX 2620 ; Extended_Pictographic# E1.0 [1] (☠️) skull and crossbones -2621 ; Extended_Pictographic# E0.0 [1] (☡) CAUTION SIGN 2622..2623 ; Extended_Pictographic# E1.0 [2] (☢️..☣️) radioactive..biohazard -2624..2625 ; Extended_Pictographic# E0.0 [2] (☤..☥) CADUCEUS..ANKH 2626 ; Extended_Pictographic# E1.0 [1] (☦️) orthodox cross -2627..2629 ; Extended_Pictographic# E0.0 [3] (☧..☩) CHI RHO..CROSS OF JERUSALEM 262A ; Extended_Pictographic# E0.7 [1] (☪️) star and crescent -262B..262D ; Extended_Pictographic# E0.0 [3] (☫..☭) FARSI SYMBOL..HAMMER AND SICKLE 262E ; Extended_Pictographic# E1.0 [1] (☮️) peace symbol 262F ; Extended_Pictographic# E0.7 [1] (☯️) yin yang -2630..2637 ; Extended_Pictographic# E0.0 [8] (☰..☷) TRIGRAM FOR HEAVEN..TRIGRAM FOR EARTH 2638..2639 ; Extended_Pictographic# E0.7 [2] (☸️..☹️) wheel of dharma..frowning face 263A ; Extended_Pictographic# E0.6 [1] (☺️) smiling face -263B..263F ; Extended_Pictographic# E0.0 [5] (☻..☿) BLACK SMILING FACE..MERCURY 2640 ; Extended_Pictographic# E4.0 [1] (♀️) female sign -2641 ; Extended_Pictographic# E0.0 [1] (♁) EARTH 2642 ; Extended_Pictographic# E4.0 [1] (♂️) male sign -2643..2647 ; Extended_Pictographic# E0.0 [5] (♃..♇) JUPITER..PLUTO 2648..2653 ; Extended_Pictographic# E0.6 [12] (♈..♓) Aries..Pisces -2654..265E ; Extended_Pictographic# E0.0 [11] (♔..♞) WHITE CHESS KING..BLACK CHESS KNIGHT 265F ; Extended_Pictographic# E11.0 [1] (♟️) chess pawn 2660 ; Extended_Pictographic# E0.6 [1] (♠️) spade suit -2661..2662 ; Extended_Pictographic# E0.0 [2] (♡..♢) WHITE HEART SUIT..WHITE DIAMOND SUIT 2663 ; Extended_Pictographic# E0.6 [1] (♣️) club suit -2664 ; Extended_Pictographic# E0.0 [1] (♤) WHITE SPADE SUIT 2665..2666 ; Extended_Pictographic# E0.6 [2] (♥️..♦️) heart suit..diamond suit -2667 ; Extended_Pictographic# E0.0 [1] (♧) WHITE CLUB SUIT 2668 ; Extended_Pictographic# E0.6 [1] (♨️) hot springs -2669..267A ; Extended_Pictographic# E0.0 [18] (♩..♺) QUARTER NOTE..RECYCLING SYMBOL FOR GENERIC MATERIALS 267B ; Extended_Pictographic# E0.6 [1] (♻️) recycling symbol -267C..267D ; Extended_Pictographic# E0.0 [2] (♼..♽) RECYCLED PAPER SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL 267E ; Extended_Pictographic# E11.0 [1] (♾️) infinity 267F ; Extended_Pictographic# E0.6 [1] (♿) wheelchair symbol -2680..2685 ; Extended_Pictographic# E0.0 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6 -2690..2691 ; Extended_Pictographic# E0.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG 2692 ; Extended_Pictographic# E1.0 [1] (⚒️) hammer and pick 2693 ; Extended_Pictographic# E0.6 [1] (⚓) anchor 2694 ; Extended_Pictographic# E1.0 [1] (⚔️) crossed swords 2695 ; Extended_Pictographic# E4.0 [1] (⚕️) medical symbol 2696..2697 ; Extended_Pictographic# E1.0 [2] (⚖️..⚗️) balance scale..alembic -2698 ; Extended_Pictographic# E0.0 [1] (⚘) FLOWER 2699 ; Extended_Pictographic# E1.0 [1] (⚙️) gear -269A ; Extended_Pictographic# E0.0 [1] (⚚) STAFF OF HERMES 269B..269C ; Extended_Pictographic# E1.0 [2] (⚛️..⚜️) atom symbol..fleur-de-lis -269D..269F ; Extended_Pictographic# E0.0 [3] (⚝..⚟) OUTLINED WHITE STAR..THREE LINES CONVERGING LEFT 26A0..26A1 ; Extended_Pictographic# E0.6 [2] (⚠️..⚡) warning..high voltage -26A2..26A6 ; Extended_Pictographic# E0.0 [5] (⚢..⚦) DOUBLED FEMALE SIGN..MALE WITH STROKE SIGN 26A7 ; Extended_Pictographic# E13.0 [1] (⚧️) transgender symbol -26A8..26A9 ; Extended_Pictographic# E0.0 [2] (⚨..⚩) VERTICAL MALE WITH STROKE SIGN..HORIZONTAL MALE WITH STROKE SIGN 26AA..26AB ; Extended_Pictographic# E0.6 [2] (⚪..⚫) white circle..black circle -26AC..26AF ; Extended_Pictographic# E0.0 [4] (⚬..⚯) MEDIUM SMALL WHITE CIRCLE..UNMARRIED PARTNERSHIP SYMBOL 26B0..26B1 ; Extended_Pictographic# E1.0 [2] (⚰️..⚱️) coffin..funeral urn -26B2..26BC ; Extended_Pictographic# E0.0 [11] (⚲..⚼) NEUTER..SESQUIQUADRATE 26BD..26BE ; Extended_Pictographic# E0.6 [2] (⚽..⚾) soccer ball..baseball -26BF..26C3 ; Extended_Pictographic# E0.0 [5] (⚿..⛃) SQUARED KEY..BLACK DRAUGHTS KING 26C4..26C5 ; Extended_Pictographic# E0.6 [2] (⛄..⛅) snowman without snow..sun behind cloud -26C6..26C7 ; Extended_Pictographic# E0.0 [2] (⛆..⛇) RAIN..BLACK SNOWMAN 26C8 ; Extended_Pictographic# E0.7 [1] (⛈️) cloud with lightning and rain -26C9..26CD ; Extended_Pictographic# E0.0 [5] (⛉..⛍) TURNED WHITE SHOGI PIECE..DISABLED CAR 26CE ; Extended_Pictographic# E0.6 [1] (⛎) Ophiuchus 26CF ; Extended_Pictographic# E0.7 [1] (⛏️) pick -26D0 ; Extended_Pictographic# E0.0 [1] (⛐) CAR SLIDING 26D1 ; Extended_Pictographic# E0.7 [1] (⛑️) rescue worker’s helmet -26D2 ; Extended_Pictographic# E0.0 [1] (⛒) CIRCLED CROSSING LANES 26D3 ; Extended_Pictographic# E0.7 [1] (⛓️) chains 26D4 ; Extended_Pictographic# E0.6 [1] (⛔) no entry -26D5..26E8 ; Extended_Pictographic# E0.0 [20] (⛕..⛨) ALTERNATE ONE-WAY LEFT WAY TRAFFIC..BLACK CROSS ON SHIELD 26E9 ; Extended_Pictographic# E0.7 [1] (⛩️) shinto shrine 26EA ; Extended_Pictographic# E0.6 [1] (⛪) church -26EB..26EF ; Extended_Pictographic# E0.0 [5] (⛫..⛯) CASTLE..MAP SYMBOL FOR LIGHTHOUSE 26F0..26F1 ; Extended_Pictographic# E0.7 [2] (⛰️..⛱️) mountain..umbrella on ground 26F2..26F3 ; Extended_Pictographic# E0.6 [2] (⛲..⛳) fountain..flag in hole 26F4 ; Extended_Pictographic# E0.7 [1] (⛴️) ferry 26F5 ; Extended_Pictographic# E0.6 [1] (⛵) sailboat -26F6 ; Extended_Pictographic# E0.0 [1] (⛶) SQUARE FOUR CORNERS 26F7..26F9 ; Extended_Pictographic# E0.7 [3] (⛷️..⛹️) skier..person bouncing ball 26FA ; Extended_Pictographic# E0.6 [1] (⛺) tent -26FB..26FC ; Extended_Pictographic# E0.0 [2] (⛻..⛼) JAPANESE BANK SYMBOL..HEADSTONE GRAVEYARD SYMBOL 26FD ; Extended_Pictographic# E0.6 [1] (⛽) fuel pump -26FE..2701 ; Extended_Pictographic# E0.0 [4] (⛾..✁) CUP ON BLACK SQUARE..UPPER BLADE SCISSORS 2702 ; Extended_Pictographic# E0.6 [1] (✂️) scissors -2703..2704 ; Extended_Pictographic# E0.0 [2] (✃..✄) LOWER BLADE SCISSORS..WHITE SCISSORS 2705 ; Extended_Pictographic# E0.6 [1] (✅) check mark button 2708..270C ; Extended_Pictographic# E0.6 [5] (✈️..✌️) airplane..victory hand 270D ; Extended_Pictographic# E0.7 [1] (✍️) writing hand -270E ; Extended_Pictographic# E0.0 [1] (✎) LOWER RIGHT PENCIL 270F ; Extended_Pictographic# E0.6 [1] (✏️) pencil -2710..2711 ; Extended_Pictographic# E0.0 [2] (✐..✑) UPPER RIGHT PENCIL..WHITE NIB 2712 ; Extended_Pictographic# E0.6 [1] (✒️) black nib 2714 ; Extended_Pictographic# E0.6 [1] (✔️) check mark 2716 ; Extended_Pictographic# E0.6 [1] (✖️) multiply @@ -959,7 +929,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 2757 ; Extended_Pictographic# E0.6 [1] (❗) red exclamation mark 2763 ; Extended_Pictographic# E1.0 [1] (❣️) heart exclamation 2764 ; Extended_Pictographic# E0.6 [1] (❤️) red heart -2765..2767 ; Extended_Pictographic# E0.0 [3] (❥..❧) ROTATED HEAVY BLACK HEART BULLET..ROTATED FLORAL HEART BULLET 2795..2797 ; Extended_Pictographic# E0.6 [3] (➕..➗) plus..divide 27A1 ; Extended_Pictographic# E0.6 [1] (➡️) right arrow 27B0 ; Extended_Pictographic# E0.6 [1] (➰) curly loop @@ -973,19 +942,19 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 303D ; Extended_Pictographic# E0.6 [1] (〽️) part alternation mark 3297 ; Extended_Pictographic# E0.6 [1] (㊗️) Japanese “congratulations” button 3299 ; Extended_Pictographic# E0.6 [1] (㊙️) Japanese “secret” button -1F000..1F003 ; Extended_Pictographic# E0.0 [4] (🀀..🀃) MAHJONG TILE EAST WIND..MAHJONG TILE NORTH WIND 1F004 ; Extended_Pictographic# E0.6 [1] (🀄) mahjong red dragon -1F005..1F0CE ; Extended_Pictographic# E0.0 [202] (🀅..🃎) MAHJONG TILE GREEN DRAGON..PLAYING CARD KING OF DIAMONDS +1F02C..1F02F ; Extended_Pictographic# E0.0 [4] (🀬..🀯) .. +1F094..1F09F ; Extended_Pictographic# E0.0 [12] (🂔..🂟) .. +1F0AF..1F0B0 ; Extended_Pictographic# E0.0 [2] (🂯..🂰) .. +1F0C0 ; Extended_Pictographic# E0.0 [1] (🃀) 1F0CF ; Extended_Pictographic# E0.6 [1] (🃏) joker -1F0D0..1F0FF ; Extended_Pictographic# E0.0 [48] (🃐..🃿) .. -1F10D..1F10F ; Extended_Pictographic# E0.0 [3] (🄍..🄏) CIRCLED ZERO WITH SLASH..CIRCLED DOLLAR SIGN WITH OVERLAID BACKSLASH -1F12F ; Extended_Pictographic# E0.0 [1] (🄯) COPYLEFT SYMBOL -1F16C..1F16F ; Extended_Pictographic# E0.0 [4] (🅬..🅯) RAISED MR SIGN..CIRCLED HUMAN FIGURE +1F0D0 ; Extended_Pictographic# E0.0 [1] (🃐) +1F0F6..1F0FF ; Extended_Pictographic# E0.0 [10] (🃶..🃿) .. 1F170..1F171 ; Extended_Pictographic# E0.6 [2] (🅰️..🅱️) A button (blood type)..B button (blood type) 1F17E..1F17F ; Extended_Pictographic# E0.6 [2] (🅾️..🅿️) O button (blood type)..P button 1F18E ; Extended_Pictographic# E0.6 [1] (🆎) AB button (blood type) 1F191..1F19A ; Extended_Pictographic# E0.6 [10] (🆑..🆚) CL button..VS button -1F1AD..1F1E5 ; Extended_Pictographic# E0.0 [57] (🆭..🇥) MASK WORK SYMBOL.. +1F1AE..1F1E5 ; Extended_Pictographic# E0.0 [56] (🆮..🇥) .. 1F201..1F202 ; Extended_Pictographic# E0.6 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button 1F203..1F20F ; Extended_Pictographic# E0.0 [13] (🈃..🈏) .. 1F21A ; Extended_Pictographic# E0.6 [1] (🈚) Japanese “free of charge” button @@ -994,7 +963,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F23C..1F23F ; Extended_Pictographic# E0.0 [4] (🈼..🈿) .. 1F249..1F24F ; Extended_Pictographic# E0.0 [7] (🉉..🉏) .. 1F250..1F251 ; Extended_Pictographic# E0.6 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button -1F252..1F2FF ; Extended_Pictographic# E0.0 [174] (🉒..🋿) .. +1F252..1F25F ; Extended_Pictographic# E0.0 [14] (🉒..🉟) .. +1F266..1F2FF ; Extended_Pictographic# E0.0 [154] (🉦..🋿) .. 1F300..1F30C ; Extended_Pictographic# E0.6 [13] (🌀..🌌) cyclone..milky way 1F30D..1F30E ; Extended_Pictographic# E0.7 [2] (🌍..🌎) globe showing Europe-Africa..globe showing Americas 1F30F ; Extended_Pictographic# E0.6 [1] (🌏) globe showing Asia-Australia @@ -1010,7 +980,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F31D..1F31E ; Extended_Pictographic# E1.0 [2] (🌝..🌞) full moon face..sun with face 1F31F..1F320 ; Extended_Pictographic# E0.6 [2] (🌟..🌠) glowing star..shooting star 1F321 ; Extended_Pictographic# E0.7 [1] (🌡️) thermometer -1F322..1F323 ; Extended_Pictographic# E0.0 [2] (🌢..🌣) BLACK DROPLET..WHITE SUN 1F324..1F32C ; Extended_Pictographic# E0.7 [9] (🌤️..🌬️) sun behind small cloud..wind face 1F32D..1F32F ; Extended_Pictographic# E1.0 [3] (🌭..🌯) hot dog..burrito 1F330..1F331 ; Extended_Pictographic# E0.6 [2] (🌰..🌱) chestnut..seedling @@ -1026,11 +995,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F37D ; Extended_Pictographic# E0.7 [1] (🍽️) fork and knife with plate 1F37E..1F37F ; Extended_Pictographic# E1.0 [2] (🍾..🍿) bottle with popping cork..popcorn 1F380..1F393 ; Extended_Pictographic# E0.6 [20] (🎀..🎓) ribbon..graduation cap -1F394..1F395 ; Extended_Pictographic# E0.0 [2] (🎔..🎕) HEART WITH TIP ON THE LEFT..BOUQUET OF FLOWERS 1F396..1F397 ; Extended_Pictographic# E0.7 [2] (🎖️..🎗️) military medal..reminder ribbon -1F398 ; Extended_Pictographic# E0.0 [1] (🎘) MUSICAL KEYBOARD WITH JACKS 1F399..1F39B ; Extended_Pictographic# E0.7 [3] (🎙️..🎛️) studio microphone..control knobs -1F39C..1F39D ; Extended_Pictographic# E0.0 [2] (🎜..🎝) BEAMED ASCENDING MUSICAL NOTES..BEAMED DESCENDING MUSICAL NOTES 1F39E..1F39F ; Extended_Pictographic# E0.7 [2] (🎞️..🎟️) film frames..admission tickets 1F3A0..1F3C4 ; Extended_Pictographic# E0.6 [37] (🎠..🏄) carousel horse..person surfing 1F3C5 ; Extended_Pictographic# E1.0 [1] (🏅) sports medal @@ -1045,11 +1011,9 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F3E0..1F3E3 ; Extended_Pictographic# E0.6 [4] (🏠..🏣) house..Japanese post office 1F3E4 ; Extended_Pictographic# E1.0 [1] (🏤) post office 1F3E5..1F3F0 ; Extended_Pictographic# E0.6 [12] (🏥..🏰) hospital..castle -1F3F1..1F3F2 ; Extended_Pictographic# E0.0 [2] (🏱..🏲) WHITE PENNANT..BLACK PENNANT 1F3F3 ; Extended_Pictographic# E0.7 [1] (🏳️) white flag 1F3F4 ; Extended_Pictographic# E1.0 [1] (🏴) black flag 1F3F5 ; Extended_Pictographic# E0.7 [1] (🏵️) rosette -1F3F6 ; Extended_Pictographic# E0.0 [1] (🏶) BLACK ROSETTE 1F3F7 ; Extended_Pictographic# E0.7 [1] (🏷️) label 1F3F8..1F3FA ; Extended_Pictographic# E1.0 [3] (🏸..🏺) badminton..amphora 1F400..1F407 ; Extended_Pictographic# E1.0 [8] (🐀..🐇) rat..rabbit @@ -1086,7 +1050,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F4F8 ; Extended_Pictographic# E1.0 [1] (📸) camera with flash 1F4F9..1F4FC ; Extended_Pictographic# E0.6 [4] (📹..📼) video camera..videocassette 1F4FD ; Extended_Pictographic# E0.7 [1] (📽️) film projector -1F4FE ; Extended_Pictographic# E0.0 [1] (📾) PORTABLE STEREO 1F4FF..1F502 ; Extended_Pictographic# E1.0 [4] (📿..🔂) prayer beads..repeat single button 1F503 ; Extended_Pictographic# E0.6 [1] (🔃) clockwise vertical arrows 1F504..1F507 ; Extended_Pictographic# E1.0 [4] (🔄..🔇) counterclockwise arrows button..muted speaker @@ -1097,51 +1060,30 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F516..1F52B ; Extended_Pictographic# E0.6 [22] (🔖..🔫) bookmark..water pistol 1F52C..1F52D ; Extended_Pictographic# E1.0 [2] (🔬..🔭) microscope..telescope 1F52E..1F53D ; Extended_Pictographic# E0.6 [16] (🔮..🔽) crystal ball..downwards button -1F546..1F548 ; Extended_Pictographic# E0.0 [3] (🕆..🕈) WHITE LATIN CROSS..CELTIC CROSS 1F549..1F54A ; Extended_Pictographic# E0.7 [2] (🕉️..🕊️) om..dove 1F54B..1F54E ; Extended_Pictographic# E1.0 [4] (🕋..🕎) kaaba..menorah -1F54F ; Extended_Pictographic# E0.0 [1] (🕏) BOWL OF HYGIEIA 1F550..1F55B ; Extended_Pictographic# E0.6 [12] (🕐..🕛) one o’clock..twelve o’clock 1F55C..1F567 ; Extended_Pictographic# E0.7 [12] (🕜..🕧) one-thirty..twelve-thirty -1F568..1F56E ; Extended_Pictographic# E0.0 [7] (🕨..🕮) RIGHT SPEAKER..BOOK 1F56F..1F570 ; Extended_Pictographic# E0.7 [2] (🕯️..🕰️) candle..mantelpiece clock -1F571..1F572 ; Extended_Pictographic# E0.0 [2] (🕱..🕲) BLACK SKULL AND CROSSBONES..NO PIRACY 1F573..1F579 ; Extended_Pictographic# E0.7 [7] (🕳️..🕹️) hole..joystick 1F57A ; Extended_Pictographic# E3.0 [1] (🕺) man dancing -1F57B..1F586 ; Extended_Pictographic# E0.0 [12] (🕻..🖆) LEFT HAND TELEPHONE RECEIVER..PEN OVER STAMPED ENVELOPE 1F587 ; Extended_Pictographic# E0.7 [1] (🖇️) linked paperclips -1F588..1F589 ; Extended_Pictographic# E0.0 [2] (🖈..🖉) BLACK PUSHPIN..LOWER LEFT PENCIL 1F58A..1F58D ; Extended_Pictographic# E0.7 [4] (🖊️..🖍️) pen..crayon -1F58E..1F58F ; Extended_Pictographic# E0.0 [2] (🖎..🖏) LEFT WRITING HAND..TURNED OK HAND SIGN 1F590 ; Extended_Pictographic# E0.7 [1] (🖐️) hand with fingers splayed -1F591..1F594 ; Extended_Pictographic# E0.0 [4] (🖑..🖔) REVERSED RAISED HAND WITH FINGERS SPLAYED..REVERSED VICTORY HAND 1F595..1F596 ; Extended_Pictographic# E1.0 [2] (🖕..🖖) middle finger..vulcan salute -1F597..1F5A3 ; Extended_Pictographic# E0.0 [13] (🖗..🖣) WHITE DOWN POINTING LEFT HAND INDEX..BLACK DOWN POINTING BACKHAND INDEX 1F5A4 ; Extended_Pictographic# E3.0 [1] (🖤) black heart 1F5A5 ; Extended_Pictographic# E0.7 [1] (🖥️) desktop computer -1F5A6..1F5A7 ; Extended_Pictographic# E0.0 [2] (🖦..🖧) KEYBOARD AND MOUSE..THREE NETWORKED COMPUTERS 1F5A8 ; Extended_Pictographic# E0.7 [1] (🖨️) printer -1F5A9..1F5B0 ; Extended_Pictographic# E0.0 [8] (🖩..🖰) POCKET CALCULATOR..TWO BUTTON MOUSE 1F5B1..1F5B2 ; Extended_Pictographic# E0.7 [2] (🖱️..🖲️) computer mouse..trackball -1F5B3..1F5BB ; Extended_Pictographic# E0.0 [9] (🖳..🖻) OLD PERSONAL COMPUTER..DOCUMENT WITH PICTURE 1F5BC ; Extended_Pictographic# E0.7 [1] (🖼️) framed picture -1F5BD..1F5C1 ; Extended_Pictographic# E0.0 [5] (🖽..🗁) FRAME WITH TILES..OPEN FOLDER 1F5C2..1F5C4 ; Extended_Pictographic# E0.7 [3] (🗂️..🗄️) card index dividers..file cabinet -1F5C5..1F5D0 ; Extended_Pictographic# E0.0 [12] (🗅..🗐) EMPTY NOTE..PAGES 1F5D1..1F5D3 ; Extended_Pictographic# E0.7 [3] (🗑️..🗓️) wastebasket..spiral calendar -1F5D4..1F5DB ; Extended_Pictographic# E0.0 [8] (🗔..🗛) DESKTOP WINDOW..DECREASE FONT SIZE SYMBOL 1F5DC..1F5DE ; Extended_Pictographic# E0.7 [3] (🗜️..🗞️) clamp..rolled-up newspaper -1F5DF..1F5E0 ; Extended_Pictographic# E0.0 [2] (🗟..🗠) PAGE WITH CIRCLED TEXT..STOCK CHART 1F5E1 ; Extended_Pictographic# E0.7 [1] (🗡️) dagger -1F5E2 ; Extended_Pictographic# E0.0 [1] (🗢) LIPS 1F5E3 ; Extended_Pictographic# E0.7 [1] (🗣️) speaking head -1F5E4..1F5E7 ; Extended_Pictographic# E0.0 [4] (🗤..🗧) THREE RAYS ABOVE..THREE RAYS RIGHT 1F5E8 ; Extended_Pictographic# E2.0 [1] (🗨️) left speech bubble -1F5E9..1F5EE ; Extended_Pictographic# E0.0 [6] (🗩..🗮) RIGHT SPEECH BUBBLE..LEFT ANGER BUBBLE 1F5EF ; Extended_Pictographic# E0.7 [1] (🗯️) right anger bubble -1F5F0..1F5F2 ; Extended_Pictographic# E0.0 [3] (🗰..🗲) MOOD BUBBLE..LIGHTNING MOOD 1F5F3 ; Extended_Pictographic# E0.7 [1] (🗳️) ballot box with ballot -1F5F4..1F5F9 ; Extended_Pictographic# E0.0 [6] (🗴..🗹) BALLOT SCRIPT X..BALLOT BOX WITH BOLD CHECK 1F5FA ; Extended_Pictographic# E0.7 [1] (🗺️) world map 1F5FB..1F5FF ; Extended_Pictographic# E0.6 [5] (🗻..🗿) mount fuji..moai 1F600 ; Extended_Pictographic# E1.0 [1] (😀) grinning face @@ -1210,26 +1152,22 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F6BF ; Extended_Pictographic# E1.0 [1] (🚿) shower 1F6C0 ; Extended_Pictographic# E0.6 [1] (🛀) person taking bath 1F6C1..1F6C5 ; Extended_Pictographic# E1.0 [5] (🛁..🛅) bathtub..left luggage -1F6C6..1F6CA ; Extended_Pictographic# E0.0 [5] (🛆..🛊) TRIANGLE WITH ROUNDED CORNERS..GIRLS SYMBOL 1F6CB ; Extended_Pictographic# E0.7 [1] (🛋️) couch and lamp 1F6CC ; Extended_Pictographic# E1.0 [1] (🛌) person in bed 1F6CD..1F6CF ; Extended_Pictographic# E0.7 [3] (🛍️..🛏️) shopping bags..bed 1F6D0 ; Extended_Pictographic# E1.0 [1] (🛐) place of worship 1F6D1..1F6D2 ; Extended_Pictographic# E3.0 [2] (🛑..🛒) stop sign..shopping cart -1F6D3..1F6D4 ; Extended_Pictographic# E0.0 [2] (🛓..🛔) STUPA..PAGODA 1F6D5 ; Extended_Pictographic# E12.0 [1] (🛕) hindu temple 1F6D6..1F6D7 ; Extended_Pictographic# E13.0 [2] (🛖..🛗) hut..elevator -1F6D8..1F6DB ; Extended_Pictographic# E0.0 [4] (🛘..🛛) .. +1F6D8 ; Extended_Pictographic# E17.0 [1] (🛘) landslide +1F6D9..1F6DB ; Extended_Pictographic# E0.0 [3] (🛙..🛛) .. 1F6DC ; Extended_Pictographic# E15.0 [1] (🛜) wireless 1F6DD..1F6DF ; Extended_Pictographic# E14.0 [3] (🛝..🛟) playground slide..ring buoy 1F6E0..1F6E5 ; Extended_Pictographic# E0.7 [6] (🛠️..🛥️) hammer and wrench..motor boat -1F6E6..1F6E8 ; Extended_Pictographic# E0.0 [3] (🛦..🛨) UP-POINTING MILITARY AIRPLANE..UP-POINTING SMALL AIRPLANE 1F6E9 ; Extended_Pictographic# E0.7 [1] (🛩️) small airplane -1F6EA ; Extended_Pictographic# E0.0 [1] (🛪) NORTHEAST-POINTING AIRPLANE 1F6EB..1F6EC ; Extended_Pictographic# E1.0 [2] (🛫..🛬) airplane departure..airplane arrival 1F6ED..1F6EF ; Extended_Pictographic# E0.0 [3] (🛭..🛯) .. 1F6F0 ; Extended_Pictographic# E0.7 [1] (🛰️) satellite -1F6F1..1F6F2 ; Extended_Pictographic# E0.0 [2] (🛱..🛲) ONCOMING FIRE ENGINE..DIESEL LOCOMOTIVE 1F6F3 ; Extended_Pictographic# E0.7 [1] (🛳️) passenger ship 1F6F4..1F6F6 ; Extended_Pictographic# E3.0 [3] (🛴..🛶) kick scooter..canoe 1F6F7..1F6F8 ; Extended_Pictographic# E5.0 [2] (🛷..🛸) sled..flying saucer @@ -1237,8 +1175,7 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F6FA ; Extended_Pictographic# E12.0 [1] (🛺) auto rickshaw 1F6FB..1F6FC ; Extended_Pictographic# E13.0 [2] (🛻..🛼) pickup truck..roller skate 1F6FD..1F6FF ; Extended_Pictographic# E0.0 [3] (🛽..🛿) .. -1F774..1F77F ; Extended_Pictographic# E0.0 [12] (🝴..🝿) LOT OF FORTUNE..ORCUS -1F7D5..1F7DF ; Extended_Pictographic# E0.0 [11] (🟕..🟟) CIRCLED TRIANGLE.. +1F7DA..1F7DF ; Extended_Pictographic# E0.0 [6] (🟚..🟟) .. 1F7E0..1F7EB ; Extended_Pictographic# E12.0 [12] (🟠..🟫) orange circle..brown square 1F7EC..1F7EF ; Extended_Pictographic# E0.0 [4] (🟬..🟯) .. 1F7F0 ; Extended_Pictographic# E14.0 [1] (🟰) heavy equals sign @@ -1247,7 +1184,10 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F848..1F84F ; Extended_Pictographic# E0.0 [8] (🡈..🡏) .. 1F85A..1F85F ; Extended_Pictographic# E0.0 [6] (🡚..🡟) .. 1F888..1F88F ; Extended_Pictographic# E0.0 [8] (🢈..🢏) .. -1F8AE..1F8FF ; Extended_Pictographic# E0.0 [82] (🢮..🣿) .. +1F8AE..1F8AF ; Extended_Pictographic# E0.0 [2] (🢮..🢯) .. +1F8BC..1F8BF ; Extended_Pictographic# E0.0 [4] (🢼..🢿) .. +1F8C2..1F8CF ; Extended_Pictographic# E0.0 [14] (🣂..🣏) .. +1F8D9..1F8FF ; Extended_Pictographic# E0.0 [39] (🣙..🣿) .. 1F90C ; Extended_Pictographic# E13.0 [1] (🤌) pinched fingers 1F90D..1F90F ; Extended_Pictographic# E12.0 [3] (🤍..🤏) white heart..pinching hand 1F910..1F918 ; Extended_Pictographic# E1.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns @@ -1293,7 +1233,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F9CD..1F9CF ; Extended_Pictographic# E12.0 [3] (🧍..🧏) person standing..deaf person 1F9D0..1F9E6 ; Extended_Pictographic# E5.0 [23] (🧐..🧦) face with monocle..socks 1F9E7..1F9FF ; Extended_Pictographic# E11.0 [25] (🧧..🧿) red envelope..nazar amulet -1FA00..1FA6F ; Extended_Pictographic# E0.0 [112] (🨀..🩯) NEUTRAL CHESS KING.. +1FA58..1FA5F ; Extended_Pictographic# E0.0 [8] (🩘..🩟) .. +1FA6E..1FA6F ; Extended_Pictographic# E0.0 [2] (🩮..🩯) .. 1FA70..1FA73 ; Extended_Pictographic# E12.0 [4] (🩰..🩳) ballet shoes..shorts 1FA74 ; Extended_Pictographic# E13.0 [1] (🩴) thong sandal 1FA75..1FA77 ; Extended_Pictographic# E15.0 [3] (🩵..🩷) light blue heart..pink heart @@ -1304,7 +1245,9 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1FA83..1FA86 ; Extended_Pictographic# E13.0 [4] (🪃..🪆) boomerang..nesting dolls 1FA87..1FA88 ; Extended_Pictographic# E15.0 [2] (🪇..🪈) maracas..flute 1FA89 ; Extended_Pictographic# E16.0 [1] (🪉) harp -1FA8A..1FA8E ; Extended_Pictographic# E0.0 [5] (🪊..🪎) .. +1FA8A ; Extended_Pictographic# E17.0 [1] (🪊) trombone +1FA8B..1FA8D ; Extended_Pictographic# E0.0 [3] (🪋..🪍) .. +1FA8E ; Extended_Pictographic# E17.0 [1] (🪎) treasure chest 1FA8F ; Extended_Pictographic# E16.0 [1] (🪏) shovel 1FA90..1FA95 ; Extended_Pictographic# E12.0 [6] (🪐..🪕) ringed planet..banjo 1FA96..1FAA8 ; Extended_Pictographic# E13.0 [19] (🪖..🪨) military helmet..rock @@ -1318,7 +1261,10 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1FAC0..1FAC2 ; Extended_Pictographic# E13.0 [3] (🫀..🫂) anatomical heart..people hugging 1FAC3..1FAC5 ; Extended_Pictographic# E14.0 [3] (🫃..🫅) pregnant man..person with crown 1FAC6 ; Extended_Pictographic# E16.0 [1] (🫆) fingerprint -1FAC7..1FACD ; Extended_Pictographic# E0.0 [7] (🫇..🫍) .. +1FAC7 ; Extended_Pictographic# E0.0 [1] (🫇) +1FAC8 ; Extended_Pictographic# E17.0 [1] (🫈) hairy creature +1FAC9..1FACC ; Extended_Pictographic# E0.0 [4] (🫉..🫌) .. +1FACD ; Extended_Pictographic# E17.0 [1] (🫍) orca 1FACE..1FACF ; Extended_Pictographic# E15.0 [2] (🫎..🫏) moose..donkey 1FAD0..1FAD6 ; Extended_Pictographic# E13.0 [7] (🫐..🫖) blueberries..teapot 1FAD7..1FAD9 ; Extended_Pictographic# E14.0 [3] (🫗..🫙) pouring liquid..jar @@ -1329,12 +1275,14 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1FAE0..1FAE7 ; Extended_Pictographic# E14.0 [8] (🫠..🫧) melting face..bubbles 1FAE8 ; Extended_Pictographic# E15.0 [1] (🫨) shaking face 1FAE9 ; Extended_Pictographic# E16.0 [1] (🫩) face with bags under eyes -1FAEA..1FAEF ; Extended_Pictographic# E0.0 [6] (🫪..🫯) .. +1FAEA ; Extended_Pictographic# E17.0 [1] (🫪) distorted face +1FAEB..1FAEE ; Extended_Pictographic# E0.0 [4] (🫫..🫮) .. +1FAEF ; Extended_Pictographic# E17.0 [1] (🫯) fight cloud 1FAF0..1FAF6 ; Extended_Pictographic# E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands 1FAF7..1FAF8 ; Extended_Pictographic# E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand 1FAF9..1FAFF ; Extended_Pictographic# E0.0 [7] (🫹..🫿) .. 1FC00..1FFFD ; Extended_Pictographic# E0.0[1022] (🰀..🿽) .. -# Total elements: 3537 +# Total elements: 2848 #EOF diff --git a/resources/teamcity/ios/keyman-ios-test.sh b/resources/teamcity/ios/keyman-ios-test.sh index 1c6f156238..63c48cc7dc 100755 --- a/resources/teamcity/ios/keyman-ios-test.sh +++ b/resources/teamcity/ios/keyman-ios-test.sh @@ -52,16 +52,14 @@ function __do_upload_to_testflight() { local FASTLANE_ITC_PROVIDER="$1" local FASTLANE_API_KEY_ID="$2" local APP_IPA_DST - local BUILD_NUMBER export FASTLANE_ITC_TEAM_ID="$3" export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 - BUILD_NUMBER=$(cat ../VERSION.md) - APP_IPA_DST="$4-ios-${BUILD_NUMBER}.ipa" + APP_IPA_DST="$4-ios-${KEYMAN_VERSION_FOR_FILENAME}.ipa" - cd "upload/${BUILD_NUMBER}" + cd "upload/${KEYMAN_VERSION}" command -v fastlane # shellcheck disable=SC2154 fastlane pilot upload \ diff --git a/resources/teamcity/linux/keyman-linux-test.sh b/resources/teamcity/linux/keyman-linux-test.sh index 950750b255..d89a57a75e 100755 --- a/resources/teamcity/linux/keyman-linux-test.sh +++ b/resources/teamcity/linux/keyman-linux-test.sh @@ -33,7 +33,7 @@ function make_source_tarball_action() { if builder_is_ci_build_level_release; then builder_echo start "make source tarball" "Make source tarball" rm -rf dist - make tmpsources + PKG_CONFIG_PATH="${KEYMAN_ROOT}/core/build/arch/release/meson-private" "${KEYMAN_ROOT}/linux/scripts/dist.sh" builder_echo end "make source tarball" success "Finished making source tarball" else builder_echo "Skipping source tarball creation - not release build level" diff --git a/resources/teamcity/triggers/trigger-test-builds.sh b/resources/teamcity/triggers/trigger-test-builds.sh index 635f133687..cbd317ec8a 100755 --- a/resources/teamcity/triggers/trigger-test-builds.sh +++ b/resources/teamcity/triggers/trigger-test-builds.sh @@ -12,6 +12,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" . "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE +# shellcheck disable=SC2154 . "${KEYMAN_ROOT}/resources/build/ci/trigger-definitions.inc.sh" . "${KEYMAN_ROOT}/resources/build/ci/trigger-builds.inc.sh" . "${KEYMAN_ROOT}/resources/build/ci/trigger-build-bot.inc.sh" @@ -29,15 +30,15 @@ if ! builder_has_option --branch; then builder_die "--branch parameter is required" fi -if [[ ! "$PRNUM" =~ ^([0-9]+|master|beta|stable-[0-9]+\.[0-9]+)$ ]]; then - builder_die "Invalid parameter --branch '$PRNUM'; expected 'master', 'beta', 'stable-x.y', or PR number" +if [[ ! "${PRNUM}" =~ ^([0-9]+|master|beta|stable-[0-9]+\.[0-9]+)$ ]]; then + builder_die "Invalid parameter --branch '${PRNUM}'; expected 'master', 'beta', 'stable-x.y', or PR number" fi # # Debug logging # function debug_echo() { - if [ ! -z ${DEBUG:-} ]; then + if [[ ! -z ${DEBUG:-} ]]; then echo "DEBUG: $1" fi true @@ -59,44 +60,45 @@ function triggerTestBuilds() { # Cancel any already running builds for this branch if builder_has_option --dry-run; then - builder_echo "DRY RUN: cancel current builds for $branch" + builder_echo "DRY RUN: cancel current builds for ${branch}" else - node "$KEYMAN_ROOT/resources/build/ci/cancel-builds/cancel-test-builds.mjs" "$branch" "$TEAMCITY_TOKEN" + node "${KEYMAN_ROOT}/resources/build/ci/cancel-builds/cancel-test-builds.mjs" "${branch}" "${TEAMCITY_TOKEN}" fi for platform in "${!platforms[@]}"; do - local platformBuildLevel=${platforms[$platform]} - if [[ $platformBuildLevel == skip ]]; then - builder_echo heading "$platform: skipping build" + local platformBuildLevel=${platforms[${platform}]} + if [[ ${platformBuildLevel} == skip ]]; then + builder_echo heading "${platform}: skipping build" continue fi - builder_echo heading "$platform: checking for git changes" - eval test_builds='(${'bc_test_$platform'[@]})' + builder_echo heading "${platform}: checking for git changes" + eval test_builds='(${'bc_test_${platform}'[@]})' + # shellcheck disable=SC2154 for test_build in "${test_builds[@]}"; do - if [[ $test_build == "" ]]; then continue; fi + if [[ ${test_build} == "" ]]; then continue; fi found_build=true - if [ "${test_build:(-7)}" == "_GitHub" ]; then + if [[ "${test_build:(-7)}" == "_GitHub" ]]; then local job=${test_build%_GitHub} if builder_has_option --dry-run; then - builder_echo "DRY RUN: Triggering GitHub action build $job/$branch, level = $platformBuildLevel" + builder_echo "DRY RUN: Triggering GitHub action build ${job}/${branch}, level = ${platformBuildLevel}" else - builder_echo "Triggering GitHub action build $job/$branch, level = $platformBuildLevel" - triggerGitHubActionsBuild true $platformBuildLevel "$job" "$branch" + builder_echo "Triggering GitHub action build ${job}/${branch}, level = ${platformBuildLevel}" + triggerGitHubActionsBuild true "${platformBuildLevel}" "${job}" "${branch}" "${KEYMAN_API_CHECK_SKIP}" fi else if builder_has_option --dry-run; then - builder_echo "DRY RUN: Triggering build configuration $test_build on teamcity for $branch, level = $platformBuildLevel" + builder_echo "DRY RUN: Triggering build configuration ${test_build} on teamcity for ${branch}, level = ${platformBuildLevel}" else - builder_echo "Triggering build configuration $test_build on teamcity for $branch, level = $platformBuildLevel" - triggerTeamCityBuild true $platformBuildLevel "$test_build" "$vcs_test" "$branch" + builder_echo "Triggering build configuration ${test_build} on teamcity for ${branch}, level = ${platformBuildLevel}" + triggerTeamCityBuild true "${platformBuildLevel}" "${test_build}" "${vcs_test}" "${branch}" fi fi done done - if [[ $found_build == false ]]; then + if [[ ${found_build} == false ]]; then postSkippedBuildsStatusResult fi } @@ -111,7 +113,7 @@ function postSkippedBuildsStatusResult() { curl --silent --write-out '\n' \ --request POST \ --header "Accept: application/vnd.github+json" \ - --header "Authorization: token $GITHUB_TOKEN" \ + --header "Authorization: token ${GITHUB_TOKEN}" \ --data '{"state":"success","description":"Skipping since no platform builds necessary","context":"Test Build (Keyman)"}' \ "https://api.github.com/repos/keymanapp/keyman/statuses/${BUILD_VCS_NUMBER}" fi @@ -122,10 +124,10 @@ function postSkippedBuildsStatusResult() { # branches as well as pull requests # -if [[ ! "$PRNUM" =~ ^[[:digit:]]+$ ]]; then +if [[ ! "${PRNUM}" =~ ^[[:digit:]]+$ ]]; then # branch name is 'master', 'beta', or 'stable-x.y' - builder_echo "Branch $PRNUM needs to pass tests on all platforms." - triggerTestBuilds main_branch_platform_build_levels $PRNUM + builder_echo "Branch ${PRNUM} needs to pass tests on all platforms." + triggerTestBuilds main_branch_platform_build_levels "${PRNUM}" exit 0 fi @@ -136,20 +138,20 @@ fi # targeted by the PR other than to ask GitHub, anyway.) # -builder_echo grey "# Get information about pull request #$PRNUM from GitHub" -prinfo=`curl -s -H "User-Agent: @keymanapp" -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/keymanapp/keyman/pulls/$PRNUM` -prbase=`echo ${prinfo} | "$JQ" -r '.base.ref'` -prhead=`echo ${prinfo} | "$JQ" -r '.head.ref'` -prbaserepo=`echo ${prinfo} | "$JQ" -r '.base.repo.html_url'` -prheadrepo=`echo ${prinfo} | "$JQ" -r '.head.repo.html_url'` +builder_echo grey "# Get information about pull request #${PRNUM} from GitHub" +prinfo=$(curl -s -H "User-Agent: @keymanapp" -H "Authorization: Bearer ${GITHUB_TOKEN}" "https://api.github.com/repos/keymanapp/keyman/pulls/${PRNUM}") +prbase=$(echo "${prinfo}" | "${JQ}" -r '.base.ref') +prhead=$(echo "${prinfo}" | "${JQ}" -r '.head.ref') +prbaserepo=$(echo "${prinfo}" | "${JQ}" -r '.base.repo.html_url') +prheadrepo=$(echo "${prinfo}" | "${JQ}" -r '.head.repo.html_url') -debug_echo "PRNUM: $PRNUM" -debug_echo "BASE: $prbase ($prbaserepo)" -debug_echo "HEAD: $prhead ($prheadrepo)" +debug_echo "PRNUM: ${PRNUM}" +debug_echo "BASE: ${prbase} (${prbaserepo})" +debug_echo "HEAD: ${prhead} (${prheadrepo})" prremote=origin -if [ "$prbaserepo" != "https://github.com/keymanapp/keyman" ]; then - echo "Unknown base repository $prbaserepo." +if [[ "${prbaserepo}" != "https://github.com/keymanapp/keyman" ]]; then + echo "Unknown base repository ${prbaserepo}." exit 1 fi @@ -160,7 +162,7 @@ fi # (Ensure we are within the repository before running git calls) builder_echo grey "# Ensure our local branch is up to date" -pushd "$KEYMAN_ROOT" > /dev/null +pushd "${KEYMAN_ROOT}" > /dev/null git fetch origin > /dev/null # @@ -169,10 +171,10 @@ git fetch origin > /dev/null # on untrusted forks. # -if [ "$prheadrepo" != "https://github.com/keymanapp/keyman" ]; then +if [[ "${prheadrepo}" != "https://github.com/keymanapp/keyman" ]]; then prremote=external-rrtb - git remote add "$prremote" "$prheadrepo" - git fetch "$prremote" > /dev/null + git remote add "${prremote}" "${prheadrepo}" + git fetch "${prremote}" > /dev/null fi # @@ -181,18 +183,18 @@ fi # builder_echo grey "# Get list of changed files in the pull request" -prfiles=`git diff --name-only "origin/$prbase"..."$prremote/$prhead" || ( if [ $? == 128 ]; then echo abort; else exit $?; fi )` -if [ "$prfiles" == "abort" ]; then +prfiles=$(git diff --name-only "origin/${prbase}...${prremote}/${prhead}" || ( if [[ $? == 128 ]]; then echo abort; else exit $?; fi )) +if [[ "${prfiles}" == "abort" ]]; then # Don't trigger any builds, exit with success - if [ "$prremote" != "origin" ]; then - git remote remove "$prremote" + if [[ "${prremote}" != "origin" ]]; then + git remote remove "${prremote}" fi - echo "Remote branch origin/$prhead has gone away; probably an automatic pull request. Skipping build." + echo "Remote branch origin/${prhead} has gone away; probably an automatic pull request. Skipping build." exit 0 fi -if [ "$prremote" != "origin" ]; then - git remote remove "$prremote" +if [[ "${prremote}" != "origin" ]]; then + git remote remove "${prremote}" fi debug_echo "Files found: ${prfiles[*]}" @@ -212,23 +214,23 @@ function find_platform_changes() { while IFS= read -r line; do # for each platform for platform in "${available_platforms[@]}"; do - if [[ ! " ${!build_platforms[@]} " =~ " $platform " ]]; then + if [[ ! " ${!build_platforms[*]} " =~ " ${platform} " ]]; then # Which platform are we watching? - eval watch='$'watch_$platform + eval watch="'$'watch_${platform}" # Add common patterns to the watch list - watch="^(${platform}|(oem/[^/]+/${platform})|resources/((?!teamcity)|teamcity/(${platform}|includes))|${watch})" + watch="^(${platform}|(oem/[^/]+/${platform})|resources/((?!teamcity|docker-images)|teamcity/(${platform}|includes)|docker-images/(${platform}|base))|${watch})" # Since bash doesn't support negative look-aheads we use perl to test # (grep has a --perl-regexp option, but not the version on macOS) if perl -e 'exit($ARGV[0] =~ /$ARGV[1]/ ? 0 : 1)' "${line}" "${watch}"; then # By default, we'll build a 'release' test build for touched platforms - build_platforms[$platform]=$build_level_release + build_platforms[${platform}]=${build_level_release} fi fi done - done <<< "$prfiles" + done <<< "${prfiles}" - builder_echo blue "Default build platforms: ${!build_platforms[@]}" + builder_echo blue "Default build platforms: ${!build_platforms[*]}" } find_platform_changes @@ -238,7 +240,9 @@ find_platform_changes # This will modify the build_platforms array # -if [ "$prremote" == "origin" ]; then +KEYMAN_API_CHECK_SKIP="false" + +if [[ "${prremote}" == "origin" ]]; then # We only accept Build-bot commands on trusted local origin PRs cd "${KEYMAN_ROOT}/resources/build/ci/github" npm ci @@ -249,9 +253,10 @@ if [ "$prremote" == "origin" ]; then NODE_WRAPPER=node fi # Note: we may move all of this into javascript at some point! - prcommits=$(${NODE_WRAPPER} api-pull-commits.mjs $GITHUB_TOKEN $PRNUM) - test_bot_check_pr_body "$PRNUM" "$prinfo" - build_bot_check_messages $PRNUM "$prinfo" "$prcommits" + prcommits=$(${NODE_WRAPPER} api-pull-commits.mjs "${GITHUB_TOKEN}" "${PRNUM}") + test_bot_check_pr_body "${PRNUM}" "${prinfo}" + build_bot_check_messages "${PRNUM}" "${prinfo}" "${prcommits}" + KEYMAN_API_CHECK_SKIP="$(is_skip_keyman_api_check "${PRNUM}" "${prinfo}")" fi # @@ -260,7 +265,7 @@ fi if [[ ! -z "${build_platforms[*]:-}" ]] && (( ${#build_platforms[@]} > 0)); then builder_echo heading "Start test builds" - triggerTestBuilds build_platforms $PRNUM + triggerTestBuilds build_platforms "${PRNUM}" else builder_echo heading "No builds to start" postSkippedBuildsStatusResult diff --git a/web/docs/engine/index.md b/web/docs/engine/index.md index 5515112f88..377b1ae66f 100644 --- a/web/docs/engine/index.md +++ b/web/docs/engine/index.md @@ -20,7 +20,7 @@ computer browsers. -[What's New](whats-new) +[What's New](whatsnew) : What's new in Keyman Engine for Web 19.0 diff --git a/web/docs/engine/whats-new.md b/web/docs/engine/whats-new.md deleted file mode 100644 index e220fb9c13..0000000000 --- a/web/docs/engine/whats-new.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: What's New in KeymanWeb 19.0 ---- diff --git a/web/docs/engine/whatsnew.md b/web/docs/engine/whatsnew.md new file mode 100644 index 0000000000..8828f3b81f --- /dev/null +++ b/web/docs/engine/whatsnew.md @@ -0,0 +1,7 @@ +--- +title: What's New in KeymanWeb 19.0 +--- + +## See also + +* [Keyman Engine for Web Documentation](index) diff --git a/web/src/engine/osk/src/visualKeyboard.ts b/web/src/engine/osk/src/visualKeyboard.ts index 1e9f88af03..ffded2c033 100644 --- a/web/src/engine/osk/src/visualKeyboard.ts +++ b/web/src/engine/osk/src/visualKeyboard.ts @@ -797,6 +797,7 @@ export default class VisualKeyboard extends EventEmitter implements Ke if (this.usesFixedWidthScaling) { let baseWidth = this.width; baseWidth -= this._borderWidth * 2; + baseWidth = (isNaN(baseWidth) || baseWidth < 0) ? 0 : baseWidth; return ParsedLengthStyle.inPixels(baseWidth); } else { return ParsedLengthStyle.forScalar(1); @@ -821,7 +822,9 @@ export default class VisualKeyboard extends EventEmitter implements Ke // Touch OSKs may apply internal padding to prevent row cropping at the edges. // ... why not precompute both, rather than recalculate each time? // - appears to contribute to layout reflow costs on layer swaps! - return ParsedLengthStyle.inPixels(this.layoutHeight.val - this._borderWidth * 2 - this.layerGroup.verticalPadding); + let baseHeight = this.layoutHeight.val - this._borderWidth * 2 - this.layerGroup.verticalPadding; + baseHeight = (isNaN(baseHeight) || baseHeight < 0) ? 0 : baseHeight; + return ParsedLengthStyle.inPixels(baseHeight); } else { return ParsedLengthStyle.forScalar(1); } diff --git a/web/src/engine/predictive-text/worker-thread/src/main/correction/classical-calculation.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/classical-calculation.ts index 6964673fad..7975f5eb8b 100644 --- a/web/src/engine/predictive-text/worker-thread/src/main/correction/classical-calculation.ts +++ b/web/src/engine/predictive-text/worker-thread/src/main/correction/classical-calculation.ts @@ -555,7 +555,6 @@ export class ClassicalDistanceCalculation< return trimmedInstance; } - // Inputs add an extra row / first index entry. // Inputs add an extra row / first index entry. addInputChar(token: TUnit): this { const returnBuffer = new ClassicalDistanceCalculation(this); @@ -583,7 +582,7 @@ export class ClassicalDistanceCalculation< } addMatchChar(token: TUnit): this { - let returnBuffer = new ClassicalDistanceCalculation(this); + const returnBuffer = new ClassicalDistanceCalculation(this); returnBuffer._addMatchChar(token); return returnBuffer as this; } diff --git a/web/src/test/auto/headless/engine/osk/visualKeyboard.tests.ts b/web/src/test/auto/headless/engine/osk/visualKeyboard.tests.ts new file mode 100644 index 0000000000..991c4be98b --- /dev/null +++ b/web/src/test/auto/headless/engine/osk/visualKeyboard.tests.ts @@ -0,0 +1,241 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ +import { assert } from 'chai'; +import sinon from 'sinon'; +// @ts-ignore // Type info unavailable; doing a npm-install for it breaks things much worse in other ways. +import { JSDOM } from 'jsdom'; + +import { VisualKeyboard } from 'keyman/engine/osk'; +import { ActiveLayout, Keyboard, KeyboardProperties } from 'keyman/engine/keyboard'; +import { StylesheetManager } from 'keyman/engine/dom-utils'; +import { DeviceSpec } from "@keymanapp/web-utils"; + +describe('VisualKeyboard', () => { + let dom: JSDOM; + let mockConfig: any; + + beforeEach(() => { + dom = new JSDOM('

Hello world

'); + global.document = dom.window.document; + global.getComputedStyle = dom.window.getComputedStyle; + + const mockDevice = { browser: DeviceSpec.Browser.Chrome, formFactor: DeviceSpec.FormFactor.Desktop, OS: DeviceSpec.OperatingSystem.Windows, touchable: false }; + const mockLayout = sinon.createStubInstance(ActiveLayout) as any; + + const keyboard = sinon.createStubInstance(Keyboard) as any; + keyboard.layout.returns(mockLayout); + sinon.stub(keyboard, 'isRTL').get(() => false); + sinon.stub(keyboard, 'id').get(() => 'test'); + sinon.stub(keyboard, 'name').get(() => 'Test Keyboard'); + + mockConfig = { + device: mockDevice, + hostDevice: mockDevice, + pathConfig: { fonts: '', resources: '' }, + keyboard: keyboard, + keyboardMetadata: sinon.createStubInstance(KeyboardProperties), + topContainer: document.createElement('div'), + styleSheetManager: sinon.createStubInstance(StylesheetManager), + }; + }); + + function createVisualKeyboard(fixedScaling: boolean = true): VisualKeyboard { + const vk = new VisualKeyboard(mockConfig); + vk.usesFixedHeightScaling = fixedScaling; + vk.usesFixedWidthScaling = fixedScaling; + vk['_borderWidth'] = 3; + return vk; + } + + describe('LayoutHeight', () => { + it('calculates LayoutHeight correctly for number', () => { + const vk = createVisualKeyboard(); + vk['_height'] = 100; + assert.equal(vk.layoutHeight.val, 94); + }); + + it('calculates LayoutHeight correctly for number string', () => { + const vk = createVisualKeyboard(); + vk['_height'] = "100"; + assert.equal(vk.layoutHeight.val, 94); + }); + + it('accepts small heights for LayoutHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = "5"; + assert.equal(vk.layoutHeight.val, 0); + }); + + it('ignores non-number string for LayoutHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = "100px"; + assert.equal(vk.layoutHeight.val, 0); + }); + + it('ignores invalid string for LayoutHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = "invalid"; + assert.equal(vk.layoutHeight.val, 0); + }); + + it('ignores undefined value for LayoutHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = undefined; + assert.equal(vk.layoutHeight.val, 0); + }); + + it('ignores null value for LayoutHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = null; + assert.equal(vk.layoutHeight.val, 0); + }); + + it('calculates LayoutHeight correctly with fixed scaling', () => { + const vk = createVisualKeyboard(true); + vk['_height'] = 100; + assert.equal(vk.layoutHeight.val, 94); + }); + + it('calculates LayoutHeight correctly with fixed scaling and string', () => { + const vk = createVisualKeyboard(true); + vk['_height'] = "100px"; + assert.equal(vk.layoutHeight.val, 0); + }); + + it('ignores invalid borderWidth for LayoutHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = 100; + vk['_borderWidth'] = "3px"; + assert.equal(vk.layoutHeight.val, 0); + }); + }); + + describe('LayoutWidth', () => { + it('calculates LayoutWidth correctly for number', () => { + const vk = createVisualKeyboard(); + vk['_width'] = 100; + assert.equal(vk.layoutWidth.val, 94); + }); + + it('calculates LayoutWidth correctly for number string', () => { + const vk = createVisualKeyboard(); + vk['_width'] = "100"; + assert.equal(vk.layoutWidth.val, 94); + }); + + it('accepts small heights for LayoutWidth', () => { + const vk = createVisualKeyboard(); + vk['_width'] = "5"; + assert.equal(vk.layoutWidth.val, 0); + }); + + it('ignores non-number string for LayoutWidth', () => { + const vk = createVisualKeyboard(); + vk['_width'] = "100px"; + assert.equal(vk.layoutWidth.val, 0); + }); + + it('ignores invalid string for LayoutWidth', () => { + const vk = createVisualKeyboard(); + vk['_width'] = "invalid"; + assert.equal(vk.layoutWidth.val, 0); + }); + + it('ignores undefined value for LayoutWidth', () => { + const vk = createVisualKeyboard(); + vk['_width'] = undefined; + assert.equal(vk.layoutWidth.val, 0); + }); + + it('ignores null value for LayoutWidth', () => { + const vk = createVisualKeyboard(); + vk['_width'] = null; + assert.equal(vk.layoutWidth.val, 0); + }); + + it('calculates LayoutWidth correctly with fixed scaling', () => { + const vk = createVisualKeyboard(true); + vk['_width'] = 100; + assert.equal(vk.layoutWidth.val, 94); + }); + + it('calculates LayoutWidth correctly with fixed scaling and string', () => { + const vk = createVisualKeyboard(true); + vk['_width'] = "100px"; + assert.equal(vk.layoutWidth.val, 0); + }); + + it('ignores invalid borderWidth for LayoutWidth', () => { + const vk = createVisualKeyboard(); + vk['_width'] = 100; + vk['_borderWidth'] = "3px"; + assert.equal(vk.layoutWidth.val, 0); + }); + + }); + + describe('InternalHeight', () => { + it('calculates InternalHeight correctly for number', () => { + const vk = createVisualKeyboard(); + vk['_height'] = 100; + assert.equal(vk.internalHeight.val, 88); + }); + + it('calculates InternalHeight correctly for number string', () => { + const vk = createVisualKeyboard(); + vk['_height'] = "100"; + assert.equal(vk.internalHeight.val, 88); + }); + + it('accepts small heights for InternalHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = "5"; + assert.equal(vk.internalHeight.val, 0); + }); + + it('ignores non-number string for InternalHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = "100px"; + assert.equal(vk.internalHeight.val, 0); + }); + + it('ignores invalid string for InternalHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = "invalid"; + assert.equal(vk.internalHeight.val, 0); + }); + + it('ignores undefined value for InternalHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = undefined; + assert.equal(vk.internalHeight.val, 0); + }); + + it('ignores null value for InternalHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = null; + assert.equal(vk.internalHeight.val, 0); + }); + + it('calculates InternalHeight correctly with fixed scaling', () => { + const vk = createVisualKeyboard(true); + vk['_height'] = 100; + assert.equal(vk.internalHeight.val, 88); + }); + + it('calculates InternalHeight correctly with fixed scaling and string', () => { + const vk = createVisualKeyboard(true); + vk['_height'] = "100px"; + assert.equal(vk.internalHeight.val, 0); + }); + + it('ignores invalid borderWidth for InternalHeight', () => { + const vk = createVisualKeyboard(); + vk['_height'] = 100; + vk['_borderWidth'] = "3px"; + assert.equal(vk.internalHeight.val, 0); + }); + + }); +}); diff --git a/web/src/test/auto/headless/engine/predictive-text/worker-thread/context/alignment-helpers.tests.ts b/web/src/test/auto/headless/engine/predictive-text/worker-thread/context/alignment-helpers.tests.ts index d25528a7fe..4872d31766 100644 --- a/web/src/test/auto/headless/engine/predictive-text/worker-thread/context/alignment-helpers.tests.ts +++ b/web/src/test/auto/headless/engine/predictive-text/worker-thread/context/alignment-helpers.tests.ts @@ -188,8 +188,8 @@ describe('computeAlignment', () => { canAlign: true, editPath: [ {op: 'substitute', input: 0, match: 0}, - {op: 'insert', match: 1}, - {op: 'insert', match: 2} + {op: 'insert', match: 1}, + {op: 'insert', match: 2} ], leadTokenShift: 0, leadEditLength: 0, @@ -347,7 +347,7 @@ describe('computeAlignment', () => { assert.deepEqual(computedAlignment, { canAlign: true, editPath: [ - {op: 'insert', match: 0}, + {op: 'insert', match: 0}, {op: 'match', input: 0, match: 1}, {op: 'match', input: 1, match: 2}, {op: 'match', input: 2, match: 3}, @@ -399,7 +399,7 @@ describe('computeAlignment', () => { assert.deepEqual(computedAlignment, { canAlign: true, editPath: [ - {op: 'insert', match: 0}, + {op: 'insert', match: 0}, {op: 'substitute', input: 0, match: 1}, {op: 'match', input: 1, match: 2}, {op: 'match', input: 2, match: 3}, @@ -430,7 +430,7 @@ describe('computeAlignment', () => { {op: 'match', input: 2, match: 1}, {op: 'match', input: 3, match: 2}, {op: 'match', input: 4, match: 3}, - {op: 'insert', match: 4} + {op: 'insert', match: 4} ], leadTokenShift: -1, leadEditLength: 0, @@ -483,7 +483,7 @@ describe('computeAlignment', () => { {op: 'match', input: 2, match: 2}, {op: 'match', input: 3, match: 3}, {op: 'substitute', input: 4, match: 4}, - {op: 'insert', match: 5} + {op: 'insert', match: 5} ], leadTokenShift: 0, leadEditLength: 1, @@ -505,7 +505,7 @@ describe('computeAlignment', () => { assert.deepEqual(computedAlignment, { canAlign: true, editPath: [ - {op: 'insert', match: 0}, + {op: 'insert', match: 0}, {op: 'match', input: 0, match: 1}, {op: 'match', input: 1, match: 2}, {op: 'match', input: 2, match: 3}, @@ -532,7 +532,7 @@ describe('computeAlignment', () => { assert.deepEqual(computedAlignment, { canAlign: true, editPath: [ - {op: 'insert', match: 0}, + {op: 'insert', match: 0}, {op: 'match', input: 0, match: 1}, {op: 'match', input: 1, match: 2}, {op: 'match', input: 2, match: 3}, @@ -559,7 +559,7 @@ describe('computeAlignment', () => { assert.deepEqual(computedAlignment, { canAlign: true, editPath: [ - {op: 'insert', match: 0}, + {op: 'insert', match: 0}, {op: 'match', input: 0, match: 1}, {op: 'match', input: 1, match: 2}, {op: 'match', input: 2, match: 3}, @@ -608,7 +608,7 @@ describe('computeAlignment', () => { canAlign: false, editPath: [ {op: 'match', input: 0, match: 0}, - {op: 'insert', match: 1}, + {op: 'insert', match: 1}, {op: 'match', input: 1, match: 2}, {op: 'match', input: 2, match: 3}, {op: 'match', input: 3, match: 4} @@ -653,7 +653,7 @@ describe('computeAlignment', () => { {op: 'match', input: 1, match: 1}, {op: 'match', input: 2, match: 2}, {op: 'match', input: 3, match: 3}, - {op: 'insert', match: 4}, + {op: 'insert', match: 4}, {op: 'match', input: 4, match: 5} ] }); @@ -715,8 +715,8 @@ describe('computeAlignment', () => { {op: 'match', input: 8, match: 8}, {op: 'match', input: 9, match: 9}, {op: 'substitute', input: 10, match: 10}, - {op: 'insert', match: 11}, - {op: 'insert', match: 12} + {op: 'insert', match: 11}, + {op: 'insert', match: 12} ], leadTokenShift: 0, leadEditLength: 0, diff --git a/web/src/test/auto/headless/engine/predictive-text/worker-thread/context/context-tokenization.tests.ts b/web/src/test/auto/headless/engine/predictive-text/worker-thread/context/context-tokenization.tests.ts index 6a79cae8ce..e88ac2ab28 100644 --- a/web/src/test/auto/headless/engine/predictive-text/worker-thread/context/context-tokenization.tests.ts +++ b/web/src/test/auto/headless/engine/predictive-text/worker-thread/context/context-tokenization.tests.ts @@ -138,8 +138,8 @@ describe('ContextTokenization', function() { {op: 'match', input: 4, match: 4}, {op: 'match', input: 5, match: 5}, {op: 'match', input: 6, match: 6}, - {op: 'insert', match: 7}, - {op: 'insert', match: 8} + {op: 'insert', match: 7}, + {op: 'insert', match: 8} ], leadTokenShift: 0, leadEditLength: 0, @@ -436,8 +436,8 @@ describe('ContextTokenization', function() { targetTokens, { canAlign: true, editPath: [ - {op: 'insert', match: 0}, - {op: 'insert', match: 1}, + {op: 'insert', match: 0}, + {op: 'insert', match: 1}, {op: 'match', input: 0, match: 2}, {op: 'match', input: 1, match: 3}, {op: 'match', input: 2, match: 4}, @@ -614,8 +614,8 @@ describe('ContextTokenization', function() { {op: 'match', input: 10, match: 9}, {op: 'match', input: 11, match: 10}, {op: 'match', input: 12, match: 11}, - {op: 'insert', match: 12}, - {op: 'insert', match: 13} + {op: 'insert', match: 12}, + {op: 'insert', match: 13} ], leadTokenShift: 1, // "'", leadEditLength: 1, // "n't" / "t" diff --git a/web/src/test/auto/headless/engine/predictive-text/worker-thread/correction-search/classical-calculation.tests.ts b/web/src/test/auto/headless/engine/predictive-text/worker-thread/correction-search/classical-calculation.tests.ts index c61f4d89b9..5f53de43ea 100644 --- a/web/src/test/auto/headless/engine/predictive-text/worker-thread/correction-search/classical-calculation.tests.ts +++ b/web/src/test/auto/headless/engine/predictive-text/worker-thread/correction-search/classical-calculation.tests.ts @@ -592,7 +592,7 @@ describe('Classical Damerau-Levenshtein edit-distance calculation', function() { { op: 'match', input: 1 /* 'c' */, match: 1 /* 'c' */}, { op: 'match', input: 2 /* 'c' */, match: 2 /* 'c' */}, { op: 'match', input: 3 /* 'o' */, match: 3 /* 'o' */}, - { op: 'insert', match: 4 /* 'm' */}, // insert is earlier + { op: 'insert', match: 4 /* 'm' */}, // insert is earlier { op: 'match', input: 4 /* 'm' */, match: 5 /* 'm' */}, // longer contiguous match sequence { op: 'match', input: 5 /* 'o' */, match: 6 /* 'o' */}, { op: 'match', input: 6 /* 'd' */, match: 7 /* 'd' */}, @@ -605,7 +605,7 @@ describe('Classical Damerau-Levenshtein edit-distance calculation', function() { { op: 'match', input: 2 /* 'c' */, match: 2 /* 'c' */}, { op: 'match', input: 3 /* 'o' */, match: 3 /* 'o' */}, { op: 'match', input: 4 /* 'm' */, match: 4 /* 'm' */}, - { op: 'insert', match: 5 /* 'm' */}, // now inserting later. + { op: 'insert', match: 5 /* 'm' */}, // now inserting later. { op: 'match', input: 5 /* 'o' */, match: 6 /* 'o' */}, { op: 'match', input: 6 /* 'd' */, match: 7 /* 'd' */}, { op: 'match', input: 7 /* 'a' */, match: 8 /* 'a' */}, @@ -635,7 +635,7 @@ describe('Classical Damerau-Levenshtein edit-distance calculation', function() { { op: 'delete', input: 3}, { op: 'match', input: 4, match: 3}, { op: 'match', input: 5, match: 4}, - { op: 'insert', match: 5} + { op: 'insert', match: 5} ]; const viablePaths = buffer.editPath(); @@ -687,14 +687,14 @@ describe('Classical Damerau-Levenshtein edit-distance calculation', function() { { op: 'match', input: 1, match: 1}, // best sequence: continguous matches { op: 'match', input: 2, match: 2}, // + adjacent substitutes { op: 'substitute', input: 3, match: 3}, - { op: 'insert', match: 4}, + { op: 'insert', match: 4}, ]; let altSequence: EditTuple[] = [ { op: 'match', input: 0, match: 0}, { op: 'match', input: 1, match: 1}, { op: 'match', input: 2, match: 2}, - { op: 'insert', match: 3}, + { op: 'insert', match: 3}, { op: 'substitute', input: 3, match: 4}, ]; @@ -717,7 +717,7 @@ describe('Classical Damerau-Levenshtein edit-distance calculation', function() { { op: 'match', input: 5, match: 3}, { op: 'match', input: 6, match: 4}, { op: 'transpose-start', input: 7, match: 5}, - { op: 'transpose-insert', match: 6}, + { op: 'transpose-insert', match: 6}, { op: 'transpose-end', input: 8, match: 7} ]; diff --git a/web/src/test/auto/headless/engine/predictive-text/worker-thread/correction-search/segmentable-calculation.tests.ts b/web/src/test/auto/headless/engine/predictive-text/worker-thread/correction-search/segmentable-calculation.tests.ts index 9b8df03e3c..1abdfa89e4 100644 --- a/web/src/test/auto/headless/engine/predictive-text/worker-thread/correction-search/segmentable-calculation.tests.ts +++ b/web/src/test/auto/headless/engine/predictive-text/worker-thread/correction-search/segmentable-calculation.tests.ts @@ -147,7 +147,6 @@ describe('Split/merge aware edit-distance calculation', () => { assert.equal(calc.getFinalCost(), 4); const editPaths = calc.editPath(); - assert.isAbove(editPaths.length, 3); assert.sameDeepOrderedMembers(editPaths[0], [ { op: 'split', input: 0, match: 0 }, { op: 'split', input: 0, match: 1 }, @@ -156,6 +155,26 @@ describe('Split/merge aware edit-distance calculation', () => { { op: 'merge', input: 3, match: 4 }, { op: 'merge', input: 4, match: 4 } ]); + + // ('insert', 'substitute') can be reordered + // ('substitute', 'delete') can be reordered + // There also exist cases where one pair or the other, but not both, + // are considered for the edit path. + assert.includeDeepMembers(editPaths, [[ + { op: 'insert', match: 0 }, // -> 'b' + { op: 'substitute', input: 0, match: 1 }, // 'abc' -> 'c' + { op: 'match', input: 1, match: 2 }, // 'd' == 'd' + { op: 'match', input: 2, match: 3 }, // 'e' == 'e' + { op: 'substitute', input: 3, match: 4 }, // 'f' -> 'fgh' + { op: 'delete', input: 4 } // 'g' -> + ]]); + + // both split and merge: 1 variation + // split, but not merge: 2 variations + // merge, but not split: 2 variations + // neither: 2x2 = 4 variations + // sum: 9 + assert.equal(editPaths.length, 9); }); it("abcd,e,f,g -> b,c,d,efgh = 4", () => { @@ -180,10 +199,10 @@ describe('Split/merge aware edit-distance calculation', () => { ]); assert.sameDeepOrderedMembers(editPaths[1], [ - { op: 'substitute', input: 0, match: 0 }, - { op: 'substitute', input: 1, match: 1 }, - { op: 'substitute', input: 2, match: 2 }, - { op: 'substitute', input: 3, match: 3 } + { op: 'substitute', input: 0, match: 0 }, // 'abcd' -> 'b' + { op: 'substitute', input: 1, match: 1 }, // 'e' -> 'c' + { op: 'substitute', input: 2, match: 2 }, // 'f' -> 'd' + { op: 'substitute', input: 3, match: 3 } // 'g' -> 'efgh' ]); }); }); \ No newline at end of file diff --git a/windows/docs/help/common/unicode.md b/windows/docs/help/common/unicode.md index 614934e72c..fd567c8ce4 100644 --- a/windows/docs/help/common/unicode.md +++ b/windows/docs/help/common/unicode.md @@ -3,5 +3,5 @@ title: Does Keyman support Unicode? --- Yes, since Keyman 5, Keyman fully supports the Unicode Standard, up to -and including Unicode 13.0. We also update Keyman with each new release +and including Unicode 17.0. We also update Keyman with each new release of the Unicode Standard. diff --git a/windows/src/desktop/inst/build.sh b/windows/src/desktop/inst/build.sh index aa581d5e04..8e64342a22 100755 --- a/windows/src/desktop/inst/build.sh +++ b/windows/src/desktop/inst/build.sh @@ -15,7 +15,7 @@ builder_describe "Installation files for Keyman for Windows" \ # after all other builds complete builder_if_release_build_level builder_describe_outputs \ - publish /windows/release/${KEYMAN_VERSION}/keyman-${KEYMAN_VERSION}.exe + publish /windows/release/${KEYMAN_VERSION}/keyman-${KEYMAN_VERSION_FOR_FILENAME}.exe builder_parse "$@" @@ -104,7 +104,7 @@ function copy-installer() { mkdir -p "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}" cp keymandesktop.msi "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/keymandesktop.msi" - cp keymandesktop.exe "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/keyman-${KEYMAN_VERSION}.exe" + cp keymandesktop.exe "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/keyman-${KEYMAN_VERSION_FOR_FILENAME}.exe" cp "$WINDOWS_PROGRAM_APP/setup.exe" "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/setup.exe" verify-installer-signatures diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas index 7b831b97c4..d98e2c05f0 100644 --- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas +++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas @@ -88,14 +88,19 @@ type procedure HandleFirstRun; function CurrentStateName: string; (** - * Checks if Keyman is the WaitingRestartState and that - * Keyman has not run in this Windows session. - * The sole purpose is for the calling code then produce - * a UI to confirm the user wants to continue install. + * Checks if Keyman is the WaitingRestartState, the cache is valid + * and that Keyman has not run in this Windows session. It also confirms + * that automatic updates are still enabled #14295. + * The purpose is for the calling code can choose to produce + * a UI to confirm the user wants to install an update, + * if everything is ready to start a update. + * + * NOTE: If it finds the cache is invalid it will abort causing + * the State Machine to change state, returning to idle. * * @returns True if the Keyman is ready to install. *) - function ReadyToInstall: Boolean; + function ValidateReadyToInstall: Boolean; function IsInstallingState: Boolean; function GetAutomaticUpdates: Boolean; @@ -558,14 +563,27 @@ begin Result := CurrentState.ClassName; end; -function TUpdateStateMachine.ReadyToInstall: Boolean; +function TUpdateStateMachine.ValidateReadyToInstall: Boolean; begin if not IsCurrentStateAssigned then Exit(False); - if (CurrentState is WaitingRestartState) and not HasKeymanRun then - Result := True - else - Result := False; + + if not (CurrentState is WaitingRestartState) then + Exit(False); + + // Verify conditions since entering WaitingRestartState: + // If the cache is not valid or automatic updates have + // been disabled clear the cache and return to the idle state. + if not TUpdateCheckStorage.CheckMetaDataForUpdate or not Self.GetAutomaticUpdates then + begin + HandleAbort; + Exit(False); + end; + + if not HasKeymanRun then + Exit(True); + + Result := False; end; function TUpdateStateMachine.IsInstallingState: Boolean; @@ -955,7 +973,7 @@ begin // Return to Idle state and check for Updates state ChangeState(IdleState); bucStateContext.CurrentState.HandleCheck; - Result := kmShellExit; + Result := kmShellContinue; end else begin diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas index 8a9e037de6..1f02c696e3 100644 --- a/windows/src/desktop/kmshell/main/initprog.pas +++ b/windows/src/desktop/kmshell/main/initprog.pas @@ -668,20 +668,11 @@ function ShouldSendToBUpdateSM(FSilent: Boolean; BUpdateSM: TUpdateStateMachine; // UI elements from the state machine we have bring some of logic here. var frmStartInstall: TfrmStartInstall; - ReadyToInstall: Boolean; + ValidateReadyToInstall: Boolean; begin - ReadyToInstall := BUpdateSM.ReadyToInstall; - // The setting for automatic updates may have been set to disable - // after updates were checked and downloaded. The user should - // not be prompted for an update in this case and the update should - // abort. - if not BUpdateSM.GetAutomaticUpdates and ReadyToInstall then - begin - BUpdateSM.HandleAbort; - Exit(False); - end; + ValidateReadyToInstall := BUpdateSM.ValidateReadyToInstall; - if not FSilent and ReadyToInstall and + if not FSilent and ValidateReadyToInstall and (FMode in [fmStart, fmSplash, fmMain, fmAbout, fmHelp, fmShowHelp, fmSettings, fmBoot]) then begin @@ -692,7 +683,7 @@ begin frmStartInstall.Free; end; end - else if FSilent and ReadyToInstall then + else if FSilent and ValidateReadyToInstall then Result := False else Result := True;