├── .npmrc ├── static └── favicon.png ├── src ├── fonts │ ├── work-sans │ │ ├── WorkSans-Black.ttf │ │ ├── WorkSans-Bold.ttf │ │ ├── WorkSans-Light.ttf │ │ ├── WorkSans-Thin.ttf │ │ ├── WorkSans-Italic.ttf │ │ ├── WorkSans-Medium.ttf │ │ ├── WorkSans-Regular.ttf │ │ ├── WorkSans-SemiBold.ttf │ │ ├── WorkSans-BoldItalic.ttf │ │ ├── WorkSans-ExtraBold.ttf │ │ ├── WorkSans-ExtraLight.ttf │ │ ├── WorkSans-ThinItalic.ttf │ │ ├── WorkSans-BlackItalic.ttf │ │ ├── WorkSans-LightItalic.ttf │ │ ├── WorkSans-MediumItalic.ttf │ │ ├── WorkSans-ExtraBoldItalic.ttf │ │ ├── WorkSans-SemiBoldItalic.ttf │ │ └── WorkSans-ExtraLightItalic.ttf │ └── work-sans.css ├── lib │ ├── index.ts │ └── tipex │ │ ├── styles │ │ ├── index.css │ │ ├── theme.css │ │ ├── tipex.css │ │ ├── prosemirror.css │ │ └── codeblock.css │ │ ├── prepare.ts │ │ ├── icons │ │ ├── Fa6SolidCheck.svelte │ │ ├── Fa6SolidItalic.svelte │ │ ├── Fa6SolidParagraph.svelte │ │ ├── Fa6SolidCopy.svelte │ │ ├── Fa6SolidXmark.svelte │ │ ├── Fa6SolidBold.svelte │ │ ├── Fa6SolidArrowUpRightFromSquare.svelte │ │ ├── Fa6SolidCode.svelte │ │ └── Fa6SolidLink.svelte │ │ ├── default.ts │ │ ├── link │ │ ├── EditLinkMenu.svelte │ │ └── LinkFloatingMenu.svelte │ │ ├── Tipex.svelte │ │ └── Controls.svelte ├── hooks.server.ts ├── index.test.ts ├── item │ ├── ParamSpan.svelte │ ├── Footer.svelte │ ├── ThemeToggle.svelte │ ├── SlotPropsTable.svelte │ ├── PropsTable.svelte │ └── codes │ │ ├── basicCodes.json │ │ ├── commandsCodes.json │ │ ├── advanceCodes.json │ │ └── customizationCodes.json ├── routes │ ├── +page.ts │ ├── +layout.svelte │ ├── commands │ │ └── +page.svelte │ ├── +page.svelte │ └── customization │ │ └── +page.svelte ├── app.html ├── icons │ ├── SimpleIconsNpm.svelte │ ├── SimpleIconsGithub.svelte │ └── SimpleIconsBuymeacoffee.svelte ├── app.css └── app.d.ts ├── .gitignore ├── .prettierignore ├── tests └── test.ts ├── .prettierrc ├── playwright.config.ts ├── tsconfig.json ├── vite.config.ts ├── svelte.config.js ├── .github └── FUNDING.yml ├── eslint.config.js ├── package.json └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/static/favicon.png -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-Black.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-Bold.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-Light.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-Thin.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-Italic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-Medium.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-Regular.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-SemiBold.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-BoldItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-ExtraBold.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-ExtraLight.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-ThinItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-BlackItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-LightItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-MediumItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/HEAD/src/fonts/work-sans/WorkSans-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Tipex, type TipexEditor, type TipexProps } from './tipex/Tipex.svelte'; 2 | export { defaultExtensions } from './tipex/default.js'; 3 | -------------------------------------------------------------------------------- /src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import { sequence } from '@sveltejs/kit/hooks'; 2 | import { handleAppearance } from '@friendofsvelte/toggle'; 3 | 4 | export const handle = sequence(handleAppearance); 5 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | 3 | describe('sum test', () => { 4 | it('adds 1 + 2 to equal 3', () => { 5 | expect(1 + 2).toBe(3); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /dist 5 | /.svelte-kit 6 | /package 7 | .env 8 | .env.* 9 | !.env.example 10 | vite.config.js.timestamp-* 11 | vite.config.ts.timestamp-* 12 | .idea/ -------------------------------------------------------------------------------- /src/item/ParamSpan.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | {name}(tipex) 8 | -------------------------------------------------------------------------------- /src/lib/tipex/styles/index.css: -------------------------------------------------------------------------------- 1 | /* Tipex Editor Styles */ 2 | @import './theme.css'; 3 | @import './prosemirror.css'; 4 | @import './codeblock.css'; 5 | @import './tipex.css'; 6 | 7 | @custom-variant dark (&:where(.dark, .dark *)); 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /tests/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/test'; 2 | 3 | test('index page has expected h1', async ({ page }) => { 4 | await page.goto('/'); 5 | await expect(page.getByRole('heading', { name: 'Welcome to SvelteKit' })).toBeVisible(); 6 | }); 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import type { PlaywrightTestConfig } from '@playwright/test'; 2 | 3 | const config: PlaywrightTestConfig = { 4 | webServer: { 5 | command: 'npm run build && npm run preview', 6 | port: 4173 7 | }, 8 | testDir: 'tests', 9 | testMatch: /(.+\.)?(test|spec)\.[jt]s/ 10 | }; 11 | 12 | export default config; 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "module": "NodeNext", 13 | "moduleResolution": "nodenext" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | import tailwindcss from '@tailwindcss/vite'; 4 | 5 | export default defineConfig({ 6 | plugins: [tailwindcss(), sveltekit()], 7 | test: { 8 | include: ['src/**/*.{test,spec}.{js,ts}'] 9 | }, 10 | define: { 11 | __VERSION__: JSON.stringify(process.env.npm_package_version) 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /src/routes/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types.js'; 2 | 3 | const API_URL = 'https://api.github.com/repos/'; 4 | const REPO_URL = 'friendofsvelte/tipex'; 5 | const FULL_URL = API_URL + REPO_URL; 6 | 7 | export const load: PageLoad = async ({ fetch }) => { 8 | let repo: GithubRepo | NonNullable; 9 | try { 10 | const response = await fetch(FULL_URL); 11 | repo = await response.json(); 12 | } catch (e) { 13 | console.log(e); 14 | repo = {}; 15 | } 16 | return { 17 | repo 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /src/lib/tipex/prepare.ts: -------------------------------------------------------------------------------- 1 | import { FloatingMenu } from '@tiptap/extension-floating-menu'; 2 | 3 | export function getDefaultFloatingMenu(editLinkRef: HTMLElement) { 4 | return FloatingMenu.configure({ 5 | pluginKey: 'floatingLinkEdit', 6 | element: editLinkRef, 7 | shouldShow: ({ editor }) => { 8 | return editor.isActive('link'); 9 | }, 10 | tippyOptions: { 11 | placement: 'top-start', 12 | zIndex: 0, 13 | popperOptions: { 14 | placement: 'top-start', 15 | strategy: 'fixed' 16 | }, 17 | appendTo: () => document.body 18 | } 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /src/item/Footer.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 23 | -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidCheck.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidItalic.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidParagraph.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /src/item/ThemeToggle.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 25 | -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidCopy.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidXmark.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-cloudflare'; 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors 7 | // for more information about preprocessors 8 | preprocess: vitePreprocess(), 9 | 10 | kit: { 11 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. 12 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter. 13 | // See https://kit.svelte.dev/docs/adapters for more information about adapters. 14 | adapter: adapter(), 15 | alias: { 16 | $item: './src/item' 17 | } 18 | } 19 | }; 20 | 21 | export default config; 22 | -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidBold.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %sveltekit.head% 9 | 10 | 11 | 20 | 21 | 22 |
%sveltekit.body%
23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: bishwasbh 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: bishwasbh 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /src/icons/SimpleIconsNpm.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | {#if display} 20 | 27 | 31 | 32 | {:else if occupy} 33 |
34 | {/if} 35 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 |
20 | {#if page.url.pathname !== '/'} 21 | 22 | {/if} 23 |
24 | {@render children?.()} 25 |
26 |
27 |
28 | 29 | 36 | -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidArrowUpRightFromSquare.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidCode.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /src/lib/tipex/default.ts: -------------------------------------------------------------------------------- 1 | import { Link } from '@tiptap/extension-link'; 2 | import { Image } from '@tiptap/extension-image'; 3 | import { Placeholder } from '@tiptap/extension-placeholder'; 4 | import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'; 5 | import { Underline } from '@tiptap/extension-underline'; 6 | import { TaskList } from '@tiptap/extension-task-list'; 7 | import { TaskItem } from '@tiptap/extension-task-item'; 8 | import { lowlight } from 'lowlight'; 9 | 10 | export const defaultExtensions = [ 11 | Link.configure({ 12 | openOnClick: false, 13 | HTMLAttributes: { 14 | target: '_blank', 15 | rel: 'noopener noreferrer' 16 | } 17 | }), 18 | Image.configure({ 19 | allowBase64: true 20 | }), 21 | Placeholder.configure({ 22 | showOnlyWhenEditable: false 23 | }), 24 | CodeBlockLowlight.configure({ 25 | lowlight, 26 | languageClassPrefix: 'language-', 27 | defaultLanguage: 'plaintext' 28 | }), 29 | Underline, 30 | TaskList, 31 | TaskItem.configure({ 32 | nested: true 33 | }) 34 | ]; 35 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import prettier from 'eslint-config-prettier'; 2 | import js from '@eslint/js'; 3 | import { includeIgnoreFile } from '@eslint/compat'; 4 | import svelte from 'eslint-plugin-svelte'; 5 | import globals from 'globals'; 6 | import { fileURLToPath } from 'node:url'; 7 | import ts from 'typescript-eslint'; 8 | import svelteConfig from './svelte.config.js'; 9 | 10 | const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); 11 | 12 | export default ts.config( 13 | includeIgnoreFile(gitignorePath), 14 | js.configs.recommended, 15 | ...ts.configs.recommended, 16 | ...svelte.configs.recommended, 17 | prettier, 18 | ...svelte.configs.prettier, 19 | { 20 | languageOptions: { 21 | globals: { 22 | ...globals.browser, 23 | ...globals.node, 24 | __VERSION__: 'readonly' 25 | } 26 | } 27 | }, 28 | { 29 | files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], 30 | ignores: ['eslint.config.js', 'svelte.config.js'], 31 | 32 | languageOptions: { 33 | parserOptions: { 34 | projectService: true, 35 | extraFileExtensions: ['.svelte'], 36 | parser: ts.parser, 37 | svelteConfig 38 | } 39 | } 40 | } 41 | ); 42 | -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidLink.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /src/icons/SimpleIconsGithub.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | {#if display} 20 | 27 | 31 | 32 | {:else if occupy} 33 |
34 | {/if} 35 | -------------------------------------------------------------------------------- /src/lib/tipex/link/EditLinkMenu.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 53 | 54 | 72 | -------------------------------------------------------------------------------- /src/lib/tipex/link/LinkFloatingMenu.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 53 | 54 |
60 | 68 | 76 | 84 |
85 | -------------------------------------------------------------------------------- /src/item/SlotPropsTable.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Tipex Props

6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 33 | 34 | 37 | 38 | 39 | 42 | 43 | 44 | 48 | 49 | 50 |
Prop NameDefaultConditionDescription
19 | 20 | undefinedOptionalA slot that accepts a function receiving the TipexEditor instance, rendered above the 25 | main editor content area 26 |
controlComponent 31 | Snippet<[TipexEditor]> | null 32 | Optional 35 | A slot that accepts a function receiving the TipexEditor instance, used to completely replace the default controls. Set to null to hide all controls completely. 36 |
40 | 41 | undefinedOptional 45 | A slot that accepts a function receiving the TipexEditor instance, rendered below the 46 | editor content and controls 47 |
51 |
52 |
55 |

Control System

56 |

57 | Tipex automatically detects which control system to use: 58 |

59 |
    60 |
  • When controlComponent is provided: Uses your custom control component
  • 61 |
  • 62 | When controlComponent is not provided: Shows default controls with built-in utilities 63 |
  • 64 |
  • 65 | When controlComponent={'{null}'}: Hides all controls completely (no toolbar) 66 |
  • 67 |
68 |

69 | This ensures a clean API where you don't need to manage boolean flags. 70 |

71 |
72 | -------------------------------------------------------------------------------- /src/item/PropsTable.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
PropTypeDefaultDescription
extensionsExtensions[]defaultExtensionsExtensions to be used in the editor.
floatingbooleanfalseDetermines display of default floating menu.
oncreatefunction{function_string}Callback function when the editor is created.
ondestroyfunction{function_string}Callback function when the editor is destroyed.
onupdatefunction{function_string}Callback function when the editor is updated.
bodystring''HTML content to render.
thisTipTapN/AInstance of the editor.
classstring''Additional CSS classes to apply to the editor container.
stylestring''Inline styles to apply to the editor container.
focalbooleantrueFocus outline when editing (blue ring).
ctxId{'${string}_tipex'}_tipexSets context ID to get editor instance via getContext.
85 |
86 | 87 |
88 |

89 | Note: 90 | For better customization, please have a look at [Slot Props] 93 |

94 |
95 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @import url('./fonts/work-sans.css'); 2 | @import 'tailwindcss'; 3 | 4 | body { 5 | @apply bg-gradient-to-b from-gray-100 to-gray-200 dark:from-stone-900 dark:to-neutral-950 min-h-screen; 6 | font-family: work-sans, sans-serif; 7 | } 8 | 9 | a[href^='http'], 10 | a[href^='/'] { 11 | @apply text-blue-500 hover:text-blue-600 12 | dark:text-blue-400 dark:hover:text-blue-300; 13 | } 14 | 15 | pre { 16 | @apply rounded-md 17 | overflow-x-auto 18 | whitespace-pre-wrap 19 | border border-neutral-300 dark:border-neutral-800 shadow-sm; 20 | } 21 | 22 | table { 23 | @apply border-collapse; 24 | } 25 | 26 | th { 27 | @apply px-4 py-2 28 | text-left 29 | border 30 | border-neutral-200 dark:border-stone-900 31 | bg-neutral-100 dark:bg-neutral-800 32 | text-neutral-700 dark:text-neutral-200; 33 | } 34 | 35 | td { 36 | @apply px-4 py-2 37 | border 38 | border-neutral-300 dark:border-stone-900 39 | text-neutral-700 dark:text-neutral-200; 40 | } 41 | 42 | tr { 43 | @apply even:bg-neutral-100 even:dark:bg-neutral-800 44 | odd:bg-neutral-200 odd:dark:bg-neutral-700; 45 | } 46 | 47 | blockquote { 48 | @apply rounded border-l-4 border-neutral-200 dark:border-neutral-700 49 | bg-neutral-100 dark:bg-neutral-800 50 | text-neutral-700 dark:text-neutral-200 51 | px-4 py-2 52 | mt-3 ml-2 53 | overflow-x-auto; 54 | } 55 | 56 | code { 57 | @apply bg-neutral-100 dark:bg-neutral-800 58 | text-neutral-700 dark:text-neutral-200 59 | px-1; 60 | } 61 | 62 | .head-section { 63 | @apply flex flex-wrap flex-row gap-2 md:gap-3 mb-2 mt-2 64 | -ml-1 border-b border-neutral-200 dark:border-neutral-700 65 | pb-2; 66 | } 67 | 68 | .head-section h1 { 69 | @apply flex justify-center items-center gap-3; 70 | } 71 | 72 | .icon-link-section { 73 | @apply flex flex-row flex-wrap sm:flex-nowrap gap-2 md:gap-3 mb-2 mt-2 74 | ml-auto; 75 | } 76 | 77 | .icon-link-section a { 78 | @apply flex items-center justify-center 79 | bg-neutral-200/70 dark:bg-neutral-800 hover:scale-105 duration-100; 80 | } 81 | 82 | h1 { 83 | @apply mb-3 text-3xl font-bold 84 | text-neutral-700 dark:text-neutral-200; 85 | } 86 | 87 | p { 88 | @apply text-base sm:text-lg mt-1 mb-2 89 | text-neutral-600 dark:text-neutral-300; 90 | } 91 | 92 | .image-tab { 93 | @apply flex flex-col justify-center gap-1; 94 | } 95 | 96 | figcaption { 97 | @apply text-center text-sm text-neutral-600 dark:text-neutral-300; 98 | } 99 | 100 | h2 { 101 | @apply text-2xl font-bold 102 | text-neutral-700 dark:text-neutral-200; 103 | } 104 | 105 | h3 { 106 | @apply text-xl font-bold 107 | text-neutral-700 dark:text-neutral-200; 108 | } 109 | 110 | @custom-variant dark (&:where(.dark, .dark *)); 111 | -------------------------------------------------------------------------------- /src/lib/tipex/styles/theme.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | @theme { 4 | /* Tipex Design System */ 5 | --color-tipex-50: oklch(0.985 0 0); 6 | --color-tipex-100: oklch(0.97 0 0); 7 | --color-tipex-200: oklch(0.922 0 0); 8 | --color-tipex-300: oklch(0.87 0 0); 9 | --color-tipex-400: oklch(0.708 0 0); 10 | --color-tipex-500: oklch(0.556 0 0); 11 | --color-tipex-600: oklch(0.439 0 0); 12 | --color-tipex-700: oklch(0.371 0 0); 13 | --color-tipex-800: oklch(0.269 0 0); 14 | --color-tipex-900: oklch(0.205 0 0); 15 | --color-tipex-950: oklch(0.145 0 0); 16 | 17 | /* Tipex Success Colors (Green) */ 18 | --color-tipex-success-50: oklch(0.984 0.007 142.425); 19 | --color-tipex-success-100: oklch(0.971 0.014 142.425); 20 | --color-tipex-success-200: oklch(0.929 0.042 142.425); 21 | --color-tipex-success-300: oklch(0.87 0.085 142.425); 22 | --color-tipex-success-400: oklch(0.769 0.156 142.425); 23 | --color-tipex-success-500: oklch(0.647 0.208 142.425); 24 | --color-tipex-success-600: oklch(0.548 0.208 142.425); 25 | --color-tipex-success-700: oklch(0.463 0.181 142.425); 26 | --color-tipex-success-800: oklch(0.385 0.146 142.425); 27 | --color-tipex-success-900: oklch(0.324 0.118 142.425); 28 | --color-tipex-success-950: oklch(0.208 0.076 142.425); 29 | 30 | /* Tipex Primary Colors (Indigo) */ 31 | --color-tipex-primary-50: oklch(0.985 0.003 272.314); 32 | --color-tipex-primary-100: oklch(0.971 0.007 272.314); 33 | --color-tipex-primary-200: oklch(0.929 0.021 272.314); 34 | --color-tipex-primary-300: oklch(0.87 0.042 272.314); 35 | --color-tipex-primary-400: oklch(0.769 0.076 272.314); 36 | --color-tipex-primary-500: oklch(0.647 0.097 272.314); 37 | --color-tipex-primary-600: oklch(0.548 0.097 272.314); 38 | --color-tipex-primary-700: oklch(0.463 0.083 272.314); 39 | --color-tipex-primary-800: oklch(0.385 0.069 272.314); 40 | --color-tipex-primary-900: oklch(0.324 0.056 272.314); 41 | --color-tipex-primary-950: oklch(0.208 0.042 272.314); 42 | 43 | /* Spacing Scale */ 44 | --spacing-tipex-xs: 0.25rem; 45 | --spacing-tipex-sm: 0.5rem; 46 | --spacing-tipex-md: 1rem; 47 | --spacing-tipex-lg: 1.5rem; 48 | --spacing-tipex-xl: 2rem; 49 | --spacing-tipex-2xl: 2.5rem; 50 | 51 | /* Sizing Scale */ 52 | --size-tipex-1: 0.25rem; 53 | --size-tipex-2: 0.5rem; 54 | --size-tipex-3: 0.75rem; 55 | --size-tipex-4: 1rem; 56 | --size-tipex-5: 1.25rem; 57 | --size-tipex-6: 1.5rem; 58 | --size-tipex-7: 1.75rem; 59 | --size-tipex-8: 2rem; 60 | --size-tipex-9: 2.25rem; 61 | --size-tipex-10: 2.5rem; 62 | --size-tipex-11: 2.75rem; 63 | --size-tipex-12: 3rem; 64 | 65 | /* Border Radius */ 66 | --radius-tipex-sm: 0.25rem; 67 | --radius-tipex-md: 0.375rem; 68 | 69 | /* Typography */ 70 | --text-tipex-xs: 0.75rem; 71 | --text-tipex-sm: 0.875rem; 72 | --text-tipex-base: 1rem; 73 | --text-tipex-lg: 1.125rem; 74 | --text-tipex-xl: 1.25rem; 75 | --text-tipex-2xl: 1.5rem; 76 | 77 | /* Z-Index */ 78 | --z-tipex-floating: 50; 79 | --z-tipex-controls: 10; 80 | } 81 | -------------------------------------------------------------------------------- /src/icons/SimpleIconsBuymeacoffee.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | {#if display} 20 | 27 | 31 | 32 | {:else if occupy} 33 |
34 | {/if} 35 | -------------------------------------------------------------------------------- /src/item/codes/basicCodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "insertUtils": "import { Tipex, Controls } from \"@friendofsvelte/tipex\";\n\n\n {#snippet controlComponent(tipex)}\n \n \n \n \n \n {/snippet}\n", 3 | 4 | "appendUtils": "\n {#snippet controlComponent(tipex)}\n \n \n \n \n \n {/snippet}\n", 5 | 6 | "overrideControl": "\n {#snippet controlComponent(tipex)}\n
...
\n {/snippet}\n
", 7 | 8 | "install": "npm install \"@friendofsvelte/tipex\";", 9 | 10 | "usage": "\n\n\n", 11 | 12 | "body": "

My Project Notes

I've been working on this new feature for the past few days. It's been quite challenging but I think I'm making good progress. The documentation was a bit outdated so I had to figure out some things on my own.

Things I need to do

  • Set up the development environment
  • Install all dependencies
  • Write the core functionality
  • Add error handling
  • Test everything thoroughly
  • Create documentation

Resources I found helpful

Next steps

  1. Finish the basic implementation
  2. Add some styling improvements
  3. Test on different browsers
  4. Get feedback from the team

Remember: It's better to have something working than something perfect that never gets done.

I should probably take a break and come back to this tomorrow with fresh eyes. Sometimes stepping away helps you see things more clearly.

", 13 | 14 | "styling": "import \"@friendofsvelte/tipex/styles/index.css\";", 15 | 16 | "access": "" 17 | } 18 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface Platform {} 9 | } 10 | 11 | const __VERSION__: string; 12 | 13 | export interface GithubRepo { 14 | id: number; 15 | node_id: string; 16 | name: string; 17 | full_name: string; 18 | private: boolean; 19 | owner: Owner; 20 | html_url: string; 21 | description: string; 22 | fork: boolean; 23 | url: string; 24 | forks_url: string; 25 | keys_url: string; 26 | collaborators_url: string; 27 | teams_url: string; 28 | hooks_url: string; 29 | issue_events_url: string; 30 | events_url: string; 31 | assignees_url: string; 32 | branches_url: string; 33 | tags_url: string; 34 | blobs_url: string; 35 | git_tags_url: string; 36 | git_refs_url: string; 37 | trees_url: string; 38 | statuses_url: string; 39 | languages_url: string; 40 | stargazers_url: string; 41 | contributors_url: string; 42 | subscribers_url: string; 43 | subscription_url: string; 44 | commits_url: string; 45 | git_commits_url: string; 46 | comments_url: string; 47 | issue_comment_url: string; 48 | contents_url: string; 49 | compare_url: string; 50 | merges_url: string; 51 | archive_url: string; 52 | downloads_url: string; 53 | issues_url: string; 54 | pulls_url: string; 55 | milestones_url: string; 56 | notifications_url: string; 57 | labels_url: string; 58 | releases_url: string; 59 | deployments_url: string; 60 | created_at: string; 61 | updated_at: string; 62 | pushed_at: string; 63 | git_url: string; 64 | ssh_url: string; 65 | clone_url: string; 66 | svn_url: string; 67 | homepage: string; 68 | size: number; 69 | stargazers_count: number; 70 | watchers_count: number; 71 | language: string; 72 | has_issues: boolean; 73 | has_projects: boolean; 74 | has_downloads: boolean; 75 | has_wiki: boolean; 76 | has_pages: boolean; 77 | has_discussions: boolean; 78 | forks_count: number; 79 | mirror_url: any; 80 | archived: boolean; 81 | disabled: boolean; 82 | open_issues_count: number; 83 | license: any; 84 | allow_forking: boolean; 85 | is_template: boolean; 86 | web_commit_signoff_required: boolean; 87 | topics: string[]; 88 | visibility: string; 89 | forks: number; 90 | open_issues: number; 91 | watchers: number; 92 | default_branch: string; 93 | temp_clone_token: any; 94 | custom_properties: CustomProperties; 95 | organization: Organization; 96 | network_count: number; 97 | subscribers_count: number; 98 | } 99 | 100 | export interface Owner { 101 | login: string; 102 | id: number; 103 | node_id: string; 104 | avatar_url: string; 105 | gravatar_id: string; 106 | url: string; 107 | html_url: string; 108 | followers_url: string; 109 | following_url: string; 110 | gists_url: string; 111 | starred_url: string; 112 | subscriptions_url: string; 113 | organizations_url: string; 114 | repos_url: string; 115 | events_url: string; 116 | received_events_url: string; 117 | type: string; 118 | site_admin: boolean; 119 | } 120 | 121 | export interface CustomProperties {} 122 | 123 | export interface Organization { 124 | login: string; 125 | id: number; 126 | node_id: string; 127 | avatar_url: string; 128 | gravatar_id: string; 129 | url: string; 130 | html_url: string; 131 | followers_url: string; 132 | following_url: string; 133 | gists_url: string; 134 | starred_url: string; 135 | subscriptions_url: string; 136 | organizations_url: string; 137 | repos_url: string; 138 | events_url: string; 139 | received_events_url: string; 140 | type: string; 141 | site_admin: boolean; 142 | } 143 | } 144 | 145 | export {}; 146 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@friendofsvelte/tipex", 3 | "version": "0.0.9", 4 | "keywords": [ 5 | "svelte", 6 | "text editor", 7 | "svelte 5", 8 | "rich text", 9 | "svelte kit" 10 | ], 11 | "author": "bishwas bhandari", 12 | "license": "MIT", 13 | "bugs": { 14 | "url": "https://github.com/friendofsvelte/tipex/issues" 15 | }, 16 | "homepage": "https://tipex.pages.dev/", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/friendofsvelte/tipex.git" 20 | }, 21 | "scripts": { 22 | "dev": "vite dev", 23 | "build": "vite build && npm run prepack", 24 | "preview": "vite preview", 25 | "prepare": "svelte-kit sync || echo ''", 26 | "prepack": "svelte-kit sync && svelte-package && publint", 27 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 28 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 29 | "format": "prettier --write .", 30 | "lint": "prettier --check . && eslint .", 31 | "test:unit": "vitest", 32 | "test": "npm run test:unit -- --run && npm run test:e2e", 33 | "test:e2e": "playwright test", 34 | "pub:minor": "git add . && git commit -m \"chore: prepare for publish minor\" && npm version minor && git push --follow-tags && npm publish", 35 | "pub:patch": "git add . && git commit -m \"chore: prepare for publish patch\" && npm version patch && git push --follow-tags && npm publish", 36 | "pub:prelish": "git add . && git commit -m \"chore: prepare for publish premajor\" && npm version premajor && git push --follow-tags && npm publish", 37 | "pub:major": "git add . && git commit -m \"chore: prepare for publish major\" && npm version major && git push --follow-tags && npm publish", 38 | "pub:preminor": "git add . && git commit -m \"chore: prepare for publish preminor\" && npm version preminor && git push --follow-tags && npm publish", 39 | "pub:prepatch": "git add . && git commit -m \"chore: prepare for publish prepatch\" && npm version prepatch && git push --follow-tags && npm publish", 40 | "pub:prerelease": "git add . && git commit -m \"chore: prepare for publish prerelease\" && npm version prerelease && git push --follow-tags && npm publish", 41 | "pub:from-git": "git add . && git commit -m \"chore: prepare for publish from-git\" && npm version from-git && git push --follow-tags && npm publish" 42 | }, 43 | "exports": { 44 | ".": { 45 | "types": "./dist/index.d.ts", 46 | "svelte": "./dist/index.js" 47 | }, 48 | "./styles/*.css": "./dist/tipex/styles/*.css" 49 | }, 50 | "files": [ 51 | "dist", 52 | "!dist/**/*.test.*", 53 | "!dist/**/*.spec.*" 54 | ], 55 | "peerDependencies": { 56 | "svelte": "^5.0.0" 57 | }, 58 | "devDependencies": { 59 | "@eslint/compat": "^1.2.5", 60 | "@eslint/js": "^9.18.0", 61 | "@friendofsvelte/toggle": "0.0.2-svelte.5.docup", 62 | "@playwright/test": "^1.28.1", 63 | "@sveltejs/kit": "^2.0.0", 64 | "@sveltejs/package": "^2.0.0", 65 | "@sveltejs/vite-plugin-svelte": "^4.0.0", 66 | "@tailwindcss/typography": "^0.5.13", 67 | "@tailwindcss/vite": "^4.0.0", 68 | "eslint": "^9.18.0", 69 | "eslint-config-prettier": "^10.0.1", 70 | "eslint-plugin-svelte": "^3.0.0", 71 | "globals": "^15.0.0", 72 | "prettier": "^3.3.2", 73 | "prettier-plugin-svelte": "^3.2.6", 74 | "prettier-plugin-tailwindcss": "^0.6.5", 75 | "publint": "^0.2.0", 76 | "svelte": "^5.0.0", 77 | "svelte-check": "^4.0.0", 78 | "svelte-highlight": "^7.4.1", 79 | "tailwindcss": "^4.0.0", 80 | "tslib": "^2.4.1", 81 | "typescript": "^5.0.0", 82 | "typescript-eslint": "^8.20.0", 83 | "vite": "^5.0.11", 84 | "@sveltejs/adapter-cloudflare": "^7.2.3", 85 | "vitest": "^2.0.4" 86 | }, 87 | "svelte": "./dist/index.js", 88 | "types": "./dist/index.d.ts", 89 | "type": "module", 90 | "dependencies": { 91 | "@tiptap/core": "^2.1.13", 92 | "@tiptap/extension-code-block": "^2.1.13", 93 | "@tiptap/extension-code-block-lowlight": "^2.1.13", 94 | "@tiptap/extension-floating-menu": "^2.1.13", 95 | "@tiptap/extension-image": "^2.1.13", 96 | "@tiptap/extension-link": "^2.1.13", 97 | "@tiptap/extension-placeholder": "^2.1.13", 98 | "@tiptap/extension-task-item": "^2.26.1", 99 | "@tiptap/extension-task-list": "^2.26.1", 100 | "@tiptap/extension-underline": "^2.26.1", 101 | "@tiptap/pm": "^2.1.13", 102 | "@tiptap/starter-kit": "^2.1.13", 103 | "iconify-icon": "^1.0.8", 104 | "lowlight": "^2.9.0" 105 | }, 106 | "overrides": {} 107 | } 108 | -------------------------------------------------------------------------------- /src/fonts/work-sans.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'work-sans'; 3 | font-style: normal; 4 | font-weight: 900; 5 | src: 6 | local('sans-serif'), 7 | url('./work-sans/WorkSans-Black.ttf') format('truetype'); 8 | font-display: swap; 9 | } 10 | 11 | @font-face { 12 | font-family: 'work-sans'; 13 | font-style: italic; 14 | font-weight: 900; 15 | src: 16 | local('sans-serif'), 17 | url('./work-sans/WorkSans-BlackItalic.ttf') format('truetype'); 18 | font-display: swap; 19 | } 20 | 21 | @font-face { 22 | font-family: 'work-sans'; 23 | font-style: normal; 24 | font-weight: 700; 25 | src: 26 | local('sans-serif'), 27 | url('./work-sans/WorkSans-Bold.ttf') format('truetype'); 28 | font-display: swap; 29 | } 30 | 31 | @font-face { 32 | font-family: 'work-sans'; 33 | font-style: italic; 34 | font-weight: 700; 35 | src: 36 | local('sans-serif'), 37 | url('./work-sans/WorkSans-BoldItalic.ttf') format('truetype'); 38 | font-display: swap; 39 | } 40 | 41 | @font-face { 42 | font-family: 'work-sans'; 43 | font-style: normal; 44 | font-weight: 800; 45 | src: 46 | local('sans-serif'), 47 | url('./work-sans/WorkSans-ExtraBold.ttf') format('truetype'); 48 | font-display: swap; 49 | } 50 | 51 | @font-face { 52 | font-family: 'work-sans'; 53 | font-style: italic; 54 | font-weight: 800; 55 | src: 56 | local('sans-serif'), 57 | url('./work-sans/WorkSans-ExtraBoldItalic.ttf') format('truetype'); 58 | font-display: swap; 59 | } 60 | 61 | @font-face { 62 | font-family: 'work-sans'; 63 | font-style: normal; 64 | font-weight: 200; 65 | src: 66 | local('sans-serif'), 67 | url('./work-sans/WorkSans-ExtraLight.ttf') format('truetype'); 68 | font-display: swap; 69 | } 70 | 71 | @font-face { 72 | font-family: 'work-sans'; 73 | font-style: italic; 74 | font-weight: 200; 75 | src: 76 | local('sans-serif'), 77 | url('./work-sans/WorkSans-ExtraLightItalic.ttf') format('truetype'); 78 | font-display: swap; 79 | } 80 | 81 | @font-face { 82 | font-family: 'work-sans'; 83 | font-style: italic; 84 | font-weight: 400; 85 | src: 86 | local('sans-serif'), 87 | url('./work-sans/WorkSans-Italic.ttf') format('truetype'); 88 | font-display: swap; 89 | } 90 | 91 | @font-face { 92 | font-family: 'work-sans'; 93 | font-style: normal; 94 | font-weight: 300; 95 | src: 96 | local('sans-serif'), 97 | url('./work-sans/WorkSans-Light.ttf') format('truetype'); 98 | font-display: swap; 99 | } 100 | 101 | @font-face { 102 | font-family: 'work-sans'; 103 | font-style: italic; 104 | font-weight: 300; 105 | src: 106 | local('sans-serif'), 107 | url('./work-sans/WorkSans-LightItalic.ttf') format('truetype'); 108 | font-display: swap; 109 | } 110 | 111 | @font-face { 112 | font-family: 'work-sans'; 113 | font-style: normal; 114 | font-weight: 500; 115 | src: 116 | local('sans-serif'), 117 | url('./work-sans/WorkSans-Medium.ttf') format('truetype'); 118 | font-display: swap; 119 | } 120 | 121 | @font-face { 122 | font-family: 'work-sans'; 123 | font-style: italic; 124 | font-weight: 500; 125 | src: 126 | local('sans-serif'), 127 | url('./work-sans/WorkSans-MediumItalic.ttf') format('truetype'); 128 | font-display: swap; 129 | } 130 | 131 | @font-face { 132 | font-family: 'work-sans'; 133 | font-style: normal; 134 | font-weight: 400; 135 | src: 136 | local('sans-serif'), 137 | url('./work-sans/WorkSans-Regular.ttf') format('truetype'); 138 | font-display: swap; 139 | } 140 | 141 | @font-face { 142 | font-family: 'work-sans'; 143 | font-style: normal; 144 | font-weight: 600; 145 | src: 146 | local('sans-serif'), 147 | url('./work-sans/WorkSans-SemiBold.ttf') format('truetype'); 148 | font-display: swap; 149 | } 150 | 151 | @font-face { 152 | font-family: 'work-sans'; 153 | font-style: italic; 154 | font-weight: 600; 155 | src: 156 | local('sans-serif'), 157 | url('./work-sans/WorkSans-SemiBoldItalic.ttf') format('truetype'); 158 | font-display: swap; 159 | } 160 | 161 | @font-face { 162 | font-family: 'work-sans'; 163 | font-style: normal; 164 | font-weight: 100; 165 | src: 166 | local('sans-serif'), 167 | url('./work-sans/WorkSans-Thin.ttf') format('truetype'); 168 | font-display: swap; 169 | } 170 | 171 | @font-face { 172 | font-family: 'work-sans'; 173 | font-style: italic; 174 | font-weight: 100; 175 | src: 176 | local('sans-serif'), 177 | url('./work-sans/WorkSans-ThinItalic.ttf') format('truetype'); 178 | font-display: swap; 179 | } 180 | -------------------------------------------------------------------------------- /src/lib/tipex/Tipex.svelte: -------------------------------------------------------------------------------- 1 | 61 | 62 | 128 | 129 | 130 | 131 | {#if floating} 132 | 133 | {/if} 134 | 135 |
142 |
143 | {@render head?.(tipex)} 144 |
145 | {#if controlComponent} 146 | {@render controlComponent(tipex)} 147 | {:else if controlComponent !== null} 148 | 149 | 150 | {/if} 151 | {@render foot?.(tipex)} 152 |
153 |
154 | -------------------------------------------------------------------------------- /src/lib/tipex/styles/tipex.css: -------------------------------------------------------------------------------- 1 | .tipex-editor { 2 | @apply flex flex-row gap-0 rounded-lg py-0 duration-200 overflow-y-hidden bg-tipex-50/80 dark:bg-tipex-900/80 border border-tipex-200/80 dark:border-tipex-700/80; 3 | transition: 4 | border-color 0.2s ease-in-out, 5 | box-shadow 0.2s ease-in-out; 6 | backdrop-filter: blur(12px); 7 | } 8 | 9 | .tipex-editor.focused.focal { 10 | @apply outline-none border-tipex-300/60 dark:border-tipex-600/60; 11 | box-shadow: 0 0 0 2px rgb(var(--color-tipex-500) / 0.08); 12 | } 13 | 14 | .tipex-editor-wrap { 15 | @apply flex flex-col gap-0 h-full w-full; 16 | } 17 | 18 | .tipex-editor-section { 19 | @apply px-tipex-sm py-tipex-md h-full overflow-y-auto; 20 | } 21 | 22 | /* Control Components */ 23 | .tipex-controller { 24 | @apply flex flex-row flex-wrap md:flex-row gap-2 sticky bottom-0 py-2 px-3 rounded-b-lg bg-tipex-100/70 dark:bg-tipex-800/70 border-t border-tipex-200/80 dark:border-tipex-700/80 backdrop-blur-md relative; 25 | z-index: var(--z-tipex-controls); 26 | } 27 | 28 | .tipex-basic-controller-wrapper { 29 | @apply flex gap-0.5 flex-wrap items-center; 30 | } 31 | 32 | .tipex-edit-button { 33 | @apply inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-tipex-500 disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed dark:focus-visible:ring-tipex-400 bg-transparent hover:bg-tipex-200/80 hover:text-tipex-800 dark:hover:bg-tipex-700/80 dark:hover:text-tipex-200 text-tipex-600 dark:text-tipex-400 cursor-pointer; 34 | height: var(--size-tipex-9); 35 | padding-left: var(--size-tipex-3); 36 | padding-right: var(--size-tipex-3); 37 | } 38 | 39 | .tipex-button-rigid { 40 | min-width: var(--size-tipex-9); 41 | } 42 | 43 | .tipex-button-extra { 44 | /* Additional button styling can go here */ 45 | } 46 | 47 | .tipex-edit-button.active { 48 | @apply bg-tipex-200/90 text-tipex-800 dark:bg-tipex-700/90 dark:text-tipex-200; 49 | } 50 | 51 | /* Copy success state - more specific selector */ 52 | .tipex-utilities .tipex-edit-button.active { 53 | @apply bg-tipex-success-50/80 text-tipex-success-600 dark:bg-tipex-success-900/60 dark:text-tipex-success-400; 54 | } 55 | 56 | .tipex-utilities .tipex-edit-button.active svg { 57 | @apply text-tipex-success-600 dark:text-tipex-success-400; 58 | } 59 | 60 | /* Link button active state - more visible */ 61 | .tipex-utilities .tipex-edit-button[aria-label='Edit link'].active { 62 | @apply bg-tipex-primary-100/90 text-tipex-primary-700 dark:bg-tipex-primary-900/80 dark:text-tipex-primary-300 border border-tipex-primary-200 dark:border-tipex-primary-700; 63 | } 64 | 65 | .tipex-utilities .tipex-edit-button[aria-label='Edit link'].active svg { 66 | @apply text-tipex-primary-700 dark:text-tipex-primary-300; 67 | } 68 | 69 | .tipex-edit-button:disabled { 70 | @apply opacity-50 cursor-not-allowed; 71 | } 72 | 73 | .tipex-edit-button:disabled:hover { 74 | @apply bg-transparent text-tipex-600 dark:text-tipex-400; 75 | transform: none; 76 | } 77 | 78 | /* Floating Menu */ 79 | .tipex-floating-menu { 80 | @apply rounded-lg p-2 gap-1 bg-white/95 dark:bg-tipex-900/95 border border-tipex-200/90 dark:border-tipex-700/90 shadow-lg; 81 | z-index: var(--z-tipex-floating); 82 | animation: tipex-float-in 0.15s ease-out; 83 | backdrop-filter: blur(8px); 84 | } 85 | 86 | .tipex-floating-group { 87 | @apply flex items-center gap-1 p-2 rounded-lg bg-white/95 dark:bg-tipex-900/95 border border-tipex-200/90 dark:border-tipex-700/90 shadow-lg; 88 | z-index: var(--z-tipex-floating); 89 | animation: tipex-float-in 0.15s ease-out; 90 | backdrop-filter: blur(8px); 91 | } 92 | 93 | .tipex-floating-button { 94 | @apply inline-flex items-center justify-center rounded-md text-tipex-600 dark:text-tipex-300 hover:bg-tipex-100/80 dark:hover:bg-tipex-700/80 hover:text-tipex-800 dark:hover:text-tipex-100 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-tipex-primary-500/50; 95 | width: var(--size-tipex-8); 96 | height: var(--size-tipex-8); 97 | } 98 | 99 | @keyframes tipex-float-in { 100 | from { 101 | opacity: 0; 102 | transform: translateY(2px) scale(0.98); 103 | } 104 | to { 105 | opacity: 1; 106 | transform: translateY(0) scale(1); 107 | } 108 | } 109 | 110 | /* Utilities Section */ 111 | .tipex-utilities { 112 | @apply flex gap-0.5 items-center ml-auto; 113 | } 114 | 115 | .tipex-utilities .tipex-edit-button { 116 | @apply opacity-75 hover:opacity-100 transition-opacity duration-200; 117 | } 118 | 119 | .tipex-utilities .tipex-edit-button:hover { 120 | @apply bg-tipex-200/80 dark:bg-tipex-700/80; 121 | } 122 | -------------------------------------------------------------------------------- /src/routes/commands/+page.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | Editor Commands & API Reference | Tipex Editor 9 | 13 | 14 | 15 | 19 | 20 | Back to Home 21 | 22 | 23 |

Editor Commands & API Reference

24 | 25 |

26 | Tipex provides comprehensive access to TipTap's powerful command system, enabling full 27 | programmatic control over the editor. This guide covers essential commands, advanced techniques, 28 | and best practices for integrating editor functionality into your applications. 29 |

30 | 31 |

Getting Started with Commands

32 |

33 | All commands are accessed through the editor instance. First, ensure you have a reference to the 34 | editor: 35 |

36 | 37 | 38 | 39 |

Content Management

40 |

These commands handle basic content operations:

41 | 42 | 43 | 44 |

Text Formatting

45 |

Apply and remove text formatting with these commands:

46 | 47 | 48 | 49 |

List Management

50 |

Create and manage different types of lists:

51 | 52 | 53 | 54 |

Link Management

55 |

Handle links with validation and custom attributes:

56 | 57 | 58 | 59 |

Image Handling

60 |

Comprehensive image management including uploads, validation, and optimization:

61 | 62 | 63 | 64 |

Selection & Cursor Management

65 |

Control text selection and cursor positioning:

66 | 67 | 68 | 69 |

History & Undo/Redo

70 |

Manage editor history and undo/redo functionality:

71 | 72 | 73 | 74 |

Advanced Command Patterns

75 |

Leverage command chaining and conditional execution for complex operations:

76 | 77 | 78 | 79 |

Custom Commands

80 |

Create your own commands for specialized functionality:

81 | 82 | 83 | 84 |

Event-Driven Commands

85 |

React to editor events and execute commands automatically:

86 | 87 | 88 | 89 |

Best Practices

90 |
    91 |
  • 92 | Always chain focus(): Use editor.chain().focus().command().run() to 93 | ensure proper focus management 94 |
  • 95 |
  • 96 | Check command availability: Use editor.can().command() before executing 97 | commands 98 |
  • 99 |
  • 100 | Error handling: Wrap commands in try-catch blocks for production applications 101 |
  • 102 |
  • 103 | Performance: Batch multiple commands using chain() instead of individual 104 | calls 105 |
  • 106 |
  • 107 | State management: Use editor.isActive() to maintain UI state consistency 108 |
  • 109 |
  • 110 | Validation: Always validate user input before executing commands (URLs, file types, 111 | etc.) 112 |
  • 113 |
114 | 115 |
116 |

117 | Complete API Reference: For the full list of available commands and their 118 | parameters, visit the 119 | 125 | official TipTap Commands API Documentation. Tipex provides 100% compatibility with all TipTap commands. 127 |

128 |
129 | -------------------------------------------------------------------------------- /src/lib/tipex/styles/prosemirror.css: -------------------------------------------------------------------------------- 1 | @layer components { 2 | .tipex-editor-section .ProseMirror { 3 | @apply outline-none px-tipex-lg pb-tipex-sm h-full text-tipex-900 dark:text-tipex-100; 4 | } 5 | 6 | /* Custom scrollbar styling for the editor section */ 7 | .tipex-editor-section { 8 | scrollbar-width: thin; 9 | scrollbar-color: rgb(var(--color-tipex-300)) transparent; 10 | } 11 | 12 | .tipex-editor-section::-webkit-scrollbar { 13 | width: var(--size-tipex-2); 14 | } 15 | 16 | .tipex-editor-section::-webkit-scrollbar-track { 17 | background: transparent; 18 | } 19 | 20 | .tipex-editor-section::-webkit-scrollbar-thumb { 21 | background-color: rgb(var(--color-tipex-300)); 22 | border-radius: var(--radius-tipex-sm); 23 | border: 2px solid transparent; 24 | background-clip: content-box; 25 | } 26 | 27 | .tipex-editor-section::-webkit-scrollbar-thumb:hover { 28 | background-color: rgb(var(--color-tipex-400)); 29 | } 30 | 31 | /* Dark mode scrollbar */ 32 | .dark .tipex-editor-section { 33 | scrollbar-color: rgb(var(--color-tipex-600)) transparent; 34 | } 35 | 36 | .dark .tipex-editor-section::-webkit-scrollbar-thumb { 37 | background-color: rgb(var(--color-tipex-600)); 38 | } 39 | 40 | .dark .tipex-editor-section::-webkit-scrollbar-thumb:hover { 41 | background-color: rgb(var(--color-tipex-500)); 42 | } 43 | 44 | .tipex-editor-section .ProseMirror pre { 45 | @apply bg-tipex-100 text-tipex-600 text-tipex-sm dark:bg-tipex-800 dark:text-tipex-400 px-tipex-xl py-tipex-lg mt-tipex-lg ml-tipex-md mr-tipex-xl overflow-x-hidden mb-tipex-lg rounded-tipex-md; 46 | } 47 | 48 | .tipex-editor-section .ProseMirror code { 49 | @apply bg-tipex-100 text-tipex-600 dark:bg-tipex-800 dark:text-tipex-400 px-tipex-sm py-tipex-xs rounded-tipex-sm; 50 | } 51 | 52 | .tipex-editor-section .ProseMirror h1 { 53 | @apply text-tipex-2xl font-black text-tipex-900 dark:text-tipex-100 my-tipex-lg; 54 | } 55 | 56 | .tipex-editor-section .ProseMirror h2 { 57 | @apply text-tipex-xl font-bold my-tipex-md; 58 | } 59 | 60 | .tipex-editor-section .ProseMirror h3 { 61 | @apply text-tipex-lg font-semibold my-tipex-sm; 62 | } 63 | 64 | .tipex-editor-section .ProseMirror img[src] { 65 | @apply mb-tipex-md; 66 | } 67 | 68 | .tipex-editor-section .ProseMirror h4, 69 | .tipex-editor-section .ProseMirror h5, 70 | .tipex-editor-section .ProseMirror h6 { 71 | @apply text-tipex-base font-medium mb-tipex-sm; 72 | } 73 | 74 | .tipex-editor-section .ProseMirror p { 75 | @apply text-tipex-base mb-tipex-md; 76 | } 77 | 78 | .tipex-editor-section .ProseMirror a[href] { 79 | @apply text-tipex-600 hover:underline hover:text-tipex-700 dark:text-tipex-400 dark:hover:text-tipex-300; 80 | } 81 | 82 | .tipex-editor-section .ProseMirror ul { 83 | @apply list-disc ml-tipex-2xl; 84 | } 85 | 86 | .tipex-editor-section .ProseMirror ol { 87 | @apply list-decimal ml-tipex-2xl; 88 | } 89 | 90 | .tipex-editor-section .ProseMirror li { 91 | @apply mb-tipex-sm; 92 | } 93 | 94 | /* Task List Styling */ 95 | .tipex-editor-section .ProseMirror ul[data-type='taskList'] { 96 | @apply list-none ml-0 p-4 border border-tipex-200 dark:border-tipex-700 rounded-lg bg-tipex-50/50 dark:bg-tipex-800/50 backdrop-blur-sm; 97 | } 98 | 99 | .tipex-editor-section .ProseMirror ul[data-type='taskList'] li { 100 | @apply flex items-center gap-3 mb-tipex-sm; 101 | } 102 | 103 | .tipex-editor-section .ProseMirror ul[data-type='taskList'] li:last-child { 104 | @apply mb-0; 105 | } 106 | 107 | .tipex-editor-section .ProseMirror ul[data-type='taskList'] li > label { 108 | @apply flex items-center justify-center cursor-pointer transition-colors duration-200; 109 | width: var(--size-tipex-5); 110 | height: var(--size-tipex-5); 111 | } 112 | 113 | .tipex-editor-section .ProseMirror ul[data-type='taskList'] li[data-checked='true'] > div { 114 | @apply line-through text-tipex-400 dark:text-tipex-500; 115 | } 116 | 117 | .tipex-editor-section .ProseMirror ul[data-type='taskList'] li > label input[type='checkbox'] { 118 | @apply accent-tipex-600 dark:accent-tipex-400; 119 | width: var(--size-tipex-3); 120 | height: var(--size-tipex-3); 121 | } 122 | 123 | .tipex-editor-section .ProseMirror ul[data-type='taskList'] li > div { 124 | @apply flex-1; 125 | } 126 | 127 | .tipex-editor-section .ProseMirror blockquote { 128 | @apply border-l-4 border-tipex-200 dark:border-tipex-700 pl-tipex-md mb-tipex-md ml-tipex-md; 129 | } 130 | 131 | .tipex-editor-section .ProseMirror img { 132 | @apply max-w-full; 133 | } 134 | 135 | .tipex-editor-section .ProseMirror hr { 136 | @apply border border-tipex-200 dark:border-tipex-700; 137 | } 138 | 139 | .tipex-editor-section .ProseMirror del { 140 | @apply line-through; 141 | } 142 | 143 | .tipex-editor-section .ProseMirror em { 144 | @apply italic; 145 | } 146 | 147 | .tipex-editor-section .ProseMirror strong { 148 | @apply font-bold; 149 | } 150 | 151 | .tipex-editor-section .ProseMirror .is-empty::before { 152 | content: attr(data-placeholder); 153 | float: left; 154 | height: 0; 155 | @apply pointer-events-none text-tipex-400 dark:text-tipex-500; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/lib/tipex/styles/codeblock.css: -------------------------------------------------------------------------------- 1 | @layer components { 2 | pre code.hljs { 3 | display: block; 4 | overflow-x: auto; 5 | padding: var(--spacing-tipex-md); 6 | @apply rounded-tipex-md; 7 | } 8 | 9 | code.hljs { 10 | padding: var(--spacing-tipex-xs) var(--spacing-tipex-sm); 11 | @apply rounded-tipex-sm; 12 | } 13 | 14 | /* Light theme - use tipex theme colors for base */ 15 | .hljs { 16 | color: var(--color-tipex-editor-text); 17 | background: var(--color-tipex-code-bg); 18 | } 19 | 20 | /* Dark theme - use tipex theme colors for base */ 21 | .dark .hljs { 22 | color: var(--color-tipex-editor-text-dark); 23 | background: var(--color-tipex-code-bg-dark); 24 | } 25 | } 26 | 27 | /*! 28 | Theme: GitHub 29 | Description: Light theme as seen on github.com 30 | Author: github.com 31 | Maintainer: @Hirse 32 | Updated: 2021-05-15 33 | 34 | Outdated base version: https://github.com/primer/github-syntax-light 35 | Current colors taken from GitHub's CSS 36 | */ 37 | .hljs-doctag, 38 | .hljs-keyword, 39 | .hljs-meta .hljs-keyword, 40 | .hljs-template-tag, 41 | .hljs-template-variable, 42 | .hljs-type, 43 | .hljs-variable.language_ { 44 | /* prettylights-syntax-keyword */ 45 | color: #d73a49; 46 | } 47 | 48 | .hljs-title, 49 | .hljs-title.class_, 50 | .hljs-title.class_.inherited__, 51 | .hljs-title.function_ { 52 | /* prettylights-syntax-entity */ 53 | color: #6f42c1; 54 | } 55 | 56 | .hljs-attr, 57 | .hljs-attribute, 58 | .hljs-literal, 59 | .hljs-meta, 60 | .hljs-number, 61 | .hljs-operator, 62 | .hljs-variable, 63 | .hljs-selector-attr, 64 | .hljs-selector-class, 65 | .hljs-selector-id { 66 | /* prettylights-syntax-constant */ 67 | color: #005cc5; 68 | } 69 | 70 | .hljs-regexp, 71 | .hljs-string, 72 | .hljs-meta .hljs-string { 73 | /* prettylights-syntax-string */ 74 | color: #032f62; 75 | } 76 | 77 | .hljs-built_in, 78 | .hljs-symbol { 79 | /* prettylights-syntax-variable */ 80 | color: #e36209; 81 | } 82 | 83 | .hljs-comment, 84 | .hljs-code, 85 | .hljs-formula { 86 | /* prettylights-syntax-comment */ 87 | color: #6a737d; 88 | } 89 | 90 | .hljs-name, 91 | .hljs-quote, 92 | .hljs-selector-tag, 93 | .hljs-selector-pseudo { 94 | /* prettylights-syntax-entity-tag */ 95 | color: #22863a; 96 | } 97 | 98 | .hljs-subst { 99 | /* prettylights-syntax-storage-modifier-import */ 100 | color: #24292e; 101 | } 102 | 103 | .hljs-section { 104 | /* prettylights-syntax-markup-heading */ 105 | color: #005cc5; 106 | font-weight: bold; 107 | } 108 | 109 | .hljs-bullet { 110 | /* prettylights-syntax-markup-list */ 111 | color: #735c0f; 112 | } 113 | 114 | .hljs-emphasis { 115 | /* prettylights-syntax-markup-italic */ 116 | color: #24292e; 117 | font-style: italic; 118 | } 119 | 120 | .hljs-strong { 121 | /* prettylights-syntax-markup-bold */ 122 | color: #24292e; 123 | font-weight: bold; 124 | } 125 | 126 | .hljs-addition { 127 | /* prettylights-syntax-markup-inserted */ 128 | color: #22863a; 129 | background-color: #f0fff4; 130 | } 131 | 132 | .hljs-deletion { 133 | /* prettylights-syntax-markup-deleted */ 134 | color: #b31d28; 135 | background-color: #ffeef0; 136 | } 137 | 138 | .hljs-char.escape_, 139 | .hljs-link, 140 | .hljs-params, 141 | .hljs-property, 142 | .hljs-punctuation, 143 | .hljs-tag { 144 | /* purposely ignored */ 145 | } 146 | 147 | /*! 148 | Theme: GitHub Dark Dimmed 149 | Description: Dark dimmed theme as seen on github.com 150 | Author: github.com 151 | Maintainer: @Hirse 152 | Updated: 2021-05-15 153 | 154 | Colors taken from GitHub's CSS 155 | */ 156 | .dark .hljs-doctag, 157 | .dark .hljs-keyword, 158 | .dark .hljs-meta .hljs-keyword, 159 | .dark .hljs-template-tag, 160 | .dark .hljs-template-variable, 161 | .dark .hljs-type, 162 | .dark .hljs-variable.language_ { 163 | /* prettylights-syntax-keyword */ 164 | color: #f47067; 165 | } 166 | 167 | .dark .hljs-title, 168 | .dark .hljs-title.class_, 169 | .dark .hljs-title.class_.inherited__, 170 | .dark .hljs-title.function_ { 171 | /* prettylights-syntax-entity */ 172 | color: #dcbdfb; 173 | } 174 | 175 | .dark .hljs-attr, 176 | .dark .hljs-attribute, 177 | .dark .hljs-literal, 178 | .dark .hljs-meta, 179 | .dark .hljs-number, 180 | .dark .hljs-operator, 181 | .dark .hljs-variable, 182 | .dark .hljs-selector-attr, 183 | .dark .hljs-selector-class, 184 | .dark .hljs-selector-id { 185 | /* prettylights-syntax-constant */ 186 | color: #6cb6ff; 187 | } 188 | 189 | .dark .hljs-regexp, 190 | .dark .hljs-string, 191 | .dark .hljs-meta .hljs-string { 192 | /* prettylights-syntax-string */ 193 | color: #96d0ff; 194 | } 195 | 196 | .dark .hljs-built_in, 197 | .dark .hljs-symbol { 198 | /* prettylights-syntax-variable */ 199 | color: #f69d50; 200 | } 201 | 202 | .dark .hljs-comment, 203 | .dark .hljs-code, 204 | .dark .hljs-formula { 205 | /* prettylights-syntax-comment */ 206 | color: #768390; 207 | } 208 | 209 | .dark .hljs-name, 210 | .dark .hljs-quote, 211 | .dark .hljs-selector-tag, 212 | .dark .hljs-selector-pseudo { 213 | /* prettylights-syntax-entity-tag */ 214 | color: #57ab5a; 215 | } 216 | 217 | .dark .hljs-subst { 218 | /* prettylights-syntax-storage-modifier-import */ 219 | color: #adbac7; 220 | } 221 | 222 | .dark .hljs-section { 223 | /* prettylights-syntax-markup-heading */ 224 | color: #6cb6ff; 225 | font-weight: bold; 226 | } 227 | 228 | .dark .hljs-bullet { 229 | /* prettylights-syntax-markup-list */ 230 | color: #c69026; 231 | } 232 | 233 | .dark .hljs-emphasis { 234 | /* prettylights-syntax-markup-italic */ 235 | color: #adbac7; 236 | font-style: italic; 237 | } 238 | 239 | .dark .hljs-strong { 240 | /* prettylights-syntax-markup-bold */ 241 | color: #adbac7; 242 | font-weight: bold; 243 | } 244 | 245 | .dark .hljs-addition { 246 | /* prettylights-syntax-markup-inserted */ 247 | color: #57ab5a; 248 | background-color: #2ea04326; 249 | } 250 | 251 | .dark .hljs-deletion { 252 | /* prettylights-syntax-markup-deleted */ 253 | color: #e5534b; 254 | background-color: #da363333; 255 | } 256 | 257 | .dark .hljs-char.escape_, 258 | .dark .hljs-link, 259 | .dark .hljs-params, 260 | .dark .hljs-property, 261 | .dark .hljs-punctuation, 262 | .dark .hljs-tag { 263 | /* purposely ignored */ 264 | } 265 | -------------------------------------------------------------------------------- /src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | Tipex Editor - Svelte Text Editor | Friend Of Svelte 21 | 25 | 26 | 27 | 71 | 72 |

73 | Tipex is a cutting-edge rich text editor designed specifically for Svelte applications. Built on 74 | the powerful foundations of 75 | TipTap and 76 | ProseMirror, 77 | Tipex provides developers with an intuitive, highly customizable editing experience while 78 | abstracting away the complexity of underlying technologies. Whether you're building a simple blog 79 | or a complex content management system, Tipex scales to meet your needs. 80 |

81 | 82 | 83 | 84 |

Key Features

85 |
    86 |
  • 87 | Rich Text Editing: Full support for formatting, links, lists, images, and code blocks 88 |
  • 89 |
  • 90 | Highly Customizable: Extensive theming and component customization options 91 |
  • 92 |
  • 93 | Svelte Native: Built specifically for Svelte with reactive bindings and modern svelte 94 | 5 runes syntax 95 |
  • 96 |
  • 97 | Accessible: WCAG compliant with keyboard navigation and screen reader support 98 |
  • 99 |
  • Extensible: Plugin architecture allows for custom functionality
  • 100 |
  • 101 | TypeScript Ready: Full TypeScript support with comprehensive type definitions 102 |
  • 103 |
104 | 105 |

Installation

106 |

107 | Install Tipex from NPM using your preferred package manager. 112 |

113 | 114 | 115 |

Examples & Variants

116 |

117 | Check out Tipex Editor Variants 122 | and its 123 | live preview 124 | for examples showcasing what you can build with Tipex, including multiple themes and configurations. 125 |

126 | 127 |

Quick Start

128 |

129 | Get started with Tipex in just a few lines of code. Import the component and its styles, then use 130 | it in your Svelte application. 131 |

132 | 133 | 134 |

Configuration Options

135 |

Tipex provides several boolean props for quick configuration:

136 |
    137 |
  • 138 | floating: Enable/disable the floating selection toolbar (!floating to disable) 139 |
  • 140 |
  • 141 | focal: Enable/disable focus ring styling (!focal to disable) 142 |
  • 143 |
144 | 145 |

Styling & Theming

146 |

147 | Tipex is built with Tailwind CSS v4 and uses the modern `@import "tailwindcss"` syntax 148 | along with the new `@theme` configuration system. Import the CSS file to get started with beautiful, 149 | responsive styling powered by the latest Tailwind architecture. 150 |

151 | 152 | 153 |
154 |

155 | Tailwind v4 Architecture: Tipex leverages Tailwind CSS v4's advanced features including 156 | the new `@theme` configuration, CSS custom properties integration, and the modern import system. 157 | The styles use `@layer components` for organization, CSS custom properties for theming (`--color-tipex-*`), 158 | and the new `@custom-variant` syntax for dark mode handling. This provides superior performance, 159 | better DX, and more flexible customization compared to older Tailwind versions. 160 |

161 |
162 | 163 |

Props & Configuration

164 |

165 | Tipex accepts a comprehensive set of props for customization and configuration. Here are the 166 | available options: 167 |

168 | 169 | 170 |

Editor Instance Access

171 |

172 | Access the TipTap editor instance to programmatically control the editor, execute commands, or 173 | listen to events. 174 |

175 | 176 |

177 | The editor instance provides access to the full TipTap API, including commands for content 178 | manipulation, state management, and event handling. This allows for advanced integrations and 179 | custom functionality. 180 |

181 | 182 |

Advanced Customization

183 |

184 | Tipex is architected with customization at its core. Every aspect of the editor can be tailored to 185 | your needs: 186 |

187 |
    188 |
  • 189 | Custom Controls: Replace or extend the default toolbar with your own components 190 |
  • 191 |
  • 192 | Theme Customization: Comprehensive CSS custom properties for colors, spacing, and 193 | typography 194 |
  • 195 |
  • 196 | Extension System: Add new functionality through TipTap's extension architecture 197 |
  • 198 |
  • Event Handling: React to editor events for custom workflows
  • 199 |
  • Content Validation: Implement custom validation and sanitization logic
  • 200 |
201 |

202 | Visit the customization guide for 203 | detailed examples and advanced techniques. 204 |

205 | 206 |

Commands & API

207 |

208 | Leverage the powerful command system to programmatically interact with the editor. Tipex provides 209 | full compatibility with TipTap's command API, enabling: 212 |

213 |
    214 |
  • Content insertion and manipulation
  • 215 |
  • Formatting and styling operations
  • 216 |
  • Selection and cursor management
  • 217 |
  • Undo/redo functionality
  • 218 |
  • Custom command chaining
  • 219 |
220 |

221 | Explore the commands documentation for 222 | comprehensive examples and use cases. 223 |

224 | 225 |

Performance & Accessibility

226 |

Tipex is optimized for performance and accessibility:

227 |
    228 |
  • Lazy Loading: Components and extensions load only when needed
  • 229 |
  • Virtual Scrolling: Efficient handling of large documents
  • 230 |
  • ARIA Support: Full screen reader compatibility
  • 231 |
  • Keyboard Navigation: Complete keyboard control for all features
  • 232 |
  • Focus Management: Proper focus handling for complex interactions
  • 233 |
234 | 235 |

About Friend Of Svelte

236 |
237 | Friend Of Svelte Logo 242 |

243 | Friend Of Svelte is 244 | a community-driven organization dedicated to creating high-quality, open-source tools and resources 245 | for the Svelte ecosystem. Our mission is to empower developers with exceptional tools that enhance 246 | productivity and developer experience. 247 |

248 |
249 | 250 |

251 | Join our growing community of contributors and help shape the future of Svelte development. We 252 | welcome contributions of all kinds - from code and documentation to feedback and feature requests. 253 |

254 | 255 |
256 |

257 | Developer Story: Learn about the journey of creating Tipex and becoming a 258 | master Svelte developer in our founder's blog. 263 |

264 |
265 | 266 | 268 | -------------------------------------------------------------------------------- /src/item/codes/commandsCodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "basicEditorSetup": "// Get editor reference\nlet editor: Editor;\n\n// In your component\n\n\n// Now you can use commands\neditor.commands.setContent('

Hello World

')", 3 | 4 | "contentManagement": "// Set entire editor content\neditor.commands.setContent('

Hello World

')\neditor.commands.setContent(htmlString, true) // true to emit update event\n\n// Insert content at current cursor position\neditor.commands.insertContent('New content')\neditor.commands.insertContent('

HTML content

')\n\n// Insert content at specific position\neditor.commands.insertContentAt(10, 'Content at position 10')\n\n// Clear all content\neditor.commands.clearContent()\neditor.commands.clearContent(true) // true to emit update event\n\n// Focus the editor\neditor.commands.focus()\neditor.commands.focus('start') // Focus at start\neditor.commands.focus('end') // Focus at end\neditor.commands.focus(10) // Focus at specific position\n\n// Blur (unfocus) the editor\neditor.commands.blur()", 5 | 6 | "textFormatting": "// Basic formatting toggles (all available in enhanced Controls)\neditor.commands.toggleBold()\neditor.commands.toggleItalic()\neditor.commands.toggleUnderline() // Now supported with extension\neditor.commands.toggleStrike()\neditor.commands.toggleCode()\n\n// Set formatting (without toggle)\neditor.commands.setBold()\neditor.commands.setItalic()\neditor.commands.setUnderline()\neditor.commands.unsetBold()\neditor.commands.unsetItalic()\neditor.commands.unsetUnderline()\n\n// Heading management (H1, H2, H3 buttons available)\neditor.commands.toggleHeading({ level: 1 })\neditor.commands.toggleHeading({ level: 2 })\neditor.commands.toggleHeading({ level: 3 })\neditor.commands.setHeading({ level: 1 })\neditor.commands.unsetHeading()\n\n// Paragraph operations\neditor.commands.setParagraph()\n\n// Check active formatting states\nconst isBold = editor.isActive('bold')\nconst isItalic = editor.isActive('italic')\nconst isUnderline = editor.isActive('underline')\nconst isH1 = editor.isActive('heading', { level: 1 })\nconst isH2 = editor.isActive('heading', { level: 2 })", 7 | 8 | "listManagement": "// Toggle list types (all available in enhanced Controls)\neditor.commands.toggleBulletList() // Bullet list button\neditor.commands.toggleOrderedList() // Numbered list button \neditor.commands.toggleTaskList() // Task list button\n\n// Set specific list types\neditor.commands.setBulletList()\neditor.commands.setOrderedList()\neditor.commands.setTaskList()\n\n// List item operations\neditor.commands.splitListItem('listItem')\neditor.commands.sinkListItem('listItem') // Indent\neditor.commands.liftListItem('listItem') // Outdent\n\n// Task list specific\neditor.commands.toggleTask()\neditor.commands.setTaskList()\n\n// Check active list states\nconst isBulletList = editor.isActive('bulletList')\nconst isOrderedList = editor.isActive('orderedList')\nconst isTaskList = editor.isActive('taskList')\n\n// Example: Smart list button with state\nfunction createListButton(listType: 'bulletList' | 'orderedList' | 'taskList') {\n return {\n isActive: () => editor.isActive(listType),\n toggle: () => {\n switch(listType) {\n case 'bulletList':\n editor.chain().focus().toggleBulletList().run()\n break\n case 'orderedList':\n editor.chain().focus().toggleOrderedList().run()\n break\n case 'taskList':\n editor.chain().focus().toggleTaskList().run()\n break\n }\n }\n }\n}", 9 | 10 | "linkManagement": "// Basic link operations\neditor.commands.setLink({ href: 'https://example.com' })\neditor.commands.setLink({ \n href: 'https://example.com',\n target: '_blank',\n rel: 'noopener noreferrer'\n})\neditor.commands.unsetLink()\n\n// Advanced link handling with validation\nfunction setLinkWithValidation(url: string, text?: string) {\n // Validate URL\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\n url = 'https://' + url\n }\n \n try {\n new URL(url) // Validate URL format\n \n if (text) {\n // Insert text with link\n editor.chain()\n .focus()\n .insertContent(`${text}`)\n .run()\n } else {\n // Apply link to selection\n editor.chain()\n .focus()\n .setLink({ \n href: url, \n target: '_blank', \n rel: 'noopener noreferrer' \n })\n .run()\n }\n } catch (error) {\n console.error('Invalid URL:', url)\n }\n}\n\n// Check if current selection/cursor is in a link\nconst isLinkActive = editor.isActive('link')\nconst linkAttributes = editor.getAttributes('link') // Get href, target, etc.", 11 | 12 | "imageHandling": "// Basic image insertion\neditor.commands.setImage({\n src: 'https://example.com/image.jpg',\n alt: 'Description',\n title: 'Image title'\n})\n\n// Handle file uploads with validation\nasync function handleImageUpload(file: File) {\n // Validate file type\n const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']\n if (!allowedTypes.includes(file.type)) {\n throw new Error('Unsupported file type')\n }\n \n // Validate file size (e.g., 5MB limit)\n const maxSize = 5 * 1024 * 1024\n if (file.size > maxSize) {\n throw new Error('File too large')\n }\n \n try {\n // Option 1: Upload to server\n const formData = new FormData()\n formData.append('image', file)\n \n const response = await fetch('/api/upload', {\n method: 'POST',\n body: formData\n })\n \n if (!response.ok) throw new Error('Upload failed')\n \n const { url, alt } = await response.json()\n \n editor.commands.setImage({\n src: url,\n alt: alt || file.name,\n title: file.name\n })\n \n } catch (error) {\n console.error('Image upload failed:', error)\n \n // Option 2: Fallback to base64 for small images\n if (file.size < 1024 * 1024) { // 1MB limit for base64\n const reader = new FileReader()\n reader.onload = (e) => {\n editor.commands.setImage({\n src: e.target?.result as string,\n alt: file.name,\n title: file.name\n })\n }\n reader.readAsDataURL(file)\n }\n }\n}\n\n// Handle paste events for images\neditor.on('paste', (event) => {\n const items = event.clipboardData?.items\n if (!items) return\n \n for (const item of items) {\n if (item.type.startsWith('image/')) {\n const file = item.getAsFile()\n if (file) {\n handleImageUpload(file)\n event.preventDefault()\n }\n }\n }\n})", 13 | 14 | 15 | "blockElements": "// Block elements (all available in enhanced Controls)\neditor.commands.toggleBlockquote() // Quote button\neditor.commands.toggleCodeBlock() // Code block button\neditor.commands.setHorizontalRule() // Horizontal rule button\n\n// Set specific block types\neditor.commands.setBlockquote()\neditor.commands.setCodeBlock()\neditor.commands.setCodeBlock({ language: 'javascript' })\n\n// Check active block states\nconst isBlockquote = editor.isActive('blockquote')\nconst isCodeBlock = editor.isActive('codeBlock')\n\n// Advanced block operations\neditor.commands.wrapIn('blockquote')\neditor.commands.lift('blockquote')\n\n// Insert dividers and breaks\neditor.commands.setHorizontalRule()\neditor.commands.setHardBreak()\n\n// Code block with language\neditor.chain()\n .focus()\n .toggleCodeBlock({ language: 'typescript' })\n .run()", 16 | 17 | "selectionManagement": "// Selection operations\neditor.commands.selectAll()\neditor.commands.selectTextblockStart()\neditor.commands.selectTextblockEnd()\neditor.commands.selectNodeForward()\neditor.commands.selectNodeBackward()\n\n// Set selection to specific range\neditor.commands.setTextSelection({ from: 0, to: 10 })\neditor.commands.setTextSelection(5) // Set cursor at position 5\n\n// Delete operations\neditor.commands.deleteSelection()\neditor.commands.deleteRange({ from: 0, to: 10 })\neditor.commands.deleteCurrentNode()\n\n// Navigation\neditor.commands.goToNext()\neditor.commands.goToPrevious()", 18 | 19 | "historyManagement": "// Undo/Redo operations (buttons available in enhanced Controls)\neditor.commands.undo() // Undo button with smart disabled state\neditor.commands.redo() // Redo button with smart disabled state\n\n// Check if undo/redo is available\nconst canUndo = editor.can().undo()\nconst canRedo = editor.can().redo()\n\n// Clear history\neditor.commands.clearHistory()\n\n// The enhanced Controls component automatically handles:\n// - Button disabled states based on availability\n// - Visual feedback for enabled/disabled buttons\n// - Proper ARIA labels for accessibility\n\n// Custom undo/redo buttons (if building custom controls)\nfunction createHistoryButton(type: 'undo' | 'redo') {\n return {\n canExecute: () => type === 'undo' ? editor.can().undo() : editor.can().redo(),\n execute: () => type === 'undo' ? editor.commands.undo() : editor.commands.redo(),\n isDisabled: () => type === 'undo' ? !editor.can().undo() : !editor.can().redo()\n }\n}", 20 | 21 | "advancedCommands": "// Command chaining for complex operations\neditor.chain()\n .focus()\n .toggleBold()\n .toggleItalic()\n .setTextAlign('center')\n .run()\n\n// Conditional command execution\nif (editor.isActive('bold')) {\n editor.chain().focus().unsetBold().setItalic().run()\n} else {\n editor.chain().focus().setBold().run()\n}\n\n// Check if command can be executed\nconst canToggleBold = editor.can().toggleBold()\nconst canSetHeading = editor.can().setHeading({ level: 2 })\n\n// Custom command wrapper with error handling\nfunction safeCommand(command: () => boolean) {\n try {\n const result = command()\n if (!result) {\n console.warn('Command execution failed')\n }\n return result\n } catch (error) {\n console.error('Command error:', error)\n return false\n }\n}\n\n// Usage\nsafeCommand(() => editor.chain().focus().toggleBold().run())\n\n// Batch operations with transaction\neditor.chain()\n .command(({ tr }) => {\n // Custom transaction operations\n tr.insertText('Hello')\n tr.addMark(0, 5, editor.schema.marks.bold.create())\n return true\n })\n .run()", 22 | 23 | "customCommands": "// Define custom command\nconst customCommand = () => ({ commands }: { commands: any }) => {\n return commands.insertContent('Custom content inserted!')\n}\n\n// Register and use custom command\neditor.commands.customCommand = customCommand()\neditor.commands.customCommand()\n\n// More complex custom command\nconst insertCurrentDate = () => ({ commands }: { commands: any }) => {\n const date = new Date().toLocaleDateString()\n return commands.insertContent(`

Today is ${date}

`)\n}\n\n// Smart formatting command\nconst smartFormat = (text: string) => ({ commands }: { commands: any }) => {\n // Auto-detect and apply formatting\n if (text.startsWith('# ')) {\n return commands.setHeading({ level: 1 })\n } else if (text.startsWith('## ')) {\n return commands.setHeading({ level: 2 })\n } else if (text.startsWith('- ')) {\n return commands.setBulletList()\n }\n return commands.setParagraph()\n}", 24 | 25 | "eventDrivenCommands": "// Listen to editor events and execute commands\neditor.on('update', ({ editor }) => {\n // Auto-save functionality\n const content = editor.getHTML()\n localStorage.setItem('editor-content', content)\n})\n\neditor.on('selectionUpdate', ({ editor }) => {\n // Update UI based on selection\n const { from, to } = editor.state.selection\n console.log(`Selection: ${from} to ${to}`)\n})\n\n// Custom keyboard shortcuts with commands\neditor.on('keydown', (event) => {\n // Custom Ctrl+Shift+D for current date\n if (event.ctrlKey && event.shiftKey && event.key === 'D') {\n event.preventDefault()\n editor.commands.insertContent(`

${new Date().toLocaleDateString()}

`)\n }\n \n // Auto-format on Enter\n if (event.key === 'Enter') {\n const { $from } = editor.state.selection\n const currentLine = $from.parent.textContent\n \n if (currentLine.startsWith('# ')) {\n setTimeout(() => {\n editor.commands.setHeading({ level: 1 })\n }, 0)\n }\n }\n})" 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tipex 2 | 3 | Tipex stands as an advanced rich text editor tailored for Svelte, meticulously engineered with the robust 4 | frameworks [Tiptap](https://tiptap.dev/) and [Prosemirror](https://prosemirror.net/). It empowers developers to 5 | effortlessly craft rich text editors, liberating them from the intricacies of underlying technologies, style management, 6 | and related complexities. 7 | 8 | > Svelte 5 and runes mode activated! ✨🔮 9 | 10 | ## Key Features 11 | 12 | - 🚀 **Svelte 5 Ready**: Built with Svelte 5's latest features including runes, snippets, and modern reactivity 13 | - 🎨 **Smart Control System**: Automatically detects whether to show default or custom controls based on `controlComponent` prop 14 | - 🔌 **Extensible Architecture**: Fully extensible through Tiptap's extension system with default extensions included 15 | - 📱 **Mobile Responsive**: Optimized for both desktop and mobile with touch-friendly interactions 16 | - 🎯 **Context-Aware Floating Menu**: Intelligent floating toolbar that appears on text selection 17 | - 🔗 **Advanced Link Management**: Built-in link editing with clipboard integration and visual feedback 18 | - ✅ **Task Lists**: Native support for interactive checkboxes and task management 19 | - 🎭 **Modern Theming**: Built with Tailwind CSS v4 using OKLCH colors and CSS custom properties 20 | - ⚡ **Performance Optimized**: Leverages Svelte's reactivity and efficient DOM updates 21 | - 💼 **Full TypeScript Support**: Complete type safety with comprehensive type definitions 22 | - 🎨 **Focus Management**: Smart focus detection with visual feedback and accessibility support 23 | 24 | ## Installation 25 | 26 | Install the package from [NPM](https://www.npmjs.com/package/@friendofsvelte/tipex): 27 | 28 | ```bash 29 | npm install "@friendofsvelte/tipex" 30 | ``` 31 | 32 | Check out [Tipex Editor Variants](https://github.com/Bishwas-py/tipex-editor-variants) and its [live preview](https://tipex-editor-variants.pages.dev/) for examples showcasing what you can build with the Tipex Svelte text editor, including multiple themes and configurations. 33 | 34 | ## Basic Usage 35 | 36 | Import the component and styles, then use it in your Svelte component: 37 | 38 | ```svelte 39 | 56 | 57 | 64 | ``` 65 | 66 | ## Core Concepts 67 | 68 | ### Control System 69 | 70 | Tipex features a simple and flexible control system: 71 | 72 | 1. **Default Controls** (automatic when no `controlComponent` is provided): 73 | - Pre-built formatting toolbar with essential editing tools 74 | - Built-in utility buttons (copy, link management) 75 | - Perfect for quick implementation 76 | 77 | 2. **Custom Controls** (when `controlComponent` snippet is provided): 78 | - Full control over the editor interface 79 | - Complete customization freedom 80 | - Can extend default controls or create entirely new interfaces 81 | - Ideal for specialized use cases 82 | 83 | 3. **No Controls** (when `controlComponent={null}` is explicitly set): 84 | - Completely hides all control elements 85 | - Provides a clean, minimal editor interface 86 | - Perfect for read-only or embedded scenarios 87 | 88 | This simple approach provides maximum flexibility while maintaining ease of use. 89 | 90 | ### Extension System 91 | 92 | Tipex leverages Tiptap's extension system for enhanced functionality. It comes with sensible defaults but you can extend or override them: 93 | 94 | ```svelte 95 | 114 | 115 | 116 | ``` 117 | 118 | ### Floating Menu 119 | 120 | The floating menu provides context-aware formatting options that appear when text is selected: 121 | 122 | ```svelte 123 | 124 | ``` 125 | 126 | ### Focus Management 127 | 128 | Tipex includes smart focus detection with visual feedback: 129 | 130 | ```svelte 131 | 132 | 133 | ``` 134 | 135 | ## Modern Theming with Tailwind CSS v4 136 | 137 | Tipex is built with **Tailwind CSS v4** and uses modern OKLCH colors for better color accuracy and consistency. The theming system uses CSS custom properties that you can override: 138 | 139 | ```css 140 | @import '@friendofsvelte/tipex/styles/index.css'; 141 | 142 | @theme { 143 | /* Override Tipex colors */ 144 | --color-tipex-primary-500: oklch(0.65 0.11 285); /* Custom purple */ 145 | --color-tipex-success-500: oklch(0.647 0.208 142.425); /* Custom green */ 146 | 147 | /* Custom spacing */ 148 | --spacing-tipex-md: 1.25rem; 149 | --spacing-tipex-lg: 2rem; 150 | } 151 | ``` 152 | 153 | ### Available CSS Custom Properties 154 | 155 | - **Colors**: `--color-tipex-{50-950}`, `--color-tipex-primary-{50-950}`, `--color-tipex-success-{50-950}` 156 | - **Spacing**: `--spacing-tipex-{xs,sm,md,lg,xl,2xl}` 157 | - **Sizing**: `--size-tipex-{1-12}` 158 | - **Typography**: `--text-tipex-{xs,sm,base,lg,xl,2xl}` 159 | - **Border Radius**: `--radius-tipex-{sm,md}` 160 | - **Z-Index**: `--z-tipex-{floating,controls}` 161 | 162 | ## Advanced Usage 163 | 164 | ### Custom Head and Foot Sections 165 | 166 | Add custom components above or below the editor using Svelte 5 snippets: 167 | 168 | ```svelte 169 | 174 | 175 | 176 | {#snippet head(editor)} 177 | 178 | {/snippet} 179 | 180 | {#snippet foot(editor)} 181 | 182 | {/snippet} 183 | 184 | ``` 185 | 186 | ### Extending Default Controls 187 | 188 | Add custom utilities while keeping the default toolbar: 189 | 190 | ```svelte 191 | 196 | 197 | 198 | {#snippet controlComponent(tipex)} 199 | 200 | 201 | 202 | 205 | 206 | {/snippet} 207 | 208 | ``` 209 | 210 | ### Custom Control Component 211 | 212 | Create a completely custom control interface: 213 | 214 | ```svelte 215 | 220 | 221 | 222 | {#snippet controlComponent(tipex)} 223 |
224 | 227 | 230 | 233 |
234 | {/snippet} 235 |
236 | ``` 237 | 238 | ### No Controls (Minimal Editor) 239 | 240 | Hide all controls for a clean, minimal editor interface: 241 | 242 | ```svelte 243 | 248 | 249 | 250 | 251 | ``` 252 | 253 | ## Props & Configuration 254 | 255 | Based on the actual `TipexProps` interface, here are all available properties: 256 | 257 | | Prop | Type | Default | Description | 258 | |------|------|---------|-------------| 259 | | `body` | `string` | `''` | Initial HTML content for the editor | 260 | | `tipex` | `TipexEditor` (bindable) | `undefined` | The editor instance - bind to access editor methods | 261 | | `extensions` | `AnyExtension[]` (bindable) | `defaultExtensions` | Array of Tiptap extensions to use | 262 | | `floating` | `boolean` | `false` | Enable floating menu on text selection | 263 | | `focal` | `boolean` | `true` | Enable focus ring styling | 264 | | `focused` | `boolean` (bindable) | `false` | Whether the editor is currently focused | 265 | | `autofocus` | `boolean` | `true` | Auto-focus the editor on mount | 266 | | `class` | `string` | `''` | Additional CSS classes for the editor container | 267 | | `style` | `string` | `''` | Inline styles for the editor container | 268 | | `ctxId` | `string` | `'_tipex'` | Context ID for the editor instance | 269 | | `head` | `Snippet<[TipexEditor]>` | `undefined` | Content rendered above the editor | 270 | | `foot` | `Snippet<[TipexEditor]>` | `undefined` | Content rendered below the editor | 271 | | `controlComponent` | `Snippet<[TipexEditor]> \| null` | `undefined` | Custom control component (replaces default controls). Set to `null` to hide all controls. | 272 | | `oncreate` | `(props: EditorEvents['create']) => void` | `() => {}` | Callback when editor is created | 273 | | `ondestroy` | `(props: EditorEvents['destroy']) => void` | `() => {}` | Callback when editor is destroyed | 274 | | `onupdate` | `(props: EditorEvents['update']) => void` | `() => {}` | Callback when editor content updates | 275 | 276 | ### Boolean Props with Negation 277 | 278 | You can disable boolean props using the `!` prefix: 279 | 280 | ```svelte 281 | 282 | ``` 283 | 284 | ## Getting Editor Content 285 | 286 | Access the editor instance and its content using Svelte 5 runes: 287 | 288 | ```svelte 289 | 307 | 308 | 309 | 310 |
311 |

Words: {wordCount}

312 |

Characters: {textContent.length}

313 |
314 | 315 |
316 | HTML Output 317 |
{htmlContent}
318 |
319 | ``` 320 | 321 | ## Documentation 322 | 323 | For comprehensive documentation, visit [tipex.pages.dev](https://tipex.pages.dev/). 324 | 325 | ## About Friend Of Svelte 326 | 327 | ![Friend Of Svelte Logo](https://avatars.githubusercontent.com/u/143795012?s=200&v=4) 328 | 329 | [Friend Of Svelte](https://github.com/friendofsvelte) is a community-driven project to help Svelte developers find and 330 | develop awesome Svelte resources. Our mission is to create high-quality, maintainable, and accessible tools for the 331 | Svelte ecosystem. 332 | 333 | ### Join the Community 334 | 335 | - 🌟 Star our repositories 336 | - 🤝 Contribute to projects 337 | - 📢 Share your ideas 338 | - 👥 Open memberships for everyone 339 | 340 | If you like this project, you can be one of the friends by contributing to the project. Memberships are open for 341 | everyone. 342 | 343 | ## License 344 | 345 | MIT Licensed. Copyright (c) 2023-2024 Friend of Svelte. 346 | -------------------------------------------------------------------------------- /src/routes/customization/+page.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | Customization Guide | Tipex Editor 12 | 16 | 17 | 18 | 22 | 23 | Back to Home 24 | 25 | 26 |

Customization Guide

27 |

28 | Tipex is architected with customization as a core principle. Every aspect of the editor can be 29 | tailored to match your application's design system and functional requirements. This comprehensive 30 | guide covers everything from basic styling to advanced extension development. 31 |

32 | 33 |

Quick Customization Overview

34 |

Tipex offers multiple layers of customization:

35 |
    36 |
  • Theme Customization: Colors, spacing, typography, and visual styling
  • 37 |
  • 38 | Component Replacement: Replace built-in controls with your own components 39 |
  • 40 |
  • Extension System: Add new functionality through TipTap extensions
  • 41 |
  • Event Handling: Custom behavior through event listeners
  • 42 |
  • Layout Customization: Header, footer, and utility area modifications
  • 43 |
44 | 45 |

Theme & Styling Customization

46 |

47 | Tipex uses Tailwind CSS v4 with the modern `@theme` configuration system and CSS custom 48 | properties for comprehensive theming. You can override any aspect of the visual design using the new 49 | Tailwind v4 architecture: 50 |

51 | 52 | 53 | 54 |

Control Component Customization

55 | 56 |

57 | Tipex provides a single, flexible way to customize the editor controls through the 58 | `controlComponent` slot. You can either extend the default controls with custom utilities or 59 | completely replace them with your own implementation. 60 |

61 | 62 |
63 | Tipex Editor control customization options 68 |
Control component provides complete flexibility for editor customization
69 |
70 | 71 |

Using Default Controls with Built-in Utilities

72 |

Include the default controls and utility buttons (copy, link management) in your editor:

73 | 74 | 75 |

Extending Default Controls with Custom Utilities

76 |

Add your own custom buttons alongside the built-in utilities:

77 | 78 | 79 |

Here's a more comprehensive example with multiple custom utilities:

80 | 81 | 82 | 83 | 84 | 85 |

Complete Control Replacement

86 |

Replace the entire control system with your own custom implementation:

87 | 88 | 89 |

Here's a complete custom control implementation:

90 | 91 | 92 | 93 |

⚠️ Tailwind v4 ONLY - No Legacy Versions!

94 |

95 | IMPORTANT: Tipex exclusively uses and 96 | requires Tailwind CSS v4. We do NOT support older versions (v1.x, v2.x, v3.x) as 97 | they lack the modern architecture required for Tipex's advanced theming system. 98 |

99 | 100 |
101 |
102 |
103 | 105 |
106 |
107 |

108 | Legacy Tailwind Versions Not Supported 109 |

110 |
111 |
    112 |
  • Tailwind v3.x and older lack @theme configuration
  • 113 |
  • Missing @custom-variant syntax for advanced dark mode
  • 114 |
  • Inferior performance and larger bundle sizes
  • 115 |
  • Limited component layer organization
  • 116 |
117 |
118 |
119 |
120 |
121 | 122 |

Tailwind v4 Advantages

123 |

Tipex exclusively uses Tailwind CSS v4 for superior developer experience and performance:

124 |
    125 |
  • 126 | 🚀 Modern Import System: Clean `@import "tailwindcss"` syntax without configuration 127 | files 128 |
  • 129 |
  • 130 | 🎨 Native CSS Custom Properties: Direct integration with CSS variables in `@theme` 131 | blocks 132 |
  • 133 |
  • 134 | ⚡ Better Performance: Faster builds and smaller bundle sizes compared to v3.x 135 |
  • 136 |
  • 🛠️ Enhanced DX: Improved IntelliSense and better error messages
  • 137 |
  • 138 | 🌙 Custom Variants: Powerful `@custom-variant` syntax for complex state handling 139 |
  • 140 |
  • 141 | 📦 Component Layers: Better organization with `@layer components` for maintainable 142 | styles 143 |
  • 144 |
  • 🔮 Future-Proof: Built for modern CSS features and browser capabilities
  • 145 |
146 | 147 |
148 |
149 |
150 | 151 |
152 |
153 |

Tailwind v4 Required

154 |

155 | Upgrade to Tailwind v4 immediately for the best Tipex experience. The new architecture is 156 | essential for Tipex's theming system to function properly. 157 |

158 |
159 |
160 |
161 | 162 |
163 |

164 | Migration Note: If you're using older Tailwind versions (v1.x, v2.x, v3.x), you 165 | MUST upgrade to Tailwind v4 for Tipex to work correctly. The new architecture provides significantly 166 | better performance, developer experience, and maintainability. 167 |

168 |
169 | 170 |

Advanced Layout Customization

171 |

Tipex supports comprehensive layout customization through multiple slot areas:

172 | 173 | 174 | 175 |

Here's a practical example with a custom header featuring document statistics:

176 | 177 | 178 | 179 |

Image Upload Customization

180 |

181 | Tipex provides flexible image handling. You can customize the upload process, validation, and 182 | storage: 183 |

184 | 185 | 186 | 187 |

Extension System

188 | 189 |

190 | Leverage TipTap's powerful extension system to add custom functionality. Tipex provides easy 191 | access to modify and extend the editor's capabilities: 192 |

193 | 194 |
195 | Advanced customization example showing custom extensions and theming in Tipex Editor 200 | 201 |
202 | Advanced customization with custom extensions and theming. 203 | 208 | View Source Code 209 | 210 |
211 |
212 | 213 |

Modifying Default Extensions

214 |

Customize the built-in extensions to match your requirements:

215 | 216 | 217 | 218 |

Creating Custom Extensions

219 |

Build your own extensions for specialized functionality:

220 | 221 | 222 | 223 |

Event-Driven Customization

224 |

React to editor events for dynamic behavior and integrations:

225 | 226 | 227 | 228 |

Accessibility Customization

229 |

230 | Enhance accessibility with custom ARIA labels, keyboard navigation, and screen reader support: 231 |

232 | 233 | 234 | 235 |

Performance Optimization with Tailwind v4

236 |

Optimize your customized editor using Tailwind v4's performance benefits:

237 | 238 |
    239 |
  • 🎯 Tailwind v4 Tree Shaking: Automatic unused CSS elimination
  • 240 |
  • ⚡ Faster Builds: Improved build performance over legacy versions
  • 241 |
  • 📦 Smaller Bundles: More efficient CSS generation
  • 242 |
  • 🔄 Lazy Loading: Load extensions and components only when needed
  • 243 |
  • ⏱️ Debounced Updates: Throttle auto-save and real-time features
  • 244 |
  • 🖥️ Virtual Scrolling: For large documents, implement virtual scrolling
  • 245 |
  • 🧠 Memory Management: Clean up event listeners and subscriptions
  • 246 |
247 | 248 |
249 |
250 |
251 | 252 |
253 |
254 |

255 | Tailwind v4 Performance Benefits 256 |

257 |

258 | Tailwind v4's modern architecture provides up to 50% faster builds and 30% smaller CSS 259 | bundles compared to v3.x, making your Tipex editor load faster and perform better. 260 |

261 |
262 |
263 |
264 | 265 |
266 |

267 | Best Practices with Tailwind v4: When customizing Tipex, leverage Tailwind v4's 268 | `@layer components`, `@theme` configuration, and `@custom-variant` features. Always test your changes 269 | across different devices and browsers. The modern Tailwind v4 architecture ensures better performance 270 | and maintainability compared to legacy versions. 271 |

272 |
273 | 274 |

Advanced Tailwind v4 Theming

275 |

276 | Create sophisticated themes using Tailwind v4's advanced features. Here's a complete example of a 277 | premium theme configuration: 278 |

279 | 280 | 281 | 282 |

283 | This example demonstrates Tailwind v4's powerful theming capabilities including gradient 284 | backgrounds, advanced shadows, custom scrollbars, and sophisticated component styling that would 285 | be much more complex in older Tailwind versions. 286 |

287 | 288 |
289 |

290 | 🚫 Why Tailwind v4 Only: Tipex exclusively supports and promotes Tailwind CSS 291 | v4 because it represents the future of utility-first CSS. The new architecture provides native 292 | CSS custom property integration, better performance, improved developer experience, and more 293 | maintainable code. We strongly discourage and do NOT support using older Tailwind versions 294 | (v1.x, v2.x, v3.x) as they lack the modern features that make Tipex's theming system possible. 295 |

296 | ⚠️ Legacy Tailwind Warning: Attempting to 297 | use Tipex with older Tailwind versions will result in broken styling, missing features, and poor 298 | performance. Upgrade to Tailwind v4 immediately. 299 |

300 |
301 | -------------------------------------------------------------------------------- /src/lib/tipex/Controls.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 34 | 35 | {#if tipex} 36 |
37 |
38 | 39 | 49 | 50 | 60 | 61 | 71 | 72 | 92 | 93 |
94 | 95 | 96 | 110 | 111 | 125 | 126 | 136 | 137 | 147 | 148 | 162 | 163 |
164 | 165 | 166 | 180 | 181 | 198 | 199 | 216 | 217 |
218 | 219 | 220 | 234 | 235 | 249 | 250 | 263 | 264 |
265 | 266 | 267 | 285 | 286 | 304 |
305 | 306 | 307 |
308 | 349 | 350 | 351 | 352 | {@render children?.()} 353 |
354 |
355 | {/if} 356 | -------------------------------------------------------------------------------- /src/item/codes/advanceCodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "customizeControlImplementation": "", 3 | 4 | "addHeadFootComponent": "\n\n\n {#snippet head(tipex)}\n \n {/snippet}\n \n {#snippet controlComponent(tipex)}\n saveDocument(tipex.getHTML())}\n onExport={() => exportDocument(tipex.getHTML())}\n />\n {/snippet}\n \n {#snippet foot(tipex)}\n \n {/snippet}\n", 5 | 6 | "tweakingExtensions": "\n\n", 7 | 8 | "advancedCustomExtension": "\n\n\n {#snippet head()}\n
\n Words: {stats.words}\n Characters: {stats.characters}\n Sentences: {stats.sentences}\n Paragraphs: {stats.paragraphs}\n
\n {/snippet}\n \n {#snippet controlComponent(tipex)}\n
\n \n \n \n \n \n \n
\n {/snippet}\n
", 9 | 10 | "collaborativeExtension": "\n\n\n {#snippet head()}\n
\n Connected users: {connectedUsers.length}\n
\n {#each connectedUsers as user}\n
\n {user.name.charAt(0)}\n
\n {/each}\n
\n
\n {/snippet}\n
", 11 | 12 | "tailwindV4Theming": "/* Premium Tipex Theme with Tailwind v4 - Using Actual Design System */\n@import \"tailwindcss\";\n\n@theme {\n /* Override Tipex Design System with Premium Colors */\n --color-tipex-*: initial;\n \n /* Premium Grayscale Palette (OKLCH for better color accuracy) */\n --color-tipex-50: oklch(0.99 0 0);\n --color-tipex-100: oklch(0.975 0 0);\n --color-tipex-200: oklch(0.925 0 0);\n --color-tipex-300: oklch(0.875 0 0);\n --color-tipex-400: oklch(0.715 0 0);\n --color-tipex-500: oklch(0.565 0 0);\n --color-tipex-600: oklch(0.445 0 0);\n --color-tipex-700: oklch(0.375 0 0);\n --color-tipex-800: oklch(0.275 0 0);\n --color-tipex-900: oklch(0.215 0 0);\n --color-tipex-950: oklch(0.15 0 0);\n \n /* Premium Success Colors (Emerald Green) */\n --color-tipex-success-50: oklch(0.985 0.008 145);\n --color-tipex-success-100: oklch(0.972 0.015 145);\n --color-tipex-success-200: oklch(0.93 0.045 145);\n --color-tipex-success-300: oklch(0.875 0.09 145);\n --color-tipex-success-400: oklch(0.775 0.16 145);\n --color-tipex-success-500: oklch(0.65 0.21 145);\n --color-tipex-success-600: oklch(0.55 0.21 145);\n --color-tipex-success-700: oklch(0.47 0.185 145);\n --color-tipex-success-800: oklch(0.39 0.15 145);\n --color-tipex-success-900: oklch(0.33 0.12 145);\n --color-tipex-success-950: oklch(0.21 0.08 145);\n \n /* Premium Primary Colors (Purple/Violet) */\n --color-tipex-primary-50: oklch(0.985 0.005 285);\n --color-tipex-primary-100: oklch(0.972 0.01 285);\n --color-tipex-primary-200: oklch(0.93 0.025 285);\n --color-tipex-primary-300: oklch(0.875 0.05 285);\n --color-tipex-primary-400: oklch(0.775 0.085 285);\n --color-tipex-primary-500: oklch(0.65 0.11 285);\n --color-tipex-primary-600: oklch(0.55 0.11 285);\n --color-tipex-primary-700: oklch(0.47 0.095 285);\n --color-tipex-primary-800: oklch(0.39 0.08 285);\n --color-tipex-primary-900: oklch(0.33 0.065 285);\n --color-tipex-primary-950: oklch(0.21 0.05 285);\n \n /* Enhanced Spacing Scale */\n --spacing-tipex-xs: 0.375rem;\n --spacing-tipex-sm: 0.625rem;\n --spacing-tipex-md: 1.125rem;\n --spacing-tipex-lg: 1.75rem;\n --spacing-tipex-xl: 2.25rem;\n --spacing-tipex-2xl: 2.75rem;\n \n /* Enhanced Sizing Scale */\n --size-tipex-1: 0.375rem;\n --size-tipex-2: 0.625rem;\n --size-tipex-3: 0.875rem;\n --size-tipex-4: 1.125rem;\n --size-tipex-5: 1.375rem;\n --size-tipex-6: 1.625rem;\n --size-tipex-7: 1.875rem;\n --size-tipex-8: 2.125rem;\n --size-tipex-9: 2.375rem;\n --size-tipex-10: 2.625rem;\n --size-tipex-11: 2.875rem;\n --size-tipex-12: 3.125rem;\n \n /* Enhanced Border Radius */\n --radius-tipex-sm: 0.375rem;\n --radius-tipex-md: 0.5rem;\n --radius-tipex-lg: 0.75rem;\n \n /* Enhanced Typography */\n --text-tipex-xs: 0.8125rem;\n --text-tipex-sm: 0.9375rem;\n --text-tipex-base: 1.0625rem;\n --text-tipex-lg: 1.1875rem;\n --text-tipex-xl: 1.375rem;\n --text-tipex-2xl: 1.625rem;\n \n /* Enhanced Z-Index */\n --z-tipex-floating: 100;\n --z-tipex-controls: 50;\n \n /* Premium Shadows */\n --shadow-tipex-sm: 0 1px 3px 0 rgb(0 0 0 / 0.08), 0 1px 2px 0 rgb(0 0 0 / 0.06);\n --shadow-tipex-md: 0 4px 8px -2px rgb(0 0 0 / 0.12), 0 2px 4px -2px rgb(0 0 0 / 0.06);\n --shadow-tipex-lg: 0 8px 16px -4px rgb(0 0 0 / 0.16), 0 4px 8px -4px rgb(0 0 0 / 0.08);\n --shadow-tipex-focus: 0 0 0 3px oklch(0.65 0.11 285 / 0.4);\n}\n\n/* Custom Dark Mode Variant */\n@custom-variant dark (&:where(.dark, .dark *));\n\n/* Premium Component Styling */\n@layer components {\n .tipex-editor-premium {\n @apply bg-gradient-to-br from-tipex-50 via-tipex-100 to-tipex-200\n dark:from-tipex-950 dark:via-tipex-900 dark:to-tipex-800\n border-2 border-tipex-200 dark:border-tipex-800\n rounded-radius-tipex-lg shadow-tipex-md;\n }\n \n .tipex-editor-premium.focused.focal {\n @apply shadow-tipex-focus\n border-tipex-primary-500 dark:border-tipex-primary-400\n outline-none ring-2 ring-tipex-primary-500/20 dark:ring-tipex-primary-400/20;\n }\n \n .tipex-control-premium {\n @apply bg-tipex-100/90 dark:bg-tipex-900/90\n backdrop-blur-sm border border-tipex-200/50 dark:border-tipex-800/50\n rounded-radius-tipex-md shadow-tipex-sm;\n }\n \n .tipex-button-premium {\n @apply px-spacing-tipex-md py-spacing-tipex-sm\n bg-gradient-to-b from-tipex-100 to-tipex-200\n dark:from-tipex-900 dark:to-tipex-800\n border border-tipex-300 dark:border-tipex-700\n rounded-radius-tipex-sm shadow-tipex-sm\n hover:shadow-tipex-md hover:scale-105\n active:scale-95 active:shadow-tipex-sm\n transition-all duration-150 ease-out\n focus:outline-none focus:ring-2 focus:ring-tipex-primary-500/40;\n }\n \n .tipex-button-premium.active {\n @apply bg-gradient-to-b from-tipex-primary-100 to-tipex-primary-200\n dark:from-tipex-primary-900 dark:to-tipex-primary-800\n border-tipex-primary-500 dark:border-tipex-primary-400\n text-tipex-primary-700 dark:text-tipex-primary-300\n shadow-tipex-md ring-2 ring-tipex-primary-500/20 dark:ring-tipex-primary-400/20;\n }\n \n .tipex-scrollbar-premium {\n scrollbar-width: thin;\n scrollbar-color: oklch(0.65 0.11 285 / 0.3) transparent;\n }\n \n .tipex-scrollbar-premium::-webkit-scrollbar {\n width: 6px;\n }\n \n .tipex-scrollbar-premium::-webkit-scrollbar-track {\n background: transparent;\n }\n \n .tipex-scrollbar-premium::-webkit-scrollbar-thumb {\n background: linear-gradient(to bottom, \n oklch(0.65 0.11 285 / 0.3), \n oklch(0.55 0.11 285 / 0.5));\n border-radius: 3px;\n }\n \n .tipex-scrollbar-premium::-webkit-scrollbar-thumb:hover {\n background: linear-gradient(to bottom, \n oklch(0.65 0.11 285 / 0.5), \n oklch(0.55 0.11 285 / 0.7));\n }\n \n .dark .tipex-scrollbar-premium {\n scrollbar-color: oklch(0.65 0.11 285 / 0.4) transparent;\n }\n \n .dark .tipex-scrollbar-premium::-webkit-scrollbar-thumb {\n background: linear-gradient(to bottom, \n oklch(0.65 0.11 285 / 0.3), \n oklch(0.55 0.11 285 / 0.5));\n }\n \n .dark .tipex-scrollbar-premium::-webkit-scrollbar-thumb:hover {\n background: linear-gradient(to bottom, \n oklch(0.65 0.11 285 / 0.5), \n oklch(0.55 0.11 285 / 0.7));\n }\n}" 13 | } 14 | -------------------------------------------------------------------------------- /src/item/codes/customizationCodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "tailwindThemeConfig": "/* Modern Tailwind v4 Theme Configuration - Based on Tipex Design System */\n@import \"tailwindcss\";\n\n@theme {\n /* Override Tipex Design System Colors */\n --color-tipex-*: initial;\n \n /* Tipex Grayscale Colors (50-950) - Override defaults */\n --color-tipex-50: oklch(0.99 0 0);\n --color-tipex-100: oklch(0.975 0 0);\n --color-tipex-200: oklch(0.93 0 0);\n --color-tipex-300: oklch(0.88 0 0);\n --color-tipex-400: oklch(0.72 0 0);\n --color-tipex-500: oklch(0.57 0 0);\n --color-tipex-600: oklch(0.45 0 0);\n --color-tipex-700: oklch(0.38 0 0);\n --color-tipex-800: oklch(0.28 0 0);\n --color-tipex-900: oklch(0.22 0 0);\n --color-tipex-950: oklch(0.15 0 0);\n \n /* Tipex Success Colors (Green) - Override defaults */\n --color-tipex-success-50: oklch(0.985 0.008 140);\n --color-tipex-success-100: oklch(0.972 0.015 140);\n --color-tipex-success-200: oklch(0.93 0.045 140);\n --color-tipex-success-300: oklch(0.875 0.09 140);\n --color-tipex-success-400: oklch(0.775 0.16 140);\n --color-tipex-success-500: oklch(0.65 0.21 140);\n --color-tipex-success-600: oklch(0.55 0.21 140);\n --color-tipex-success-700: oklch(0.47 0.185 140);\n --color-tipex-success-800: oklch(0.39 0.15 140);\n --color-tipex-success-900: oklch(0.33 0.12 140);\n --color-tipex-success-950: oklch(0.21 0.08 140);\n \n /* Tipex Primary Colors (Indigo) - Override defaults */\n --color-tipex-primary-50: oklch(0.985 0.004 270);\n --color-tipex-primary-100: oklch(0.972 0.008 270);\n --color-tipex-primary-200: oklch(0.93 0.022 270);\n --color-tipex-primary-300: oklch(0.875 0.045 270);\n --color-tipex-primary-400: oklch(0.775 0.08 270);\n --color-tipex-primary-500: oklch(0.65 0.1 270);\n --color-tipex-primary-600: oklch(0.55 0.1 270);\n --color-tipex-primary-700: oklch(0.47 0.085 270);\n --color-tipex-primary-800: oklch(0.39 0.07 270);\n --color-tipex-primary-900: oklch(0.33 0.058 270);\n --color-tipex-primary-950: oklch(0.21 0.045 270);\n \n /* Spacing Scale - Override defaults */\n --spacing-tipex-xs: 0.25rem;\n --spacing-tipex-sm: 0.5rem;\n --spacing-tipex-md: 1rem;\n --spacing-tipex-lg: 1.5rem;\n --spacing-tipex-xl: 2rem;\n --spacing-tipex-2xl: 2.5rem;\n \n /* Sizing Scale - Override defaults */\n --size-tipex-1: 0.25rem;\n --size-tipex-2: 0.5rem;\n --size-tipex-3: 0.75rem;\n --size-tipex-4: 1rem;\n --size-tipex-5: 1.25rem;\n --size-tipex-6: 1.5rem;\n --size-tipex-7: 1.75rem;\n --size-tipex-8: 2rem;\n --size-tipex-9: 2.25rem;\n --size-tipex-10: 2.5rem;\n --size-tipex-11: 2.75rem;\n --size-tipex-12: 3rem;\n \n /* Border Radius - Override defaults */\n --radius-tipex-sm: 0.25rem;\n --radius-tipex-md: 0.375rem;\n \n /* Typography Scale - Override defaults */\n --text-tipex-xs: 0.75rem;\n --text-tipex-sm: 0.875rem;\n --text-tipex-base: 1rem;\n --text-tipex-lg: 1.125rem;\n --text-tipex-xl: 1.25rem;\n --text-tipex-2xl: 1.5rem;\n \n /* Z-Index - Override defaults */\n --z-tipex-floating: 50;\n --z-tipex-controls: 10;\n \n /* Custom additions (not in default theme) */\n --shadow-tipex-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n --shadow-tipex-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);\n --shadow-tipex-focus: 0 0 0 3px rgb(139 92 246 / 0.5);\n}\n\n/* Custom Dark Mode Variant (Tailwind v4) */\n@custom-variant dark (&:where(.dark, .dark *));\n\n/* Component Layer Customization */\n@layer components {\n .tipex-editor {\n @apply bg-tipex-50 dark:bg-tipex-950\n border border-tipex-200 dark:border-tipex-800\n rounded-radius-tipex-md;\n }\n \n .tipex-editor.focused.focal {\n @apply shadow-tipex-focus\n border-tipex-primary-500 dark:border-tipex-primary-400\n outline-none;\n }\n \n .tipex-controls {\n @apply bg-tipex-100 dark:bg-tipex-900\n border border-tipex-200 dark:border-tipex-800\n rounded-radius-tipex-sm;\n }\n}", 3 | 4 | "customUtilities": "\n {#snippet controlComponent(tipex)}\n \n \n \n \n \n \n \n \n \n {/snippet}\n", 5 | 6 | "customToolbar": "\n\n\n {#snippet controlComponent(tipex)}\n \n {/snippet}\n\n\n\n\n\n
\n
\n \n \n \n
\n \n
\n \n
\n
\n\n", 7 | 8 | "layoutCustomization": "\n\n\n {#snippet head(tipex)}\n
\n \n \n
\n {/snippet}\n \n {#snippet controlComponent(tipex)}\n \n {/snippet}\n \n {#snippet foot(tipex)}\n
\n Ready\n {getWordCount(tipex)} words\n
\n {/snippet}\n
", 9 | 10 | "imageUploadCustomization": "\n\n\n {#snippet controlComponent(tipex)}\n
\n \n \n \n \n \n \n
\n {/snippet}\n
", 11 | 12 | "customExtensions": "\n\n\n {#snippet controlComponent(tipex)}\n
\n \n \n \n \n \n Words: {tipex.storage.wordCount?.wordCount || 0}\n \n
\n {/snippet}\n
", 13 | 14 | "eventDrivenCustomization": "\n\n\n {#snippet head()}\n
\n {#if isTyping}\n Typing...\n {:else}\n Saved\n {/if}\n
\n {/snippet}\n
", 15 | 16 | "accessibilityCustomization": "\n\n\n {#snippet controlComponent(tipex)}\n
\n \n \n \n
\n {/snippet}\n
\n\n" 17 | } 18 | --------------------------------------------------------------------------------