@magicpages/ghost-typesense-search-ui 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,88 +1,115 @@
1
1
  # @magicpages/ghost-typesense-search-ui
2
2
 
3
- A beautiful, accessible search interface for Ghost blogs using Typesense. Built with vanilla JavaScript.
3
+ A beautiful, accessible search interface for Ghost blogs using Typesense. This package provides a drop-in replacement for Ghost's default search functionality, offering enhanced features and seamless integration with Typesense.
4
4
 
5
5
  ![Search UI Preview](./preview.png)
6
6
 
7
7
  ## Features
8
8
 
9
- - 🔍 Real-time search with Typesense
9
+ - 🔍 Real-time search powered by Typesense (needs a Typesense server)
10
10
  - 🎨 Beautiful, accessible interface
11
- - 🌓 Automatic dark mode
11
+ - 🌓 Automatic dark mode support
12
12
  - ⌨️ Full keyboard navigation
13
- - 📱 Responsive design
14
- - 🎯 Common searches support
13
+ - 📱 Responsive design for all devices
14
+ - 🎯 Configurable common searches suggestions
15
+ - ⚡ Lightweight and performant
15
16
 
16
17
  ## Installation
17
18
 
18
- ### Option 1: Replace Ghost's `sodoSearch` (Recommended if you can edit your config)
19
+ There are two ways to integrate the search UI into your Ghost site:
20
+
21
+ ### Option 1: Replace Ghost's Default Search (Recommended)
22
+
23
+ This is the preferred method as it prevents loading two search scripts, resulting in better performance. You'll need access to your Ghost configuration.
19
24
 
20
25
  Add to your `config.[environment].json`:
21
26
  ```json
22
27
  "sodoSearch": {
23
- "url": "[link to search.min.js]"
28
+ "url": "https://unpkg.com/@magicpages/ghost-typesense-search-ui/dist/search.min.js"
24
29
  }
25
30
  ```
26
31
 
27
- Or set environment variable:
32
+ Or set the environment variable:
28
33
  ```bash
29
- sodoSearch__url=[link to search.min.js]
34
+ sodoSearch__url=https://unpkg.com/@magicpages/ghost-typesense-search-ui/dist/search.min.js
30
35
  ```
31
36
 
32
- ### Option 2: Direct Installation (Works if you can't edit your config, e.g. on Ghost(Pro))
37
+ ### Option 2: Code Injection Method
33
38
 
34
- Add to your site's code injection:
39
+ If you're using a managed Ghost host like Ghost(Pro) where you can't modify the configuration, use this method. The script will automatically remove any traces of the default search to prevent conflicts, but cannot prevent the `sodo-search.min.js` from being loaded.
35
40
 
36
- ```html
41
+ Add to your site's code injection (Settings → Code injection → Site Header):
37
42
 
43
+ ```html
38
44
  <script src="https://unpkg.com/@magicpages/ghost-typesense-search-ui/dist/search.min.js"></script>
39
45
  ```
40
46
 
41
- The script automatically takes over Ghost's native search and works with the `/#/search` URL trigger.
47
+ For either method, you can also self-host the `search.min.js` and add that URL instead of `https://unpkg.com/@magicpages/ghost-typesense-search-ui/dist/search.min.js`.
42
48
 
43
49
  ## Configuration
44
50
 
51
+ Configure the search by adding a global configuration object before loading the script. You can add this to your theme, or use Ghost's code injection to add it to your site's header.
52
+
53
+ ```html
54
+ <script>
55
+ window.__MP_SEARCH_CONFIG__ = {
56
+ typesenseNodes: [{
57
+ host: 'your-typesense-host',
58
+ port: '443',
59
+ protocol: 'https'
60
+ }], // also supports a Typesense cluster
61
+ typesenseApiKey: 'your-search-only-api-key', // Under no circumstances use an admin API key here. These values are stored client-side and are therefore accessible to the end user.
62
+ collectionName: 'your-collection-name',
63
+ theme: 'system', // 'light', 'dark', or 'system'
64
+ commonSearches: ['Getting Started', 'Tutorials', 'API'] // can also be empty
65
+ };
66
+ </script>
67
+ ```
68
+
69
+ ### Configuration Options
70
+
45
71
  | Option | Type | Required | Description |
46
72
  |--------|------|----------|-------------|
47
- | `typesenseNodes` | `Array` | Yes | Typesense node configurations |
48
- | `typesenseApiKey` | `String` | Yes | Search-only API key |
49
- | `collectionName` | `String` | Yes | Collection name |
50
- | `theme` | `String` | No | 'light', 'dark', or 'system' |
51
- | `commonSearches` | `Array` | No | Common search terms |
52
- | `searchFields` | `Object` | No | Field weights and highlighting |
73
+ | `typesenseNodes` | `Array` | Yes | Array of Typesense node configurations |
74
+ | `typesenseApiKey` | `String` | Yes | Search-only API key from Typesense |
75
+ | `collectionName` | `String` | Yes | Name of your Typesense collection |
76
+ | `theme` | `String` | No | UI theme: 'light', 'dark', or 'system' (default) |
77
+ | `commonSearches` | `Array` | No | Array of suggested search terms |
78
+ | `searchFields` | `Object` | No | Customize field weights and highlighting |
79
+
80
+ ### Search Fields Configuration
81
+
82
+ Customize search relevance with field weights and highlighting:
83
+
84
+ ```javascript
85
+ searchFields: {
86
+ title: { weight: 4, highlight: true },
87
+ excerpt: { weight: 2, highlight: true },
88
+ html: { weight: 1, highlight: true }
89
+ }
90
+ ```
91
+
92
+ ## Usage
53
93
 
54
- ## Keyboard Shortcuts
94
+ The search interface can be triggered in multiple ways:
95
+ - Click the search icon in your Ghost theme
96
+ - Press `/` on your keyboard
97
+ - Navigate to `/#/search` URL
98
+ - Programmatically via `window.magicPagesSearch.openModal()`
99
+
100
+ ### Keyboard Shortcuts
55
101
 
56
102
  - `/`: Open search
57
- - `↑/↓`: Navigate results
103
+ - `↑/↓`: Navigate through results
58
104
  - `Enter`: Select result
59
105
  - `Esc`: Close search
60
106
 
61
107
  ## Customization
62
108
 
63
- The UI uses CSS variables for styling:
64
-
65
- ```css
66
- #mp-search-wrapper {
67
- --modal-bg: #fff;
68
- --text-primary: #333;
69
- --text-secondary: #666;
70
- --border-color: rgba(0, 0, 0, 0.1);
71
- --hover-bg: rgba(0, 0, 0, 0.05);
72
- --backdrop-color: rgba(0, 0, 0, 0.5);
73
- --accent-color: var(--ghost-accent-color, #1c1c1c);
74
- }
109
+ The search UI automatically detects and uses your Ghost site's accent color by reading the `--ghost-accent-color` CSS variable. This ensures that the search interface matches your site's branding.
75
110
 
76
- /* Dark mode */
77
- #mp-search-wrapper.dark {
78
- --modal-bg: #1c1c1c;
79
- --text-primary: #fff;
80
- --text-secondary: #999;
81
- --border-color: rgba(255, 255, 255, 0.1);
82
- --hover-bg: rgba(255, 255, 255, 0.05);
83
- }
84
- ```
111
+ The UI also includes a built-in dark mode that automatically activates based on the user's system preferences. It can also be overwritten in the UI's configuration.
85
112
 
86
113
  ## License
87
114
 
88
- MIT © [MagicPages](https://www.magicpages.co)
115
+ MIT © [MagicPages](https://www.magicpages.co)
@@ -0,0 +1 @@
1
+ #mp-search-wrapper{--color-base-50:255 255 255;--color-base-100:250 250 250;--color-base-200:229 231 235;--color-base-300:209 213 219;--color-base-700:55 65 81;--color-base-800:31 41 55;--color-base-900:17 24 39;--color-base-950:3 7 18;--color-surface:rgb(var(--color-base-50));--color-surface-elevated:rgb(var(--color-base-100));--color-surface-hover:color-mix(in srgb,rgb(var(--color-base-950)) 4%,transparent);--color-accent-bg:color-mix(in srgb,var(--accent-color) 8%,transparent);--color-text:rgb(var(--color-base-900));--color-text-secondary:rgb(var(--color-base-700));--color-border:color-mix(in srgb,rgb(var(--color-base-950)) 8%,transparent);--color-backdrop:color-mix(in srgb,rgb(var(--color-base-950)) 60%,transparent);--accent-color:var(--ghost-accent-color,#4338ca);--accent-color-hover:color-mix(in srgb,var(--accent-color) 92%,#000);--transition-base:100ms ease;--transition-smooth:150ms ease;--shadow-sm:0 1px 2px 0 rgba(0,0,0,.05);--shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--shadow-lg:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);all:initial;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;inset:0;pointer-events:none;position:fixed;z-index:9999}.ais-SearchBox-input[type=search]::-webkit-search-cancel-button{display:none}#mp-search-wrapper.dark{--color-surface:rgb(var(--color-base-900));--color-surface-elevated:rgb(var(--color-base-800));--color-surface-hover:color-mix(in srgb,rgb(var(--color-base-50)) 4%,transparent);--color-text:rgb(var(--color-base-50));--color-text-secondary:rgb(var(--color-base-300));--color-border:color-mix(in srgb,rgb(var(--color-base-50)) 12%,transparent);--color-backdrop:color-mix(in srgb,rgb(var(--color-base-950)) 80%,transparent)}#mp-search-modal{height:100%;pointer-events:auto;position:relative;width:100%}#mp-search-modal.hidden{display:none}.mp-backdrop{backdrop-filter:blur(8px);background:var(--color-backdrop);inset:0;position:fixed;transition:opacity var(--transition-base)}.mp-modal-container{align-items:flex-start;display:flex;justify-content:center;min-height:100%;padding:1rem;position:relative;width:100%}.mp-modal-content{background:var(--color-surface);border:1px solid var(--color-border);border-radius:.75rem;box-shadow:var(--shadow-lg);margin-top:10vh;max-width:42rem;overflow:hidden;position:relative;width:100%}.mp-search-header{align-items:center;background:var(--color-surface-elevated);border-bottom:1px solid var(--color-border);display:grid;gap:1rem;grid-template-columns:1fr auto;padding:.875rem 1.5rem;position:relative}.mp-search-input-wrapper{align-items:center;position:relative;width:100%}.mp-search-input{-webkit-appearance:none;background:transparent;border:none;border-radius:.75rem;box-shadow:none;color:var(--color-text);font-size:1.125rem;height:2.5rem;outline:none;padding:.5rem .5rem .5rem 2.5rem;transition:all var(--transition-base);width:100%}.mp-search-input:focus,.mp-search-input:focus-visible{box-shadow:none;outline:none}.mp-search-input::placeholder{color:var(--color-text-secondary);opacity:.7}.mp-search-header:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='%23374151' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607'/%3E%3C/svg%3E");color:var(--color-text);content:"";height:1.25rem;left:1.5rem;opacity:.7;position:absolute;top:50%;transform:translateY(-50%);transition:opacity var(--transition-base);width:1.25rem}.dark .mp-search-header:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='%23D1D5DB' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607'/%3E%3C/svg%3E")}.mp-results-container{max-height:70vh;overflow-y:scroll;padding:.75rem calc(1.5rem - 6px) .75rem 1.5rem;scroll-behavior:smooth}.mp-modal-content:not(:has(.mp-common-searches)) .mp-results-container{padding-bottom:0}.mp-results-container::-webkit-scrollbar{width:6px}.mp-results-container::-webkit-scrollbar-track{background:transparent}.mp-results-container::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:3px}.mp-results-container>:last-child{margin-bottom:.75rem}.mp-common-searches{margin:0;padding:.75rem 6px .75rem 0}.mp-common-searches.hidden{display:none}.mp-common-searches-title{color:var(--color-text-secondary);font-size:.75rem;font-weight:600;letter-spacing:.05em;margin:0 1.5rem .75rem;text-transform:uppercase}.mp-common-search-btn{align-items:center;background:transparent;border:1px solid transparent;border-radius:.5rem;color:var(--color-text);cursor:pointer;display:flex;font-size:.875rem;padding:1rem calc(1.5rem - 6px) 1rem 1.5rem;text-align:left;text-decoration:none;transition:all var(--transition-base);width:100%}.mp-common-search-btn.mp-selected,.mp-common-search-btn:hover{background:var(--color-accent-bg)}.mp-result-link{color:inherit}.mp-result-item,.mp-result-link{cursor:pointer;display:block;text-decoration:none}.mp-result-item{border:1px solid transparent;border-radius:.75rem;margin:.5rem 0;padding:1rem 1.25rem;transition:background-color var(--transition-base);word-break:break-word}.mp-result-link.mp-selected .mp-result-item,.mp-result-link:focus .mp-result-item,.mp-result-link:hover .mp-result-item{background:var(--color-accent-bg);border-color:var(--color-border)}.mp-result-title{color:var(--color-text);font-size:1.125rem;font-weight:600;line-height:1.4;margin-bottom:.25rem;transition:color var(--transition-base);word-break:break-word}.mp-result-link.mp-selected .mp-result-title,.mp-result-link:focus .mp-result-title,.mp-result-link:hover .mp-result-title{color:var(--accent-color)}.mp-result-excerpt{color:var(--color-text-secondary);font-size:.875rem;line-height:1.6;margin-top:.25rem;word-break:break-word}.mp-keyboard-hints{display:flex;gap:.75rem;padding-left:.5rem;user-select:none;white-space:nowrap}.mp-kbd,.mp-keyboard-hints{color:var(--color-text-secondary);font-size:.75rem}.mp-kbd{background:var(--color-surface);border:1px solid var(--color-border);border-radius:.375rem;box-shadow:var(--shadow-sm);font-weight:500;padding:.125rem .375rem}.dark .mp-kbd,.dark .mp-keyboard-hints{color:rgb(var(--color-base-200))}.mp-empty-message{color:var(--color-text-secondary);font-size:1rem;padding:3rem 1.5rem;text-align:center}.mp-close-button{align-items:center;background:var(--color-surface-elevated);border:1px solid var(--color-border);border-radius:9999px;color:var(--color-text-secondary);cursor:pointer;display:flex;font-size:1.25rem;height:2rem;justify-content:center;line-height:1;padding:0;position:absolute;right:1.25rem;top:.875rem;transition:all var(--transition-base);width:2rem;z-index:50}.mp-close-button:hover{background:var(--color-surface-hover);border-color:var(--accent-color);color:var(--color-text)}.mp-close-button:focus-visible{border-color:var(--accent-color);box-shadow:0 0 0 2px var(--color-surface),0 0 0 4px var(--accent-color);outline:none}.ais-Highlight-highlighted,.ais-Snippet-highlighted{background:none;color:var(--accent-color);font-weight:600}@media (max-width:640px){.mp-keyboard-hints{display:none}.mp-search-input-wrapper{margin-right:0}.mp-modal-content{border-radius:0;height:100vh;margin-top:0}.mp-modal-container{padding:0}.mp-close-button{right:1rem;top:1rem}.mp-results-container{max-height:calc(100vh - 4.5rem);padding-bottom:1.5rem}}