snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
This commit is contained in:
58
packages/utils/src/lib/string.ts
Normal file
58
packages/utils/src/lib/string.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
let graphemeSegmenter: Intl.Segmenter | undefined
|
||||
let checkedForSegmenter = false
|
||||
|
||||
/**
|
||||
* Resolve a shared grapheme `Intl.Segmenter`, or `undefined` when the runtime has no
|
||||
* `Intl.Segmenter`. `Intl.Segmenter` is unavailable on some older browsers (notably Firefox before
|
||||
* 125), so feature-detect it once rather than constructing it eagerly, which would throw at module
|
||||
* load and take down the whole app.
|
||||
*/
|
||||
function getGraphemeSegmenter(): Intl.Segmenter | undefined {
|
||||
if (!checkedForSegmenter) {
|
||||
checkedForSegmenter = true
|
||||
if (typeof Intl !== 'undefined' && 'Segmenter' in Intl) {
|
||||
graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' })
|
||||
}
|
||||
}
|
||||
return graphemeSegmenter
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over the grapheme clusters of a string — user-perceived characters such as emoji
|
||||
* sequences, flags, and accented letters — instead of UTF-16 code units. Uses `Intl.Segmenter`
|
||||
* where available and falls back to iterating by code point on browsers that lack it, where a
|
||||
* multi-code-point cluster is yielded as its component code points rather than as one segment.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function* iterateGraphemes(str: string): IterableIterator<string> {
|
||||
const segmenter = getGraphemeSegmenter()
|
||||
if (segmenter) {
|
||||
for (const { segment } of segmenter.segment(str)) yield segment
|
||||
} else {
|
||||
yield* str
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first character of a string, treating a multi-code-unit character such as an emoji as a
|
||||
* single character. Unlike `str[0]` or `str.charAt(0)`, which return one UTF-16 code unit and so
|
||||
* split an emoji into a broken half, this returns the first whole grapheme cluster. Returns an
|
||||
* empty string when the input is empty.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* getFirstCharacter('hello') // 'h'
|
||||
* getFirstCharacter('😀 hello') // '😀'
|
||||
* getFirstCharacter('') // ''
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function getFirstCharacter(str: string): string {
|
||||
if (!str) return ''
|
||||
for (const segment of iterateGraphemes(str)) {
|
||||
return segment
|
||||
}
|
||||
return ''
|
||||
}
|
||||
Reference in New Issue
Block a user