+2
-2
components/Blocks/TextBlock/index.tsx
+2
-2
components/Blocks/TextBlock/index.tsx
···
1
-
import { useRef, useEffect, useState } from "react";
1
+
import { useRef, useEffect, useState, useLayoutEffect } from "react";
2
2
import { elementId } from "src/utils/elementId";
3
3
import { baseKeymap } from "prosemirror-commands";
4
4
import { keymap } from "prosemirror-keymap";
···
22
22
import { addBlueskyPostBlock, addLinkBlock } from "src/utils/addLinkBlock";
23
23
import { BlockCommandBar } from "components/Blocks/BlockCommandBar";
24
24
import { useEditorStates } from "src/state/useEditorState";
25
-
import { isIOS, useLayoutEffect } from "@react-aria/utils";
26
25
import { useEntitySetContext } from "components/EntitySetProvider";
27
26
import { useHandlePaste } from "./useHandlePaste";
28
27
import { highlightSelectionPlugin } from "./plugins";
···
35
34
import { AddTiny } from "components/Icons/AddTiny";
36
35
import { BlockDocPageSmall } from "components/Icons/BlockDocPageSmall";
37
36
import { BlockImageSmall } from "components/Icons/BlockImageSmall";
37
+
import { isIOS } from "src/utils/isDevice";
38
38
39
39
const HeadingStyle = {
40
40
1: "text-xl font-bold",
+1
-1
components/HelpPopover.tsx
+1
-1
components/HelpPopover.tsx
···
1
1
"use client";
2
-
import { isMac } from "@react-aria/utils";
3
2
import { ShortcutKey } from "./Layout";
4
3
import { Media } from "./Media";
5
4
import { Popover } from "./Popover";
···
8
7
import { useState } from "react";
9
8
import { ActionButton } from "components/ActionBar/ActionButton";
10
9
import { HelpSmall } from "./Icons/HelpSmall";
10
+
import { isMac } from "src/utils/isDevice";
11
11
12
12
export const HelpPopover = (props: { noShortcuts?: boolean }) => {
13
13
let entity_set = useEntitySetContext();
+1
-1
components/Input.tsx
+1
-1
components/Input.tsx
···
1
1
"use client";
2
-
import { isIOS } from "@react-aria/utils";
3
2
import { useCallback, useEffect, useRef, type JSX } from "react";
4
3
import { onMouseDown } from "src/utils/iosInputMouseDown";
4
+
import { isIOS } from "src/utils/isDevice";
5
5
6
6
export function Input(
7
7
props: React.DetailedHTMLProps<
+1
-1
components/Toolbar/HighlightToolbar.tsx
+1
-1
components/Toolbar/HighlightToolbar.tsx
···
22
22
import { rangeHasMark } from "src/utils/prosemirror/rangeHasMark";
23
23
24
24
import { Separator, ShortcutKey } from "components/Layout";
25
-
import { isMac } from "@react-aria/utils";
26
25
import { ToolbarButton } from ".";
27
26
import { NestedCardThemeProvider } from "components/ThemeManager/ThemeProvider";
28
27
import { Props } from "components/Icons/Props";
···
30
29
import { ArrowRightTiny } from "components/Icons/ArrowRightTiny";
31
30
import { PaintSmall } from "components/Icons/PaintSmall";
32
31
import { Color } from "react-aria-components";
32
+
import { isMac } from "src/utils/isDevice";
33
33
34
34
export const HighlightButton = (props: {
35
35
lastUsedHighlight: string;
+1
-1
components/Toolbar/TextToolbar.tsx
+1
-1
components/Toolbar/TextToolbar.tsx
···
1
-
import { isMac } from "@react-aria/utils";
2
1
import { Separator, ShortcutKey } from "components/Layout";
3
2
import { metaKey } from "src/utils/metaKey";
4
3
import { LinkButton } from "./InlineLinkToolbar";
···
11
10
import { TextAlignmentButton } from "./TextAlignmentToolbar";
12
11
import { LockBlockButton } from "./LockBlockButton";
13
12
import { Props } from "components/Icons/Props";
13
+
import { isMac } from "src/utils/isDevice";
14
14
15
15
export const TextToolbar = (props: {
16
16
lastUsedHighlight: string;
+37
-1
components/ViewportSizeLayout.tsx
+37
-1
components/ViewportSizeLayout.tsx
···
1
1
"use client";
2
-
import { isIOS, useViewportSize } from "@react-aria/utils";
3
2
import { useEffect, useState } from "react";
3
+
import { isIOS } from "src/utils/isDevice";
4
4
5
5
export function ViewportSizeLayout(props: { children: React.ReactNode }) {
6
6
let viewheight = useViewportSize().height;
···
53
53
height: visualViewport?.height || window?.innerHeight,
54
54
};
55
55
}
56
+
57
+
export function useViewportSize(): {
58
+
width: number;
59
+
height: number;
60
+
} {
61
+
let [size, setSize] = useState(() => getViewportSize());
62
+
63
+
useEffect(() => {
64
+
// Use visualViewport api to track available height even on iOS virtual keyboard opening
65
+
let onResize = () => {
66
+
setSize((size) => {
67
+
let newSize = getViewportSize();
68
+
if (newSize.width === size.width && newSize.height === size.height) {
69
+
return size;
70
+
}
71
+
return newSize;
72
+
});
73
+
};
74
+
75
+
if (!visualViewport) {
76
+
window.addEventListener("resize", onResize);
77
+
} else {
78
+
visualViewport.addEventListener("resize", onResize);
79
+
}
80
+
81
+
return () => {
82
+
if (!visualViewport) {
83
+
window.removeEventListener("resize", onResize);
84
+
} else {
85
+
visualViewport.removeEventListener("resize", onResize);
86
+
}
87
+
};
88
+
}, []);
89
+
90
+
return size;
91
+
}
+159
-27
package-lock.json
+159
-27
package-lock.json
···
18
18
"@atproto/xrpc": "^0.6.9",
19
19
"@mdx-js/loader": "^3.1.0",
20
20
"@mdx-js/react": "^3.1.0",
21
+
"@next/bundle-analyzer": "^15.3.2",
21
22
"@next/mdx": "15.3.2",
22
23
"@radix-ui/react-dropdown-menu": "^2.1.14",
23
24
"@radix-ui/react-popover": "^1.1.13",
24
25
"@radix-ui/react-slider": "^1.3.4",
25
26
"@radix-ui/react-tooltip": "^1.2.6",
26
-
"@react-aria/utils": "^3.24.1",
27
27
"@react-spring/web": "^10.0.0-beta.0",
28
28
"@rocicorp/undo": "^0.2.1",
29
29
"@supabase/ssr": "^0.3.0",
···
31
31
"@tiptap/core": "^2.11.5",
32
32
"@types/mdx": "^2.0.13",
33
33
"@vercel/analytics": "^1.3.1",
34
-
"@vercel/kv": "^1.0.1",
35
34
"@vercel/sdk": "^1.3.1",
36
35
"babel-plugin-react-compiler": "^19.1.0-rc.1",
37
36
"base64-js": "^1.5.1",
···
790
789
"version": "1.2.0",
791
790
"resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.2.0.tgz",
792
791
"integrity": "sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg=="
792
+
},
793
+
"node_modules/@discoveryjs/json-ext": {
794
+
"version": "0.5.7",
795
+
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
796
+
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
797
+
"license": "MIT",
798
+
"engines": {
799
+
"node": ">=10.0.0"
800
+
}
793
801
},
794
802
"node_modules/@emnapi/runtime": {
795
803
"version": "1.4.3",
···
2518
2526
"react": ">=16"
2519
2527
}
2520
2528
},
2529
+
"node_modules/@next/bundle-analyzer": {
2530
+
"version": "15.3.2",
2531
+
"resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-15.3.2.tgz",
2532
+
"integrity": "sha512-zY5O1PNKNxWEjaFX8gKzm77z2oL0cnj+m5aiqNBgay9LPLCDO13Cf+FJONeNq/nJjeXptwHFT9EMmTecF9U4Iw==",
2533
+
"license": "MIT",
2534
+
"dependencies": {
2535
+
"webpack-bundle-analyzer": "4.10.1"
2536
+
}
2537
+
},
2521
2538
"node_modules/@next/env": {
2522
2539
"version": "15.3.2",
2523
2540
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.2.tgz",
···
2802
2819
"engines": {
2803
2820
"node": ">=14"
2804
2821
}
2822
+
},
2823
+
"node_modules/@polka/url": {
2824
+
"version": "1.0.0-next.29",
2825
+
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
2826
+
"integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
2827
+
"license": "MIT"
2805
2828
},
2806
2829
"node_modules/@radix-ui/number": {
2807
2830
"version": "1.1.1",
···
5958
5981
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
5959
5982
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
5960
5983
},
5961
-
"node_modules/@upstash/redis": {
5962
-
"version": "1.25.1",
5963
-
"resolved": "https://registry.npmjs.org/@upstash/redis/-/redis-1.25.1.tgz",
5964
-
"integrity": "sha512-ACj0GhJ4qrQyBshwFgPod6XufVEfKX2wcaihsEvSdLYnY+m+pa13kGt1RXm/yTHKf4TQi/Dy2A8z/y6WUEOmlg==",
5965
-
"dependencies": {
5966
-
"crypto-js": "^4.2.0"
5967
-
}
5968
-
},
5969
5984
"node_modules/@vercel/analytics": {
5970
5985
"version": "1.3.1",
5971
5986
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.3.1.tgz",
···
5984
5999
"react": {
5985
6000
"optional": true
5986
6001
}
5987
-
}
5988
-
},
5989
-
"node_modules/@vercel/kv": {
5990
-
"version": "1.0.1",
5991
-
"resolved": "https://registry.npmjs.org/@vercel/kv/-/kv-1.0.1.tgz",
5992
-
"integrity": "sha512-uTKddsqVYS2GRAM/QMNNXCTuw9N742mLoGRXoNDcyECaxEXvIHG0dEY+ZnYISV4Vz534VwJO+64fd9XeSggSKw==",
5993
-
"dependencies": {
5994
-
"@upstash/redis": "1.25.1"
5995
-
},
5996
-
"engines": {
5997
-
"node": ">=14.6"
5998
6002
}
5999
6003
},
6000
6004
"node_modules/@vercel/sdk": {
···
6053
6057
"version": "8.3.2",
6054
6058
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
6055
6059
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
6056
-
"dev": true,
6057
6060
"engines": {
6058
6061
"node": ">=0.4.0"
6059
6062
}
···
7135
7138
"node": ">= 8"
7136
7139
}
7137
7140
},
7138
-
"node_modules/crypto-js": {
7139
-
"version": "4.2.0",
7140
-
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
7141
-
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
7142
-
},
7143
7141
"node_modules/cssesc": {
7144
7142
"version": "3.0.0",
7145
7143
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
···
7587
7585
"engines": {
7588
7586
"node": ">= 0.4"
7589
7587
}
7588
+
},
7589
+
"node_modules/duplexer": {
7590
+
"version": "0.1.2",
7591
+
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
7592
+
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
7593
+
"license": "MIT"
7590
7594
},
7591
7595
"node_modules/eastasianwidth": {
7592
7596
"version": "0.2.0",
···
9255
9259
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
9256
9260
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
9257
9261
},
9262
+
"node_modules/gzip-size": {
9263
+
"version": "6.0.0",
9264
+
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
9265
+
"integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
9266
+
"license": "MIT",
9267
+
"dependencies": {
9268
+
"duplexer": "^0.1.2"
9269
+
},
9270
+
"engines": {
9271
+
"node": ">=10"
9272
+
},
9273
+
"funding": {
9274
+
"url": "https://github.com/sponsors/sindresorhus"
9275
+
}
9276
+
},
9258
9277
"node_modules/hanji": {
9259
9278
"version": "0.0.5",
9260
9279
"resolved": "https://registry.npmjs.org/hanji/-/hanji-0.0.5.tgz",
···
9670
9689
"resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz",
9671
9690
"integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==",
9672
9691
"dev": true
9692
+
},
9693
+
"node_modules/html-escaper": {
9694
+
"version": "2.0.2",
9695
+
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
9696
+
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
9697
+
"license": "MIT"
9673
9698
},
9674
9699
"node_modules/html-void-elements": {
9675
9700
"version": "3.0.0",
···
10159
10184
},
10160
10185
"funding": {
10161
10186
"url": "https://github.com/sponsors/sindresorhus"
10187
+
}
10188
+
},
10189
+
"node_modules/is-plain-object": {
10190
+
"version": "5.0.0",
10191
+
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
10192
+
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
10193
+
"license": "MIT",
10194
+
"engines": {
10195
+
"node": ">=0.10.0"
10162
10196
}
10163
10197
},
10164
10198
"node_modules/is-promise": {
···
12003
12037
"url": "https://github.com/sponsors/isaacs"
12004
12038
}
12005
12039
},
12040
+
"node_modules/mrmime": {
12041
+
"version": "2.0.1",
12042
+
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
12043
+
"integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
12044
+
"license": "MIT",
12045
+
"engines": {
12046
+
"node": ">=10"
12047
+
}
12048
+
},
12006
12049
"node_modules/ms": {
12007
12050
"version": "2.1.2",
12008
12051
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
···
12402
12445
"dev": true,
12403
12446
"dependencies": {
12404
12447
"wrappy": "1"
12448
+
}
12449
+
},
12450
+
"node_modules/opener": {
12451
+
"version": "1.5.2",
12452
+
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
12453
+
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
12454
+
"license": "(WTFPL OR MIT)",
12455
+
"bin": {
12456
+
"opener": "bin/opener-bin.js"
12405
12457
}
12406
12458
},
12407
12459
"node_modules/optionator": {
···
14464
14516
"is-arrayish": "^0.3.1"
14465
14517
}
14466
14518
},
14519
+
"node_modules/sirv": {
14520
+
"version": "2.0.4",
14521
+
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
14522
+
"integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
14523
+
"license": "MIT",
14524
+
"dependencies": {
14525
+
"@polka/url": "^1.0.0-next.24",
14526
+
"mrmime": "^2.0.0",
14527
+
"totalist": "^3.0.0"
14528
+
},
14529
+
"engines": {
14530
+
"node": ">= 10"
14531
+
}
14532
+
},
14467
14533
"node_modules/sisteransi": {
14468
14534
"version": "1.0.5",
14469
14535
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
···
15113
15179
"license": "MIT",
15114
15180
"engines": {
15115
15181
"node": ">=0.6"
15182
+
}
15183
+
},
15184
+
"node_modules/totalist": {
15185
+
"version": "3.0.1",
15186
+
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
15187
+
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
15188
+
"license": "MIT",
15189
+
"engines": {
15190
+
"node": ">=6"
15116
15191
}
15117
15192
},
15118
15193
"node_modules/tr46": {
···
16196
16271
"license": "BSD-2-Clause",
16197
16272
"engines": {
16198
16273
"node": ">=12"
16274
+
}
16275
+
},
16276
+
"node_modules/webpack-bundle-analyzer": {
16277
+
"version": "4.10.1",
16278
+
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz",
16279
+
"integrity": "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==",
16280
+
"license": "MIT",
16281
+
"dependencies": {
16282
+
"@discoveryjs/json-ext": "0.5.7",
16283
+
"acorn": "^8.0.4",
16284
+
"acorn-walk": "^8.0.0",
16285
+
"commander": "^7.2.0",
16286
+
"debounce": "^1.2.1",
16287
+
"escape-string-regexp": "^4.0.0",
16288
+
"gzip-size": "^6.0.0",
16289
+
"html-escaper": "^2.0.2",
16290
+
"is-plain-object": "^5.0.0",
16291
+
"opener": "^1.5.2",
16292
+
"picocolors": "^1.0.0",
16293
+
"sirv": "^2.0.3",
16294
+
"ws": "^7.3.1"
16295
+
},
16296
+
"bin": {
16297
+
"webpack-bundle-analyzer": "lib/bin/analyzer.js"
16298
+
},
16299
+
"engines": {
16300
+
"node": ">= 10.13.0"
16301
+
}
16302
+
},
16303
+
"node_modules/webpack-bundle-analyzer/node_modules/commander": {
16304
+
"version": "7.2.0",
16305
+
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
16306
+
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
16307
+
"license": "MIT",
16308
+
"engines": {
16309
+
"node": ">= 10"
16310
+
}
16311
+
},
16312
+
"node_modules/webpack-bundle-analyzer/node_modules/ws": {
16313
+
"version": "7.5.10",
16314
+
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
16315
+
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
16316
+
"license": "MIT",
16317
+
"engines": {
16318
+
"node": ">=8.3.0"
16319
+
},
16320
+
"peerDependencies": {
16321
+
"bufferutil": "^4.0.1",
16322
+
"utf-8-validate": "^5.0.2"
16323
+
},
16324
+
"peerDependenciesMeta": {
16325
+
"bufferutil": {
16326
+
"optional": true
16327
+
},
16328
+
"utf-8-validate": {
16329
+
"optional": true
16330
+
}
16199
16331
}
16200
16332
},
16201
16333
"node_modules/whatwg-url": {
+2
-2
package.json
+2
-2
package.json
···
5
5
"main": "index.js",
6
6
"scripts": {
7
7
"dev": "next dev",
8
+
"generate-db-types": "supabase gen types --local > supabase/database.types.ts && drizzle-kit introspect && rm -rf ./drizzle/*.sql ./drizzle/meta",
8
9
"lexgen": "tsx ./lexicons/build.ts && lex gen-api ./lexicons/api ./lexicons/pub/leaflet/* ./lexicons/pub/leaflet/*/* ./lexicons/com/atproto/*/* --yes",
9
10
"wrangler-dev": "wrangler dev",
10
11
"start-appview-dev": "tsx --env-file='./.env.local' --watch appview/index.ts",
···
23
24
"@atproto/xrpc": "^0.6.9",
24
25
"@mdx-js/loader": "^3.1.0",
25
26
"@mdx-js/react": "^3.1.0",
27
+
"@next/bundle-analyzer": "^15.3.2",
26
28
"@next/mdx": "15.3.2",
27
29
"@radix-ui/react-dropdown-menu": "^2.1.14",
28
30
"@radix-ui/react-popover": "^1.1.13",
29
31
"@radix-ui/react-slider": "^1.3.4",
30
32
"@radix-ui/react-tooltip": "^1.2.6",
31
-
"@react-aria/utils": "^3.24.1",
32
33
"@react-spring/web": "^10.0.0-beta.0",
33
34
"@rocicorp/undo": "^0.2.1",
34
35
"@supabase/ssr": "^0.3.0",
···
36
37
"@tiptap/core": "^2.11.5",
37
38
"@types/mdx": "^2.0.13",
38
39
"@vercel/analytics": "^1.3.1",
39
-
"@vercel/kv": "^1.0.1",
40
40
"@vercel/sdk": "^1.3.1",
41
41
"babel-plugin-react-compiler": "^19.1.0-rc.1",
42
42
"base64-js": "^1.5.1",
+1
-1
src/shortcuts.ts
+1
-1
src/shortcuts.ts
+1
-1
src/utils/iosInputMouseDown.ts
+1
-1
src/utils/iosInputMouseDown.ts
+87
src/utils/isDevice.ts
+87
src/utils/isDevice.ts
···
1
+
/*
2
+
* Copyright 2020 Adobe. All rights reserved.
3
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+
* you may not use this file except in compliance with the License. You may obtain a copy
5
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
*
7
+
* Unless required by applicable law or agreed to in writing, software distributed under
8
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+
* OF ANY KIND, either express or implied. See the License for the specific language
10
+
* governing permissions and limitations under the License.
11
+
*/
12
+
13
+
function testUserAgent(re: RegExp) {
14
+
if (typeof window === "undefined" || window.navigator == null) {
15
+
return false;
16
+
}
17
+
return (
18
+
//@ts-ignore
19
+
window.navigator["userAgentData"]?.brands.some(
20
+
(brand: { brand: string; version: string }) => re.test(brand.brand),
21
+
) || re.test(window.navigator.userAgent)
22
+
);
23
+
}
24
+
25
+
function testPlatform(re: RegExp) {
26
+
return typeof window !== "undefined" && window.navigator != null
27
+
? re.test(
28
+
//@ts-ignore
29
+
window.navigator["userAgentData"]?.platform ||
30
+
window.navigator.platform,
31
+
)
32
+
: false;
33
+
}
34
+
35
+
function cached(fn: () => boolean) {
36
+
if (process.env.NODE_ENV === "test") {
37
+
return fn;
38
+
}
39
+
40
+
let res: boolean | null = null;
41
+
return () => {
42
+
if (res == null) {
43
+
res = fn();
44
+
}
45
+
return res;
46
+
};
47
+
}
48
+
49
+
export const isMac = cached(function () {
50
+
return testPlatform(/^Mac/i);
51
+
});
52
+
53
+
export const isIPhone = cached(function () {
54
+
return testPlatform(/^iPhone/i);
55
+
});
56
+
57
+
export const isIPad = cached(function () {
58
+
return (
59
+
testPlatform(/^iPad/i) ||
60
+
// iPadOS 13 lies and says it's a Mac, but we can distinguish by detecting touch support.
61
+
(isMac() && navigator.maxTouchPoints > 1)
62
+
);
63
+
});
64
+
65
+
export const isIOS = cached(function () {
66
+
return isIPhone() || isIPad();
67
+
});
68
+
69
+
export const isAppleDevice = cached(function () {
70
+
return isMac() || isIOS();
71
+
});
72
+
73
+
export const isWebKit = cached(function () {
74
+
return testUserAgent(/AppleWebKit/i) && !isChrome();
75
+
});
76
+
77
+
export const isChrome = cached(function () {
78
+
return testUserAgent(/Chrome/i);
79
+
});
80
+
81
+
export const isAndroid = cached(function () {
82
+
return testUserAgent(/Android/i);
83
+
});
84
+
85
+
export const isFirefox = cached(function () {
86
+
return testUserAgent(/Firefox/i);
87
+
});