···1111export * from './nested-comments';
1212export * from './link-card';
1313export * from './animated-emoji-picker';
1414+export * from './atproto-handle-popup';
14151516export function numberToHumanReadable(number: number) {
1617 if (number < 1000) {
···11-// from https://github.com/Doist/typist/blob/main/src/extensions/rich-text/rich-text-link.ts
22-import { InputRule, markInputRule, markPasteRule, PasteRule } from '@tiptap/core';
33-import { Link } from '@tiptap/extension-link';
44-55-import type { LinkOptions } from '@tiptap/extension-link';
66-77-/**
88- * The input regex for Markdown links with title support, and multiple quotation marks (required
99- * in case the `Typography` extension is being included).
1010- */
1111-const inputRegex = /(?:^|\s)\[([^\]]*)?\]\((\S+)(?: ["“](.+)["”])?\)$/i;
1212-1313-/**
1414- * The paste regex for Markdown links with title support, and multiple quotation marks (required
1515- * in case the `Typography` extension is being included).
1616- */
1717-const pasteRegex = /(?:^|\s)\[([^\]]*)?\]\((\S+)(?: ["“](.+)["”])?\)/gi;
1818-1919-/**
2020- * Input rule built specifically for the `Link` extension, which ignores the auto-linked URL in
2121- * parentheses (e.g., `(https://doist.dev)`).
2222- *
2323- * @see https://github.com/ueberdosis/tiptap/discussions/1865
2424- */
2525-function linkInputRule(config: Parameters<typeof markInputRule>[0]) {
2626- const defaultMarkInputRule = markInputRule(config);
2727-2828- return new InputRule({
2929- find: config.find,
3030- handler(props) {
3131- const { tr } = props.state;
3232-3333- defaultMarkInputRule.handler(props);
3434- tr.setMeta('preventAutolink', true);
3535- }
3636- });
3737-}
3838-3939-/**
4040- * Paste rule built specifically for the `Link` extension, which ignores the auto-linked URL in
4141- * parentheses (e.g., `(https://doist.dev)`). This extension was inspired from the multiple
4242- * implementations found in a Tiptap discussion at GitHub.
4343- *
4444- * @see https://github.com/ueberdosis/tiptap/discussions/1865
4545- */
4646-function linkPasteRule(config: Parameters<typeof markPasteRule>[0]) {
4747- const defaultMarkPasteRule = markPasteRule(config);
4848-4949- return new PasteRule({
5050- find: config.find,
5151- handler(props) {
5252- const { tr } = props.state;
5353-5454- defaultMarkPasteRule.handler(props);
5555- tr.setMeta('preventAutolink', true);
5656- }
5757- });
5858-}
5959-6060-/**
6161- * The options available to customize the `RichTextLink` extension.
6262- */
6363-type RichTextLinkOptions = LinkOptions;
6464-6565-/**
6666- * Custom extension that extends the built-in `Link` extension to add additional input/paste rules
6767- * for converting the Markdown link syntax (i.e. `[Doist](https://doist.com)`) into links, and also
6868- * adds support for the `title` attribute.
6969- */
7070-const RichTextLink = Link.extend<RichTextLinkOptions>({
7171- inclusive: false,
7272- addOptions() {
7373- return {
7474- ...this.parent?.(),
7575- openOnClick: 'whenNotEditable' as const
7676- } as RichTextLinkOptions;
7777- },
7878- addAttributes() {
7979- return {
8080- ...this.parent?.(),
8181- title: {
8282- default: null
8383- }
8484- };
8585- },
8686- addInputRules() {
8787- return [
8888- linkInputRule({
8989- find: inputRegex,
9090- type: this.type,
9191-9292- // We need to use `pop()` to remove the last capture groups from the match to
9393- // satisfy Tiptap's `markPasteRule` expectation of having the content as the last
9494- // capture group in the match (this makes the attribute order important)
9595- getAttributes(match) {
9696- return {
9797- title: match.pop()?.trim(),
9898- href: match.pop()?.trim()
9999- };
100100- }
101101- })
102102- ];
103103- },
104104- addPasteRules() {
105105- return [
106106- linkPasteRule({
107107- find: pasteRegex,
108108- type: this.type,
109109-110110- // We need to use `pop()` to remove the last capture groups from the match to
111111- // satisfy Tiptap's `markInputRule` expectation of having the content as the last
112112- // capture group in the match (this makes the attribute order important)
113113- getAttributes(match) {
114114- return {
115115- title: match.pop()?.trim(),
116116- href: match.pop()?.trim()
117117- };
118118- }
119119- })
120120- ];
121121- }
122122-});
123123-124124-export { RichTextLink };
125125-126126-export type { RichTextLinkOptions };
···10101111## Usage
12121313-### Client side login
1313+### Add modal to your `+layout.svelte`
14141515```svelte
1616<script lang="ts">
1717- import { AtprotoLogin } from '@foxui/social';
1717+ import { AtprotoLoginModal } from '@foxui/social';
1818</script>
19192020-<AtprotoLogin
2020+<AtprotoLoginModal
2121 login={async (handle) => {
2222 // do login and return true if login
2323+ // is successful and should hide the modal
2424+ }}
2525+ signup={async () => {
2626+ // do signup and return true if signup
2327 // is successful and should hide the modal
2428 }}
2529/>
2630```
27312828-### Use with forms
2929-3030-Pass in `formAction` and `formMethod` to use the BlueskyLogin component as a form submit handler.
3232+### Open the modal
31333234```svelte
3333-<ATProtoLogin formAction="/login" formMethod="post" />
3434-```
3535+<script lang="ts">
3636+ import { atProtoLoginModalState, Button } from '@foxui/all';
3737+</script>
35383636-And then use like a normal form in your `+page.server.ts` or `+layout.server.ts` file:
3737-3838-```ts
3939-export const actions: Actions = {
4040- login: async ({ request }) => {
4141- const formData = await request.formData();
4242- let handle = formData.get('handle') as string;
4343-4444- // authorization logic here
4545- }
4646-};
4747-```
3939+<Button onclick={() => atProtoLoginModalState.show()}>Login</Button>
4040+```