├── .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 | 
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 | Prop
10 | Type
11 | Default
12 | Description
13 |
14 |
15 |
16 |
17 | extensions
18 | Extensions[]
19 | defaultExtensions
20 | Extensions to be used in the editor.
21 |
22 |
23 | floating
24 | boolean
25 | false
26 | Determines display of default floating menu.
27 |
28 |
29 | oncreate
30 | function
31 | {function_string}
32 | Callback function when the editor is created.
33 |
34 |
35 | ondestroy
36 | function
37 | {function_string}
38 | Callback function when the editor is destroyed.
39 |
40 |
41 | onupdate
42 | function
43 | {function_string}
44 | Callback function when the editor is updated.
45 |
46 |
47 | body
48 | string
49 | ''
50 | HTML content to render.
51 |
52 |
53 | this
54 | TipTap
55 | N/A
56 | Instance of the editor.
57 |
58 |
59 | class
60 | string
61 | ''
62 | Class to be applied to the editor.
63 |
64 |
65 | style
66 | string
67 | ''
68 | Style to be applied to the editor.
69 |
70 |
71 | focal
72 | boolean
73 | true
74 | Focus outline when editing (blue ring).
75 |
76 |
77 | controls
78 | boolean
79 | false
80 | Display the default controls.
81 |
82 |
83 | ctxId
84 | {'${string}_tipex'}
85 | _tipex
86 | Sets context ID to get editor instance via getContext.
87 |
88 |
89 |
90 |
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 | Prop Name
11 | Default
12 | Condition
13 | Description
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | undefined
22 | Optional
23 | A slot that accepts a function receiving the TipexEditor instance, rendered above the main editor content
24 | area
25 |
26 |
27 |
28 |
29 |
30 |
31 | undefined
32 | Only used when controls=false
33 | A custom control component that receives the editor instance. Cannot be used together with the utilities
34 | prop
35 |
36 |
37 |
38 |
39 |
40 |
41 | Utility
42 | Only used when controls=true
43 |
44 | Custom utility components rendered within the default Controls component. Cannot
45 | be used together with controlComponent
46 |
47 |
48 |
49 |
50 |
51 |
52 | undefined
53 | Optional
54 |
55 | A slot that accepts a function receiving the TipexEditor instance,
56 | rendered below the editor content and controls
57 |
58 |
59 |
60 |
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 | appearance.dark=!appearance.dark}>
11 |
13 |
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 | tipex?.chain().focus().toggleHeading({ level: 1 }).run()}
28 | class:active={tipex?.isActive('heading', { level: 1 })}
29 | class="tipex-edit-button tipex-button-extra tipex-button-rigid"
30 | aria-label="Heading 1"
31 | type="button"
32 | >
33 | H1
34 |
35 |
36 | tipex?.chain().focus().toggleHeading({ level: 2 }).run()}
38 | class:active={tipex?.isActive('heading', { level: 2 })}
39 | class="tipex-edit-button tipex-button-extra tipex-button-rigid"
40 | aria-label="Heading 2"
41 | type="button"
42 | >
43 | H2
44 |
45 |
46 | tipex?.chain().focus().setParagraph().run()}
48 | class:active={tipex?.isActive('paragraph')}
49 | class="tipex-edit-button tipex-button-extra tipex-button-rigid"
50 | aria-label="Paragraph/Normal text"
51 | type="button"
52 | >
53 |
54 |
55 |
56 | tipex?.chain().focus().toggleBold().run()}
58 | class:active={tipex?.isActive('bold')}
59 | class="tipex-edit-button tipex-button-extra tipex-button-rigid"
60 | aria-label="Bold"
61 | type="button"
62 | >
63 |
64 |
65 |
66 | tipex?.chain().focus().toggleItalic().run()}
68 | class:active={tipex?.isActive('italic')}
69 | class="tipex-edit-button tipex-button-extra tipex-button-rigid"
70 | aria-label="Italic"
71 | type="button"
72 | >
73 |
74 |
75 |
76 | tipex?.chain().focus().toggleCode().run()}
78 | class:active={tipex?.isActive('code')}
79 | class="tipex-edit-button tipex-button-extra tipex-button-rigid"
80 | aria-label="Code"
81 | type="button"
82 | >
83 |
84 |
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 |
69 |
71 |
75 |
76 | {/if}
77 |
--------------------------------------------------------------------------------
/src/lib/tipex/link/LinkFloatingMenu.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
46 |
47 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
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 |
30 |
Tipex Editor
31 |
33 | {__VERSION__}
34 |
35 |
36 |
62 |
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 |
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 |
27 |
28 |
29 | The in-built utility buttons consist of Copy
and Link
buttons.
30 |
31 |
32 |
33 |
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 |
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 |
--------------------------------------------------------------------------------