├── .eslintignore ├── .eslintrc.cjs ├── .github └── FUNDING.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── README.md ├── package-lock.json ├── package.json ├── playwright.config.ts ├── pnpm-lock.yaml ├── postcss.config.cjs ├── src ├── app.d.ts ├── app.html ├── app.postcss ├── fonts │ ├── work-sans.css │ └── work-sans │ │ ├── WorkSans-Black.ttf │ │ ├── WorkSans-BlackItalic.ttf │ │ ├── WorkSans-Bold.ttf │ │ ├── WorkSans-BoldItalic.ttf │ │ ├── WorkSans-ExtraBold.ttf │ │ ├── WorkSans-ExtraBoldItalic.ttf │ │ ├── WorkSans-ExtraLight.ttf │ │ ├── WorkSans-ExtraLightItalic.ttf │ │ ├── WorkSans-Italic.ttf │ │ ├── WorkSans-Light.ttf │ │ ├── WorkSans-LightItalic.ttf │ │ ├── WorkSans-Medium.ttf │ │ ├── WorkSans-MediumItalic.ttf │ │ ├── WorkSans-Regular.ttf │ │ ├── WorkSans-SemiBold.ttf │ │ ├── WorkSans-SemiBoldItalic.ttf │ │ ├── WorkSans-Thin.ttf │ │ └── WorkSans-ThinItalic.ttf ├── hooks.server.ts ├── icons │ ├── SimpleIconsBuymeacoffee.svelte │ ├── SimpleIconsGithub.svelte │ └── SimpleIconsNpm.svelte ├── index.test.ts ├── item │ ├── Footer.svelte │ ├── ParamSpan.svelte │ ├── PropsTable.svelte │ ├── SlotPropsTable.svelte │ ├── ThemeToggle.svelte │ └── codes │ │ ├── advanceCodes.ts │ │ └── index.ts ├── lib │ ├── index.ts │ └── tipex │ │ ├── Controls.svelte │ │ ├── Tipex.svelte │ │ ├── Utility.svelte │ │ ├── default.ts │ │ ├── icons │ │ ├── Fa6SolidArrowUpRightFromSquare.svelte │ │ ├── Fa6SolidBold.svelte │ │ ├── Fa6SolidCheck.svelte │ │ ├── Fa6SolidCode.svelte │ │ ├── Fa6SolidCopy.svelte │ │ ├── Fa6SolidItalic.svelte │ │ ├── Fa6SolidLink.svelte │ │ ├── Fa6SolidParagraph.svelte │ │ └── Fa6SolidXmark.svelte │ │ ├── link │ │ ├── EditLinkMenu.svelte │ │ └── LinkFloatingMenu.svelte │ │ ├── prepare.ts │ │ └── styles │ │ ├── CodeBlock.css │ │ ├── Controls.css │ │ ├── EditLink.css │ │ ├── ProseMirror.css │ │ └── Tipex.css ├── root.css └── routes │ ├── +layout.svelte │ ├── +page.svelte │ ├── +page.ts │ ├── commands │ └── +page.svelte │ └── customization │ └── +page.svelte ├── static └── favicon.png ├── svelte.config.js ├── tailwind.config.cjs ├── tests └── test.ts ├── tsconfig.json └── vite.config.ts /.eslintignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:svelte/recommended', 7 | 'prettier' 8 | ], 9 | parser: '@typescript-eslint/parser', 10 | plugins: ['@typescript-eslint'], 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020, 14 | extraFileExtensions: ['.svelte'] 15 | }, 16 | env: { 17 | browser: true, 18 | es2017: true, 19 | node: true 20 | }, 21 | overrides: [ 22 | { 23 | files: ['*.svelte'], 24 | parser: 'svelte-eslint-parser', 25 | parserOptions: { 26 | parser: '@typescript-eslint/parser' 27 | } 28 | } 29 | ] 30 | }; 31 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/ -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | resolution-mode=highest 3 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | > Svelte5 and runes mode activated! ✨🔮 9 | 10 | ## Key Features 11 | 12 | - 🚀 **Svelte 5 Ready**: Built with Svelte 5's latest features including runes and snippets 13 | - 🎨 **Customizable Controls**: Flexible control system with both default and custom options 14 | - 🔌 **Plugin Architecture**: Extensible through Tiptap extensions 15 | - 📱 **Responsive**: Works great on both desktop and mobile devices 16 | - 🎯 **Floating Menu**: Context-aware floating toolbar for enhanced editing experience 17 | - 🔗 **Link Management**: Built-in link handling with preview and editing capabilities 18 | - 🎭 **Theming Support**: Easy styling with CSS variables and utility classes 19 | - ⚡ **Performance Optimized**: Leverages Svelte's reactivity for smooth editing 20 | - 💼 **TypeScript Support**: Full TypeScript support for better development experience 21 | 22 | ## Installation 23 | 24 | Install the package from [NPM](https://www.npmjs.com/package/@friendofsvelte/tipex): 25 | 26 | ```bash 27 | npm install "@friendofsvelte/tipex" 28 | ``` 29 | 30 | ## Basic Usage 31 | 32 | Import the component and use it in your component: 33 | 34 | ```svelte 35 | 40 | 41 | 48 | ``` 49 | 50 | ## Core Concepts 51 | 52 | ### Control Modes 53 | 54 | Tipex offers two control modes: 55 | 56 | 1. **Default Controls** (`controls={true}`): 57 | - Pre-built formatting toolbar 58 | - Customizable through the `utilities` prop 59 | - Perfect for quick implementation 60 | 61 | 2. **Custom Controls** (`controls={false}`): 62 | - Full control over the editor interface 63 | - Use `controlComponent` for custom implementations 64 | - Ideal for specialized use cases 65 | 66 | ### Extension System 67 | 68 | Tipex leverages Tiptap's extension system for enhanced functionality: 69 | 70 | ```typescript 71 | import { Tipex } from "@friendofsvelte/tipex"; 72 | import { TextAlign } from '@tiptap/extension-text-align'; 73 | 74 | const extensions = [ 75 | TextAlign.configure({ 76 | types: ['heading', 'paragraph'], 77 | }), 78 | ]; 79 | 80 | // Use in component 81 | 83 | ``` 84 | 85 | ### Floating Menu 86 | 87 | The floating menu provides context-aware formatting options: 88 | 89 | ```svelte 90 | // Enables the floating menu 91 | ``` 92 | 93 | ## Advanced Usage 94 | 95 | ### Custom Head and Foot Sections 96 | 97 | Add custom components above or below the editor using Svelte 5 snippets: 98 | 99 | ```svelte 100 | 105 | 106 | 107 | {#snippet head(editor)} 108 | 109 | {/snippet} 110 | 111 | {#snippet foot(editor)} 112 | 113 | {/snippet} 114 | 115 | ``` 116 | 117 | ### Custom Utilities 118 | 119 | Add custom controls while keeping the default toolbar: 120 | 121 | ```svelte 122 | 127 | 128 | 129 | {#snippet utilities(editor)} 130 | 131 | {/snippet} 132 | 133 | ``` 134 | 135 | ### Custom Control Component 136 | 137 | Create a completely custom control interface: 138 | 139 | ```svelte 140 | 145 | 146 | 147 | {#snippet controlComponent(editor)} 148 | 149 | {/snippet} 150 | 151 | ``` 152 | 153 | ## How to get html content from editor? 154 | 155 | ```svelte 156 | 164 | 165 | 166 | ``` 167 | 168 | ## Documentation 169 | 170 | For comprehensive documentation, visit [tipex.pages.dev](https://tipex.pages.dev/). 171 | 172 | ## About Friend Of Svelte 173 | 174 | ![Friend Of Svelte Logo](https://avatars.githubusercontent.com/u/143795012?s=200&v=4) 175 | 176 | [Friend Of Svelte](https://github.com/friendofsvelte) is a community-driven project to help Svelte developers find and 177 | develop awesome Svelte resources. Our mission is to create high-quality, maintainable, and accessible tools for the 178 | Svelte ecosystem. 179 | 180 | ### Join the Community 181 | 182 | - 🌟 Star our repositories 183 | - 🤝 Contribute to projects 184 | - 📢 Share your ideas 185 | - 👥 Open memberships for everyone 186 | 187 | If you like this project, you can be one of the friends by contributing to the project. Memberships are open for 188 | everyone. 189 | 190 | ## License 191 | 192 | MIT Licensed. Copyright (c) 2023-2024 Friend of Svelte. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@friendofsvelte/tipex", 3 | "version": "0.0.7-fix-1", 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 package", 24 | "preview": "vite preview", 25 | "package": "svelte-kit sync && svelte-package && publint", 26 | "prepareTipexCss": "postcss --dir ./dist/tipex/styles --config ./postcss.config.js ./dist/tipex/styles/*.css && postcss src/app.postcss -o dist/tipex/styles/app.css", 27 | "packageTipex": "npm run package && npm run prepareTipexCss", 28 | "prepublishOnly": "npm run packageTipex", 29 | "test": "npm run test:integration && npm run test:unit", 30 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 31 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 32 | "lint": "prettier --plugin-search-dir . --check . && eslint .", 33 | "format": "prettier --plugin-search-dir . --write .", 34 | "test:integration": "playwright test", 35 | "test:unit": "vitest" 36 | }, 37 | "exports": { 38 | ".": { 39 | "types": "./dist/index.d.ts", 40 | "svelte": "./dist/index.js" 41 | }, 42 | "./styles/*.css": "./dist/tipex/styles/*.css" 43 | }, 44 | "files": [ 45 | "dist", 46 | "!dist/**/*.test.*", 47 | "!dist/**/*.spec.*" 48 | ], 49 | "peerDependencies": { 50 | "svelte": "^5.0.0" 51 | }, 52 | "devDependencies": { 53 | "@friendofsvelte/toggle": "0.0.2-svelte.5.docup", 54 | "@playwright/test": "^1.28.1", 55 | "@sveltejs/adapter-cloudflare": "^4.7.4", 56 | "@tailwindcss/typography": "^0.5.13", 57 | "svelte-highlight": "^7.4.1", 58 | "tslib": "^2.4.1", 59 | "@sveltejs/kit": "^2.0.0", 60 | "@sveltejs/package": "^2.0.0", 61 | "@sveltejs/vite-plugin-svelte": "^4.0.0", 62 | "@types/eslint": "^9.6.0", 63 | "autoprefixer": "^10.4.20", 64 | "eslint": "^9.7.0", 65 | "eslint-config-prettier": "^9.1.0", 66 | "eslint-plugin-svelte": "^2.36.0", 67 | "globals": "^15.0.0", 68 | "prettier": "^3.3.2", 69 | "prettier-plugin-svelte": "^3.2.6", 70 | "prettier-plugin-tailwindcss": "^0.6.5", 71 | "publint": "^0.2.0", 72 | "svelte": "^5.0.0", 73 | "svelte-check": "^4.0.0", 74 | "tailwindcss": "^3.4.9", 75 | "typescript": "^5.0.0", 76 | "typescript-eslint": "^8.0.0", 77 | "vite": "^5.0.11", 78 | "vitest": "^2.0.4", 79 | "postcss": "^8.4.38", 80 | "postcss-cli": "^10.1.0", 81 | "postcss-load-config": "^4.0.1" 82 | }, 83 | "svelte": "./dist/index.js", 84 | "types": "./dist/index.d.ts", 85 | "type": "module", 86 | "dependencies": { 87 | "@tiptap/core": "^2.1.13", 88 | "@tiptap/extension-code-block": "^2.1.13", 89 | "@tiptap/extension-code-block-lowlight": "^2.1.13", 90 | "@tiptap/extension-floating-menu": "^2.1.13", 91 | "@tiptap/extension-image": "^2.1.13", 92 | "@tiptap/extension-link": "^2.1.13", 93 | "@tiptap/extension-placeholder": "^2.1.13", 94 | "@tiptap/pm": "^2.1.13", 95 | "@tiptap/starter-kit": "^2.1.13", 96 | "iconify-icon": "^1.0.8", 97 | "lowlight": "^2.9.0" 98 | }, 99 | "overrides": {} 100 | } 101 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const tailwindcss = require('tailwindcss'); 2 | const autoprefixer = require('autoprefixer'); 3 | 4 | const config = { 5 | plugins: [ 6 | //Some plugins, like tailwindcss/nesting, need to run before Tailwind, 7 | tailwindcss(), 8 | //But others, like autoprefixer, need to run after, 9 | autoprefixer 10 | ] 11 | }; 12 | 13 | module.exports = config; 14 | -------------------------------------------------------------------------------- /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 | export interface GithubRepo { 12 | id: number; 13 | node_id: string; 14 | name: string; 15 | full_name: string; 16 | private: boolean; 17 | owner: Owner; 18 | html_url: string; 19 | description: string; 20 | fork: boolean; 21 | url: string; 22 | forks_url: string; 23 | keys_url: string; 24 | collaborators_url: string; 25 | teams_url: string; 26 | hooks_url: string; 27 | issue_events_url: string; 28 | events_url: string; 29 | assignees_url: string; 30 | branches_url: string; 31 | tags_url: string; 32 | blobs_url: string; 33 | git_tags_url: string; 34 | git_refs_url: string; 35 | trees_url: string; 36 | statuses_url: string; 37 | languages_url: string; 38 | stargazers_url: string; 39 | contributors_url: string; 40 | subscribers_url: string; 41 | subscription_url: string; 42 | commits_url: string; 43 | git_commits_url: string; 44 | comments_url: string; 45 | issue_comment_url: string; 46 | contents_url: string; 47 | compare_url: string; 48 | merges_url: string; 49 | archive_url: string; 50 | downloads_url: string; 51 | issues_url: string; 52 | pulls_url: string; 53 | milestones_url: string; 54 | notifications_url: string; 55 | labels_url: string; 56 | releases_url: string; 57 | deployments_url: string; 58 | created_at: string; 59 | updated_at: string; 60 | pushed_at: string; 61 | git_url: string; 62 | ssh_url: string; 63 | clone_url: string; 64 | svn_url: string; 65 | homepage: string; 66 | size: number; 67 | stargazers_count: number; 68 | watchers_count: number; 69 | language: string; 70 | has_issues: boolean; 71 | has_projects: boolean; 72 | has_downloads: boolean; 73 | has_wiki: boolean; 74 | has_pages: boolean; 75 | has_discussions: boolean; 76 | forks_count: number; 77 | mirror_url: any; 78 | archived: boolean; 79 | disabled: boolean; 80 | open_issues_count: number; 81 | license: any; 82 | allow_forking: boolean; 83 | is_template: boolean; 84 | web_commit_signoff_required: boolean; 85 | topics: string[]; 86 | visibility: string; 87 | forks: number; 88 | open_issues: number; 89 | watchers: number; 90 | default_branch: string; 91 | temp_clone_token: any; 92 | custom_properties: CustomProperties; 93 | organization: Organization; 94 | network_count: number; 95 | subscribers_count: number; 96 | } 97 | 98 | export interface Owner { 99 | login: string; 100 | id: number; 101 | node_id: string; 102 | avatar_url: string; 103 | gravatar_id: string; 104 | url: string; 105 | html_url: string; 106 | followers_url: string; 107 | following_url: string; 108 | gists_url: string; 109 | starred_url: string; 110 | subscriptions_url: string; 111 | organizations_url: string; 112 | repos_url: string; 113 | events_url: string; 114 | received_events_url: string; 115 | type: string; 116 | site_admin: boolean; 117 | } 118 | 119 | export interface CustomProperties {} 120 | 121 | export interface Organization { 122 | login: string; 123 | id: number; 124 | node_id: string; 125 | avatar_url: string; 126 | gravatar_id: string; 127 | url: string; 128 | html_url: string; 129 | followers_url: string; 130 | following_url: string; 131 | gists_url: string; 132 | starred_url: string; 133 | subscriptions_url: string; 134 | organizations_url: string; 135 | repos_url: string; 136 | events_url: string; 137 | received_events_url: string; 138 | type: string; 139 | site_admin: boolean; 140 | } 141 | } 142 | 143 | export {}; 144 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %sveltekit.head% 9 | 10 | 11 | 18 | 19 | 20 |
%sveltekit.body%
21 | 22 | 23 | -------------------------------------------------------------------------------- /src/app.postcss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/fonts/work-sans.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'work-sans'; 3 | font-style: normal; 4 | font-weight: 900; 5 | src: local('sans-serif'), url('./work-sans/WorkSans-Black.ttf') format('truetype'); 6 | font-display: swap; 7 | } 8 | 9 | @font-face { 10 | font-family: 'work-sans'; 11 | font-style: italic; 12 | font-weight: 900; 13 | src: local('sans-serif'), url('./work-sans/WorkSans-BlackItalic.ttf') format('truetype'); 14 | font-display: swap; 15 | } 16 | 17 | @font-face { 18 | font-family: 'work-sans'; 19 | font-style: normal; 20 | font-weight: 700; 21 | src: local('sans-serif'), url('./work-sans/WorkSans-Bold.ttf') format('truetype'); 22 | font-display: swap; 23 | } 24 | 25 | @font-face { 26 | font-family: 'work-sans'; 27 | font-style: italic; 28 | font-weight: 700; 29 | src: local('sans-serif'), url('./work-sans/WorkSans-BoldItalic.ttf') format('truetype'); 30 | font-display: swap; 31 | } 32 | 33 | @font-face { 34 | font-family: 'work-sans'; 35 | font-style: normal; 36 | font-weight: 800; 37 | src: local('sans-serif'), url('./work-sans/WorkSans-ExtraBold.ttf') format('truetype'); 38 | font-display: swap; 39 | } 40 | 41 | @font-face { 42 | font-family: 'work-sans'; 43 | font-style: italic; 44 | font-weight: 800; 45 | src: local('sans-serif'), url('./work-sans/WorkSans-ExtraBoldItalic.ttf') format('truetype'); 46 | font-display: swap; 47 | } 48 | 49 | @font-face { 50 | font-family: 'work-sans'; 51 | font-style: normal; 52 | font-weight: 200; 53 | src: local('sans-serif'), url('./work-sans/WorkSans-ExtraLight.ttf') format('truetype'); 54 | font-display: swap; 55 | } 56 | 57 | @font-face { 58 | font-family: 'work-sans'; 59 | font-style: italic; 60 | font-weight: 200; 61 | src: local('sans-serif'), url('./work-sans/WorkSans-ExtraLightItalic.ttf') format('truetype'); 62 | font-display: swap; 63 | } 64 | 65 | @font-face { 66 | font-family: 'work-sans'; 67 | font-style: italic; 68 | font-weight: 400; 69 | src: local('sans-serif'), url('./work-sans/WorkSans-Italic.ttf') format('truetype'); 70 | font-display: swap; 71 | } 72 | 73 | @font-face { 74 | font-family: 'work-sans'; 75 | font-style: normal; 76 | font-weight: 300; 77 | src: local('sans-serif'), url('./work-sans/WorkSans-Light.ttf') format('truetype'); 78 | font-display: swap; 79 | } 80 | 81 | @font-face { 82 | font-family: 'work-sans'; 83 | font-style: italic; 84 | font-weight: 300; 85 | src: local('sans-serif'), url('./work-sans/WorkSans-LightItalic.ttf') format('truetype'); 86 | font-display: swap; 87 | } 88 | 89 | @font-face { 90 | font-family: 'work-sans'; 91 | font-style: normal; 92 | font-weight: 500; 93 | src: local('sans-serif'), url('./work-sans/WorkSans-Medium.ttf') format('truetype'); 94 | font-display: swap; 95 | } 96 | 97 | @font-face { 98 | font-family: 'work-sans'; 99 | font-style: italic; 100 | font-weight: 500; 101 | src: local('sans-serif'), url('./work-sans/WorkSans-MediumItalic.ttf') format('truetype'); 102 | font-display: swap; 103 | } 104 | 105 | @font-face { 106 | font-family: 'work-sans'; 107 | font-style: normal; 108 | font-weight: 400; 109 | src: local('sans-serif'), url('./work-sans/WorkSans-Regular.ttf') format('truetype'); 110 | font-display: swap; 111 | } 112 | 113 | @font-face { 114 | font-family: 'work-sans'; 115 | font-style: normal; 116 | font-weight: 600; 117 | src: local('sans-serif'), url('./work-sans/WorkSans-SemiBold.ttf') format('truetype'); 118 | font-display: swap; 119 | } 120 | 121 | @font-face { 122 | font-family: 'work-sans'; 123 | font-style: italic; 124 | font-weight: 600; 125 | src: local('sans-serif'), url('./work-sans/WorkSans-SemiBoldItalic.ttf') format('truetype'); 126 | font-display: swap; 127 | } 128 | 129 | @font-face { 130 | font-family: 'work-sans'; 131 | font-style: normal; 132 | font-weight: 100; 133 | src: local('sans-serif'), url('./work-sans/WorkSans-Thin.ttf') format('truetype'); 134 | font-display: swap; 135 | } 136 | 137 | @font-face { 138 | font-family: 'work-sans'; 139 | font-style: italic; 140 | font-weight: 100; 141 | src: local('sans-serif'), url('./work-sans/WorkSans-ThinItalic.ttf') format('truetype'); 142 | font-display: swap; 143 | } -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-Black.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-BlackItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-Bold.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-BoldItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-ExtraBold.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-ExtraLight.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-Italic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-Light.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-LightItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-Medium.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-MediumItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-Regular.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-SemiBold.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-Thin.ttf -------------------------------------------------------------------------------- /src/fonts/work-sans/WorkSans-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/src/fonts/work-sans/WorkSans-ThinItalic.ttf -------------------------------------------------------------------------------- /src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import {sequence} from "@sveltejs/kit/hooks"; 2 | import {handleAppearance} from "@friendofsvelte/toggle"; 3 | 4 | 5 | export const handle = sequence( 6 | handleAppearance 7 | ); 8 | 9 | -------------------------------------------------------------------------------- /src/icons/SimpleIconsBuymeacoffee.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/icons/SimpleIconsGithub.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/icons/SimpleIconsNpm.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/item/Footer.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/item/ParamSpan.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | {name}(tipex) -------------------------------------------------------------------------------- /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 | 85 | 86 | 87 | 88 | 89 | 90 |
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''Class to be applied to the editor.
stylestring''Style to be applied to the editor.
focalbooleantrueFocus outline when editing (blue ring).
controlsbooleanfalseDisplay the default controls.
ctxId{'${string}_tipex'}_tipexSets context ID to get editor instance via getContext.
91 |
92 | 93 |
94 |

Note: 95 | For better customization, please have a look at [Slot Props] 96 |

97 | -------------------------------------------------------------------------------- /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 | 26 | 27 | 28 | 31 | 32 | 33 | 36 | 37 | 38 | 41 | 42 | 43 | 47 | 48 | 49 | 52 | 53 | 54 | 58 | 59 | 60 |
Prop NameDefaultConditionDescription
19 | 20 | undefinedOptionalA slot that accepts a function receiving the TipexEditor instance, rendered above the main editor content 24 | area 25 |
29 | 30 | undefinedOnly used when controls=falseA custom control component that receives the editor instance. Cannot be used together with the utilities 34 | prop 35 |
39 | 40 | UtilityOnly used when controls=true 44 | Custom utility components rendered within the default Controls component. Cannot 45 | be used together with controlComponent 46 |
50 | 51 | undefinedOptional 55 | A slot that accepts a function receiving the TipexEditor instance, 56 | rendered below the editor content and controls 57 |
61 |
62 |
63 |

Note: 64 | These props are mutually exclusive based on the control mode: 65 |

66 |
    67 |
  • When controls=true: Only utilities can be used
  • 68 |
  • When controls=false: Only controlComponent can be used
  • 69 |
70 | 71 |

72 | This is enforced by the WithControlsOn and WithControlsOff type interfaces. 73 |

74 |
75 | -------------------------------------------------------------------------------- /src/item/ThemeToggle.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 14 | -------------------------------------------------------------------------------- /src/item/codes/advanceCodes.ts: -------------------------------------------------------------------------------- 1 | const customizeControlImplementation = ``; 4 | 5 | const addHeadFootComponent = ` 11 | 12 | 13 | {#snippet head(tipex)} 14 | 15 | {/snippet} 16 | {#snippet controlComponent(tipex)} 17 | 18 | {/snippet} 19 | {#snippet foot(tipex)} 20 | 21 | {/snippet} 22 | `; 23 | 24 | const tweakingExtensions = ` 33 | 34 | `; 35 | 36 | export default { 37 | customizeControlImplementation, 38 | addHeadFootComponent, 39 | tweakingExtensions 40 | }; 41 | -------------------------------------------------------------------------------- /src/item/codes/index.ts: -------------------------------------------------------------------------------- 1 | export const insertUtils = `import {Utility} from "@friendofsvelte/tipex"; 2 | 3 | {#snippet utilities(tipex)} 4 | 5 | {/snippet} 6 | `; 7 | 8 | export const appendUtils = ` 9 | {#snippet utilities(tipex)} 10 |
...
11 | {/snippet} 12 |
`; 13 | 14 | export const overrideControl = ` 15 | {#snippet controlComponent(tipex)} 16 |
...
17 | {/snippet} 18 |
`; 19 | 20 | export const install = `npm install "@friendofsvelte/tipex";`; 21 | export const usage = ` 25 | 26 | 27 | 30 | `; 31 | 32 | let body = ` 33 |

This content was written by Bishwas in 2023. You can edit this content and see the changes in the editor.

Do you have any questions? Feel free to ask in the Github repository.

Try writing some code, list, or blockquote, and see how it looks in the editor.

34 |
35 |

I hope you enjoyed it 😊

36 | `; 37 | 38 | export let styling = `import "@friendofsvelte/tipex/styles/Tipex.css"; 39 | import "@friendofsvelte/tipex/styles/ProseMirror.css"; 40 | import "@friendofsvelte/tipex/styles/Controls.css"; 41 | import "@friendofsvelte/tipex/styles/EditLink.css"; 42 | import "@friendofsvelte/tipex/styles/CodeBlock.css";`; 43 | 44 | export let access = ``; 45 | 46 | const codes = { 47 | insertUtils, 48 | install, 49 | usage, 50 | appendUtils, 51 | overrideControl, 52 | body, 53 | access, 54 | styling 55 | }; 56 | export default codes; 57 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Tipex, type TipexEditor, type TipexProps } from './tipex/Tipex.svelte'; 2 | export { default as Utility, type UtilityProps } from './tipex/Utility.svelte'; 3 | export { defaultExtensions } from './tipex/default.js'; 4 | -------------------------------------------------------------------------------- /src/lib/tipex/Controls.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 22 | 23 | {#if tipex} 24 |
25 |
26 | 35 | 36 | 45 | 46 | 55 | 56 | 65 | 66 | 75 | 76 | 85 | 86 |
87 | {@render children?.()} 88 |
89 | {/if} 90 | -------------------------------------------------------------------------------- /src/lib/tipex/Tipex.svelte: -------------------------------------------------------------------------------- 1 | 80 | 81 | 154 | 155 | 156 | 157 | {#if floating} 158 | 159 | {/if} 160 | 161 |
162 |
163 | {@render head?.(tipex)} 164 |
165 | {#if controls} 166 | 167 | 168 | {#if utilities} 169 |
170 | {@render utilities?.(tipex)} 171 |
172 | {:else} 173 | 174 |
175 | 176 |
177 | {/if} 178 |
179 | {:else} 180 | {@render controlComponent?.(tipex)} 181 | {/if} 182 | {@render foot?.(tipex)} 183 |
184 |
185 | -------------------------------------------------------------------------------- /src/lib/tipex/Utility.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | {#if !enableLinkEdit} 26 | 33 | {@render children?.()} 34 | {/if} 35 | 36 | 37 | -------------------------------------------------------------------------------- /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 { lowlight } from 'lowlight'; 6 | 7 | export const defaultExtensions = [ 8 | Link.configure({ 9 | openOnClick: false, 10 | HTMLAttributes: { 11 | target: '_blank', 12 | rel: 'noopener noreferrer' 13 | } 14 | }), 15 | Image.configure({ 16 | allowBase64: true 17 | }), 18 | Placeholder.configure({ 19 | showOnlyWhenEditable: false 20 | }), 21 | CodeBlockLowlight.configure({ 22 | lowlight, 23 | languageClassPrefix: 'language-', 24 | defaultLanguage: 'plaintext' 25 | }) 26 | ]; 27 | -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidArrowUpRightFromSquare.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidBold.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidCheck.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidCode.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidCopy.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidItalic.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidLink.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidParagraph.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/lib/tipex/icons/Fa6SolidXmark.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | {#if display} 15 | 21 | 22 | 23 | {:else if occupy} 24 |
25 | {/if} -------------------------------------------------------------------------------- /src/lib/tipex/link/EditLinkMenu.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 53 | 54 | 66 | 67 | {#if enableLinkEdit} 68 | 76 | {/if} 77 | -------------------------------------------------------------------------------- /src/lib/tipex/link/LinkFloatingMenu.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 46 | 47 |
51 | 54 | 57 | 60 |
61 | -------------------------------------------------------------------------------- /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/lib/tipex/styles/CodeBlock.css: -------------------------------------------------------------------------------- 1 | pre code.hljs { 2 | display: block; 3 | overflow-x: auto; 4 | padding: 1em; 5 | } 6 | 7 | code.hljs { 8 | padding: 3px 5px; 9 | } 10 | 11 | /*! 12 | Theme: GitHub 13 | Description: Light theme as seen on github.com 14 | Author: github.com 15 | Maintainer: @Hirse 16 | Updated: 2021-05-15 17 | 18 | Outdated base version: https://github.com/primer/github-syntax-light 19 | Current colors taken from GitHub's CSS 20 | */ 21 | .hljs { 22 | color: #24292e; 23 | background: #ffffff; 24 | } 25 | 26 | .hljs-doctag, 27 | .hljs-keyword, 28 | .hljs-meta .hljs-keyword, 29 | .hljs-template-tag, 30 | .hljs-template-variable, 31 | .hljs-type, 32 | .hljs-variable.language_ { 33 | /* prettylights-syntax-keyword */ 34 | color: #d73a49; 35 | } 36 | 37 | .hljs-title, 38 | .hljs-title.class_, 39 | .hljs-title.class_.inherited__, 40 | .hljs-title.function_ { 41 | /* prettylights-syntax-entity */ 42 | color: #6f42c1; 43 | } 44 | 45 | .hljs-attr, 46 | .hljs-attribute, 47 | .hljs-literal, 48 | .hljs-meta, 49 | .hljs-number, 50 | .hljs-operator, 51 | .hljs-variable, 52 | .hljs-selector-attr, 53 | .hljs-selector-class, 54 | .hljs-selector-id { 55 | /* prettylights-syntax-constant */ 56 | color: #005cc5; 57 | } 58 | 59 | .hljs-regexp, 60 | .hljs-string, 61 | .hljs-meta .hljs-string { 62 | /* prettylights-syntax-string */ 63 | color: #032f62; 64 | } 65 | 66 | .hljs-built_in, 67 | .hljs-symbol { 68 | /* prettylights-syntax-variable */ 69 | color: #e36209; 70 | } 71 | 72 | .hljs-comment, 73 | .hljs-code, 74 | .hljs-formula { 75 | /* prettylights-syntax-comment */ 76 | color: #6a737d; 77 | } 78 | 79 | .hljs-name, 80 | .hljs-quote, 81 | .hljs-selector-tag, 82 | .hljs-selector-pseudo { 83 | /* prettylights-syntax-entity-tag */ 84 | color: #22863a; 85 | } 86 | 87 | .hljs-subst { 88 | /* prettylights-syntax-storage-modifier-import */ 89 | color: #24292e; 90 | } 91 | 92 | .hljs-section { 93 | /* prettylights-syntax-markup-heading */ 94 | color: #005cc5; 95 | font-weight: bold; 96 | } 97 | 98 | .hljs-bullet { 99 | /* prettylights-syntax-markup-list */ 100 | color: #735c0f; 101 | } 102 | 103 | .hljs-emphasis { 104 | /* prettylights-syntax-markup-italic */ 105 | color: #24292e; 106 | font-style: italic; 107 | } 108 | 109 | .hljs-strong { 110 | /* prettylights-syntax-markup-bold */ 111 | color: #24292e; 112 | font-weight: bold; 113 | } 114 | 115 | .hljs-addition { 116 | /* prettylights-syntax-markup-inserted */ 117 | color: #22863a; 118 | background-color: #f0fff4; 119 | } 120 | 121 | .hljs-deletion { 122 | /* prettylights-syntax-markup-deleted */ 123 | color: #b31d28; 124 | background-color: #ffeef0; 125 | } 126 | 127 | .hljs-char.escape_, 128 | .hljs-link, 129 | .hljs-params, 130 | .hljs-property, 131 | .hljs-punctuation, 132 | .hljs-tag { 133 | /* purposely ignored */ 134 | } 135 | 136 | /*! 137 | Theme: GitHub Dark Dimmed 138 | Description: Dark dimmed theme as seen on github.com 139 | Author: github.com 140 | Maintainer: @Hirse 141 | Updated: 2021-05-15 142 | 143 | Colors taken from GitHub's CSS 144 | */ 145 | .dark .hljs { 146 | color: #adbac7; 147 | background: #22272e; 148 | } 149 | 150 | .dark .hljs-doctag, 151 | .dark .hljs-keyword, 152 | .dark .hljs-meta .hljs-keyword, 153 | .dark .hljs-template-tag, 154 | .dark .hljs-template-variable, 155 | .dark .hljs-type, 156 | .dark .hljs-variable.language_ { 157 | /* prettylights-syntax-keyword */ 158 | color: #f47067; 159 | } 160 | 161 | .dark .hljs-title, 162 | .dark .hljs-title.class_, 163 | .dark .hljs-title.class_.inherited__, 164 | .dark .hljs-title.function_ { 165 | /* prettylights-syntax-entity */ 166 | color: #dcbdfb; 167 | } 168 | 169 | .dark .hljs-attr, 170 | .dark .hljs-attribute, 171 | .dark .hljs-literal, 172 | .dark .hljs-meta, 173 | .dark .hljs-number, 174 | .dark .hljs-operator, 175 | .dark .hljs-variable, 176 | .dark .hljs-selector-attr, 177 | .dark .hljs-selector-class, 178 | .dark .hljs-selector-id { 179 | /* prettylights-syntax-constant */ 180 | color: #6cb6ff; 181 | } 182 | 183 | .dark .hljs-regexp, 184 | .dark .hljs-string, 185 | .dark .hljs-meta .hljs-string { 186 | /* prettylights-syntax-string */ 187 | color: #96d0ff; 188 | } 189 | 190 | .dark .hljs-built_in, 191 | .dark .hljs-symbol { 192 | /* prettylights-syntax-variable */ 193 | color: #f69d50; 194 | } 195 | 196 | .dark .hljs-comment, 197 | .dark .hljs-code, 198 | .dark .hljs-formula { 199 | /* prettylights-syntax-comment */ 200 | color: #768390; 201 | } 202 | 203 | .dark .hljs-name, 204 | .dark .hljs-quote, 205 | .dark .hljs-selector-tag, 206 | .dark .hljs-selector-pseudo { 207 | /* prettylights-syntax-entity-tag */ 208 | color: #8ddb8c; 209 | } 210 | 211 | .dark .hljs-subst { 212 | /* prettylights-syntax-storage-modifier-import */ 213 | color: #adbac7; 214 | } 215 | 216 | .dark .hljs-section { 217 | /* prettylights-syntax-markup-heading */ 218 | color: #316dca; 219 | font-weight: bold; 220 | } 221 | 222 | .dark .hljs-bullet { 223 | /* prettylights-syntax-markup-list */ 224 | color: #eac55f; 225 | } 226 | 227 | .dark .hljs-emphasis { 228 | /* prettylights-syntax-markup-italic */ 229 | color: #adbac7; 230 | font-style: italic; 231 | } 232 | 233 | .dark .hljs-strong { 234 | /* prettylights-syntax-markup-bold */ 235 | color: #adbac7; 236 | font-weight: bold; 237 | } 238 | 239 | .dark .hljs-addition { 240 | /* prettylights-syntax-markup-inserted */ 241 | color: #b4f1b4; 242 | background-color: #1b4721; 243 | } 244 | 245 | .dark .hljs-deletion { 246 | /* prettylights-syntax-markup-deleted */ 247 | color: #ffd8d3; 248 | background-color: #78191b; 249 | } 250 | 251 | .dark .hljs-char.escape_, 252 | .dark .hljs-link, 253 | .dark .hljs-params, 254 | .dark .hljs-property, 255 | .dark .hljs-punctuation, 256 | .dark .hljs-tag { 257 | /* purposely ignored */ 258 | } 259 | -------------------------------------------------------------------------------- /src/lib/tipex/styles/Controls.css: -------------------------------------------------------------------------------- 1 | 2 | .tipex-controller { 3 | @apply flex flex-row flex-wrap justify-between md:flex-row gap-2 sticky bottom-0 z-10 py-2 px-3 4 | rounded-b-md backdrop-filter backdrop-blur-sm 5 | bg-neutral-200 dark:bg-gray-800/80 6 | border-t border-neutral-300 dark:border-gray-700; 7 | } 8 | 9 | .tipex-basic-controller-wrapper { 10 | @apply flex gap-2 flex-wrap; 11 | } 12 | 13 | .tipex-edit-button { 14 | @apply bg-neutral-50 text-neutral-700 15 | dark:bg-gray-800 dark:text-gray-200 16 | border dark:border-gray-700 border-neutral-400 17 | dark:hover:bg-neutral-600/5 hover:bg-gray-100 18 | dark:hover:border-gray-600 19 | rounded-md flex justify-center items-center 20 | duration-100 cursor-pointer; 21 | } 22 | 23 | .tipex-button-rigid { 24 | @apply px-2 py-1 h-9 w-9; 25 | } 26 | 27 | .tipex-button-free { 28 | @apply px-2 py-1 h-9; 29 | } 30 | 31 | .tipex-button-extra { 32 | @apply hover:scale-105 active:scale-90 focus:scale-105 focus:shadow active:border-neutral-900 dark:active:border-white; 33 | } 34 | 35 | .tipex-edit-button.active { 36 | @apply dark:bg-gray-800 bg-zinc-200/70 border-neutral-700 dark:border-zinc-300 37 | text-zinc-700 dark:text-zinc-300; 38 | } -------------------------------------------------------------------------------- /src/lib/tipex/styles/EditLink.css: -------------------------------------------------------------------------------- 1 | 2 | .tipex-floating-button { 3 | @apply flex flex-row items-center justify-center 4 | border dark:border-neutral-700/60 border-neutral-200/70 5 | rounded-lg 6 | hover:bg-neutral-200 dark:hover:bg-neutral-800 7 | h-6 w-6; 8 | } 9 | 10 | .tipex-floating-group { 11 | @apply rounded-lg shadow 12 | text-gray-600 dark:text-gray-400 13 | text-xs py-0.5 px-1 14 | z-10 15 | flex flex-row gap-0.5 items-center 16 | bg-neutral-100 dark:bg-neutral-800; 17 | } 18 | 19 | 20 | .tipex-link-edit-input-group input { 21 | @apply focus:outline-0 pl-2 pr-1 w-full text-gray-950 dark:text-gray-200 text-xs truncate 22 | bg-gray-300 dark:bg-gray-700 dark:placeholder-gray-400 placeholder-gray-600 23 | dark:focus:placeholder-gray-400 focus:placeholder-gray-600 rounded-md; 24 | } 25 | 26 | .tipex-link-edit-top { 27 | @apply flex rounded-md; 28 | } 29 | 30 | .tipex-link-edit-input-group { 31 | @apply flex gap-2 h-full w-full max-w-xl; 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/tipex/styles/ProseMirror.css: -------------------------------------------------------------------------------- 1 | 2 | .tipex-editor-section .ProseMirror { 3 | @apply outline-none 4 | px-2.5 pb-1.5 h-full 5 | text-gray-800 dark:text-gray-200; 6 | } 7 | 8 | .tipex-editor-section .ProseMirror pre { 9 | @apply bg-gray-200 text-blue-800 text-sm 10 | dark:bg-gray-800 dark:text-blue-300 11 | px-4 py-7 mt-7 ml-2 mr-4 12 | overflow-x-hidden 13 | mb-3; 14 | } 15 | 16 | .tipex-editor-section .ProseMirror code { 17 | @apply bg-gray-200 text-blue-800 18 | dark:bg-gray-800 dark:text-blue-300 19 | p-1 rounded; 20 | } 21 | 22 | .tipex-editor-section .ProseMirror h1 { 23 | @apply text-2xl font-black dark:text-gray-200 text-gray-950 24 | my-3; 25 | } 26 | 27 | .tipex-editor-section .ProseMirror h2 { 28 | @apply text-xl font-bold 29 | my-2; 30 | } 31 | 32 | .tipex-editor-section .ProseMirror h3 { 33 | @apply text-lg font-semibold 34 | my-1; 35 | } 36 | 37 | 38 | .tipex-editor-section .ProseMirror img[src] { 39 | @apply mb-2; 40 | } 41 | 42 | .tipex-editor-section .ProseMirror h4, 43 | .tipex-editor-section .ProseMirror h5, 44 | .tipex-editor-section .ProseMirror h6 { 45 | @apply text-base font-medium 46 | mb-1; 47 | } 48 | 49 | .tipex-editor-section .ProseMirror p { 50 | @apply text-base 51 | mb-2; 52 | } 53 | 54 | .tipex-editor-section .ProseMirror a[href] { 55 | @apply text-blue-500 hover:underline 56 | hover:text-blue-600; 57 | } 58 | 59 | .tipex-editor-section .ProseMirror ul { 60 | @apply list-disc ml-7; 61 | } 62 | 63 | 64 | .tipex-editor-section .ProseMirror ol { 65 | @apply list-decimal ml-7; 66 | } 67 | 68 | .tipex-editor-section .ProseMirror li { 69 | } 70 | 71 | .tipex-editor-section .ProseMirror li { 72 | @apply mb-1; 73 | } 74 | 75 | .tipex-editor-section .ProseMirror blockquote { 76 | @apply border-l-4 border-gray-300 pl-2 77 | mb-2 ml-2; 78 | } 79 | 80 | .tipex-editor-section .ProseMirror img { 81 | @apply max-w-full; 82 | } 83 | 84 | .tipex-editor-section .ProseMirror hr { 85 | @apply border border-gray-300 dark:border-gray-600/30; 86 | } 87 | 88 | .tipex-editor-section .ProseMirror del { 89 | @apply line-through; 90 | } 91 | 92 | .tipex-editor-section .ProseMirror em { 93 | @apply italic; 94 | } 95 | 96 | .tipex-editor-section .ProseMirror strong { 97 | @apply font-bold; 98 | } 99 | 100 | .tipex-editor-section .ProseMirror .is-empty::before { 101 | content: attr(data-placeholder); 102 | float: left; 103 | height: 0; 104 | @apply pointer-events-none text-gray-400 105 | dark:text-gray-600; 106 | } -------------------------------------------------------------------------------- /src/lib/tipex/styles/Tipex.css: -------------------------------------------------------------------------------- 1 | 2 | .tipex-editor { 3 | @apply flex flex-row 4 | gap-0 5 | dark:bg-neutral-900 bg-neutral-50 6 | rounded-md 7 | py-0 8 | duration-200 overflow-y-hidden; 9 | } 10 | 11 | .tipex-editor-wrap { 12 | @apply flex flex-col gap-0 h-full w-full; 13 | } 14 | 15 | .tipex-editor-section { 16 | @apply px-0.5 py-2 17 | h-full 18 | overflow-y-auto; 19 | } 20 | 21 | .tipex-editor.focused.focal { 22 | @apply outline-none ring-4 ring-blue-500 border-transparent; 23 | } 24 | 25 | .tipex-utilities { 26 | @apply flex gap-2 ml-auto max-w-lg w-full justify-end; 27 | } -------------------------------------------------------------------------------- /src/root.css: -------------------------------------------------------------------------------- 1 | @import url('./fonts/work-sans.css'); 2 | 3 | body { 4 | @apply bg-gradient-to-b from-gray-100 to-gray-200 dark:from-stone-900 dark:to-neutral-950 min-h-screen; 5 | font-family: work-sans, sans-serif; 6 | } 7 | 8 | a[href^="http"], a[href^="/"] { 9 | @apply text-blue-500 hover:text-blue-600 10 | dark:text-blue-400 dark:hover:text-blue-300; 11 | } 12 | 13 | pre { 14 | @apply rounded-md 15 | overflow-x-auto 16 | whitespace-pre-wrap 17 | border border-neutral-300 dark:border-neutral-800 shadow-sm; 18 | } 19 | 20 | table { 21 | @apply border-collapse; 22 | } 23 | 24 | th { 25 | @apply px-4 py-2 26 | text-left 27 | border 28 | border-neutral-200 dark:border-stone-900 29 | bg-neutral-100 dark:bg-neutral-800 30 | text-neutral-700 dark:text-neutral-200; 31 | } 32 | 33 | td { 34 | @apply px-4 py-2 35 | border 36 | border-neutral-300 dark:border-stone-900 37 | text-neutral-700 dark:text-neutral-200; 38 | } 39 | 40 | tr { 41 | @apply even:bg-neutral-100 even:dark:bg-neutral-800 42 | odd:bg-neutral-200 odd:dark:bg-neutral-700; 43 | } 44 | 45 | 46 | blockquote { 47 | @apply rounded border-l-4 border-neutral-200 dark:border-neutral-700 48 | bg-neutral-100 dark:bg-neutral-800 49 | text-neutral-700 dark:text-neutral-200 50 | px-4 py-2 51 | mt-3 ml-2 52 | overflow-x-auto; 53 | } 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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | Tipex Editor - Svelte Text Editor | Friend Of Svelte 24 | 26 | 27 | 28 | 29 | 63 | 64 |

65 | Tipex stands as an advanced rich text editor tailored for Svelte, meticulously engineered with the robust 66 | frameworks Tiptap and 67 | Prosemirror. It 68 | empowers developers to effortlessly craft rich text editors, liberating 69 | them from the intricacies of underlying technologies, style management, and related complexities. 70 |

71 | 72 | 73 | 79 | 80 | 81 |

Installation

82 |

83 | Install the package from NPM. 85 |

86 | 87 | 88 |

Usage

89 |

90 | Import the component and use it in your component. 91 |

92 | 93 | 94 |

Quick short-hands

95 |
    96 |
  • 97 | !controls: Disable the control buttons, or controls={false}. 98 |
  • 99 |
  • 100 | !floating: Disable the floating toolbar, or floating={false}. 101 |
  • 102 |
  • 103 | !focal: Disable the focal point, or focal={false}. 104 |
  • 105 |
106 | 107 |

Styling

108 |

109 | Tipex comes with a default style. You can use it by importing the following CSS file inside the 110 | {'script'} tag. 111 |

112 | 113 | 114 | 115 |
116 |

117 | The import for @friendofsvelte/tipex/styles/ProseMirror.css is used to 118 | style content written in the editor. You can use your own style or use the default one. 119 | Or, remove any CSS you don't wanna use. 120 |

121 |
122 | 123 |

Props

124 |

125 | Tipex component accepts following props. 126 |

127 | 128 | 129 |

Accessing Editor Instance

130 |

131 | You can access the editor instance via: 132 |

133 | 134 |

135 | The editor instance is stored in a store. You can use it to access the editor instance 136 | from anywhere in your app. 137 |

138 | 139 |

Customizing Editor

140 |

141 | Tipex is built taking into consideration the need for customization. We believe that 142 | a software lacking customization is a software lacking soul. Tipex provides an extensive 143 | set of options to customize the editor to your heart's content. From functionality, style, 144 | to key bindings, you can customize almost everything. Visit the 145 | customization page to learn more. 146 |

147 | 148 |

Commands

149 |

150 | You can use the editor.commands API to interact with the editor. The API is 151 | compatible with the TipTap 152 | commands. Visit the commands page to learn more. 153 |

154 | 155 |

About Friend Of Svelte

156 |
157 | Friend Of Svelte Logo 159 |

160 | Friend Of 161 | Svelte is a 162 | community driven project to help Svelte developers to find and develop awesome Svelte resources. 163 |

164 |
165 | 166 |

167 | If you like this project, you can be one of the friend by contributing to the project. Memberships are open 168 | for everyone. 169 |

170 |
171 |

172 | Read about my quest on becoming a top notch, master 173 | Svelte developer. 176 |

177 |
178 | 179 | 180 | -------------------------------------------------------------------------------- /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/routes/commands/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | Editor Commands | Tipex Editor 8 | 9 | 10 | 11 | 12 | 13 | Back to Home 14 | 15 | 16 |

Editor Commands

17 | 18 |

19 | Tipex is built on top of TipTap and provides full access to TipTap's powerful command API. This guide covers common editor commands and their usage. 20 |

21 | 22 |

Basic Content Management

23 |

Here are the fundamental commands for managing editor content:

24 | 25 | Hello World

') 27 | 28 | // Insert content at cursor position 29 | editor.commands.insertContent('New content') 30 | 31 | // Clear content 32 | editor.commands.clearContent() 33 | 34 | // Focus editor 35 | editor.commands.focus()`} /> 36 | 37 |

Text Formatting

38 |

Common text formatting commands:

39 | 40 | 51 | 52 |

Working with Lists

53 |

Commands for handling different types of lists:

54 | 55 | tipex.chain().focus().toggleBulletList().run()} 68 | class="tipex-edit-button" 69 | class:active={tipex.isActive('bulletList')} 70 | > 71 | 72 | `} /> 73 | 74 |

Link Management

75 |

Commands for working with links:

76 | 77 | 92 | 93 |

Image Handling

94 |

95 | Tipex supports both base64 and URL-based images. Here's how to handle image uploads: 96 |

97 | 98 | 124 | 125 |

Advanced Usage

126 |

127 | You can chain multiple commands together for complex operations: 128 |

129 | 130 | 140 | 141 |
142 |

143 | Note: Tipex provides full access to TipTap's command API. For a complete reference of all available commands, 144 | visit the 145 | TipTap Commands API Documentation. 146 |

147 |
148 | -------------------------------------------------------------------------------- /src/routes/customization/+page.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | Customization | Tipex Editor 11 | 13 | 14 | 15 | 16 | 17 | Back to Home 18 | 19 | 20 |

Customization

21 |

22 | As we have said, a software having customization is a software having soul. Tipex editor is highly customizable. 23 | You can customize the editor to your heart's content. 24 |

25 | 26 |

In-built Utility Buttons

27 | 28 |

29 | The in-built utility buttons consist of Copy and Link buttons. 30 |

31 | 32 |
33 | Svelte Text Editor: Tipex Editor with custom utility buttons 36 | 37 |
38 | Location of utility buttons in Text Editor 39 |
40 |
41 |

Insert a set of buttons

42 |

43 | Here's a basic example of how you can insert in-built utility buttons. 44 |

45 | 46 | 47 |

Append custom buttons between

48 |

49 | To append new buttons you can use the Utility.svelte component, it has a slot for you 50 | to insert your buttons. 51 |

52 | 53 | 54 | 55 | 56 |

Using new custom controls

57 |

58 | You can override the default controls with your own custom controls. Here's an example of how you can do it. 59 |

60 | 61 | 62 |

63 | Or, you can use {'{#snippet controlComponent}'} to append your buttons. 64 |

65 | 66 |

Image Upload

67 |

68 | Tipex editor supports image upload. You can upload images by dragging and dropping them in the editor, or 69 | by copy pasting them from your clipboard, which is the most common way to upload images. 70 |

71 | 72 |

Custom Image Upload Tab

73 |

74 | You can use utility customization for this. Apply the upper mentioned method to append your custom button. 75 |

76 | 77 |

Advanced Customization

78 | 79 |

80 | Tipex, being a highly customizable editor, allows you to customize the editor to your heart's content and 81 | make it look like your own. Here's some ways you can customize the editor. 82 |

83 | 84 |
85 | Svelte Text Editor: Advanced Customization of Tipex Editor 88 | 89 |
90 | Advanced Customization of Tipex Editor, Source 91 | Code 92 |
93 |
94 | 95 |

Customize the controls

96 | 97 |

98 | You can customize the controls of the editor by passing a controlComponent slot. 99 |

100 | 101 | 102 | 103 |

104 | You can also add a header and footer with using a slot. 105 |

106 | 107 |

Tweaking extensions

108 | 109 |

110 | Tipex provides an defaultExtensions object which contains all the extensions that are used in the 111 | editor. You can tweak the extensions, or add new extensions to the editor by passing a extensions 112 | prop to the editor. 113 |

114 | 115 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendofsvelte/tipex/18aeccdc1660eafbe295ac98983ed51adedd9b25/static/favicon.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config}*/ 2 | const config = { 3 | content: ['./src/**/*.{html,js,svelte,ts}'], 4 | 5 | theme: { 6 | extend: {} 7 | }, 8 | 9 | plugins: [], 10 | 11 | darkMode: 'class' 12 | }; 13 | 14 | module.exports = config; 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | "moduleResolution": "NodeNext", 13 | "allowImportingTsExtensions": false 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()], 6 | test: { 7 | include: ['src/**/*.{test,spec}.{js,ts}'] 8 | }, 9 | define: { 10 | __VERSION__: JSON.stringify(process.env.npm_package_version), 11 | }, 12 | }); 13 | --------------------------------------------------------------------------------