diff --git a/v8/flexsearch_plugin/CHANGELOG.md b/v8/flexsearch_plugin/CHANGELOG.md
new file mode 100644
index 00000000..b252b097
--- /dev/null
+++ b/v8/flexsearch_plugin/CHANGELOG.md
@@ -0,0 +1,67 @@
+# CHANGELOG - FlexSearch Plugin
+
+## v0.2 (March 21, 2025)
+
+### New Features
+- **Page Indexing**: Added support for indexing pages in addition to posts by removing the post-only restriction in build_index()
+- **Configuration Options**:
+ - `FLEXSEARCH_INDEX_POSTS` (default: `True`) - Control whether posts are indexed
+ - `FLEXSEARCH_INDEX_PAGES` (default: `False`) - Control whether pages are indexed
+ - `FLEXSEARCH_INDEX_DRAFTS` (default: `False`) - Control whether draft content is indexed
+- **Content Type Display**: Search results now display content type (post/page) with badges using CSS-styled spans
+- **Enhanced Search Index**:
+ - Improved indexing to include title, content, and tags in search for better relevance
+ - Created a multi-field search approach: `searchIndex.add(key, data[key].title + " " + data[key].content + data[key].tags + " " + data[key].content)`
+ - Optional `FLEXSEARCH_OVERLAY` version available with advanced features (not enabled by default)
+- **Basic UI Improvements**:
+ - Better handling of search results with clear formatting and type badges
+ - Support for both overlay and inline result display options
+- **Keyboard Shortcuts**:
+ - ESC key to close search overlay via global event listener
+ - Enter key support for search submission with preventDefault() to avoid form submission
+- **Tracking**: Added `?utm_source=internal_search` to result URLs for analytics tracking
+- **Updated Libraries**: Using the latest FlexSearch v0.8.0 from CDN with minified bundle
+
+### Improvements
+- Added console logging for easier debugging
+- Upgraded search index structure to include tags
+- Better structured code with improved organization
+- Two available search implementations:
+ - Simple implementation (FLEXSEARCH_EXTEND) for reliable basic functionality
+ - Advanced implementation (FLEXSEARCH_OVERLAY) available for future enhancements
+
+### Bug Fixes
+- Fixed issue where search only worked on post content, not pages by modifying the `if post.is_post` condition in build_index()
+- Added specific null checks in JavaScript (`if (searchButton)`, `if (searchInput)`, etc.) to prevent errors when elements aren't found
+- Resolved event handler issues by moving key functions outside the DOMContentLoaded scope
+- Fixed closeSearch function accessibility by defining it globally in the window scope
+- Improved error handling with console.log statements for debugging common issues
+- Changed URL path in fetch() to `/search_index.json` (with leading slash) to ensure correct file loading regardless of current page URL
+
+### Technical Changes
+- Modified search index generation to remove the `post.is_post` filter that previously excluded pages from indexing
+- Added support for tags in the search index: `item.meta('tags')` is now included in each indexed item
+- Changed the search approach to concatenate multiple fields (`title + content + tags`) for more comprehensive search. This can be changed in `build_index` and in
+ ```javascript
+ // Change here which keys should be used for the search index.
+ searchIndex.add(key, data[key].title + " " + data[key].content + data[key].tags + " " + data[key].content);
+ ```
+- Implemented two distinct JavaScript implementations:
+ - The default simple version (FLEXSEARCH_EXTEND) prioritizes reliability
+ - An optional advanced version (FLEXSEARCH_OVERLAY) with separate title/content indices
+- Changed index URL from relative to absolute path (`/search_index.json`) to avoid path resolution issues
+- Added content type field to search index to distinguish between posts and pages
+- Commented out content indexing in plugin code but added comprehensive content indexing in JavaScript
+
+### Documentation
+- Added comprehensive documentation for new features
+- Updated installation and configuration instructions
+- Added code comments for better maintainability
+
+---
+
+## v0.1 (Initial Release)
+- Basic search functionality for posts
+- Support for triggering search via button click or Enter key
+- Simple search index generation
+- Basic overlay and inline result display options
\ No newline at end of file
diff --git a/v8/flexsearch_plugin/README.md b/v8/flexsearch_plugin/README.md
index 362cb648..669b44e7 100644
--- a/v8/flexsearch_plugin/README.md
+++ b/v8/flexsearch_plugin/README.md
@@ -1,244 +1,232 @@
# flexsearch_plugin
-This plugin adds search functionality to a Nikola Site.
+This plugin adds search functionality to a Nikola static site. It generates a JSON file with posts, pages, and their metadata, then uses [flexsearch](https://github.com/nextapps-de/flexsearch) for fast client-side search.
-It generates a json with all the articles, slugs and titles and then uses [flexsearch](https://github.com/nextapps-de/flexsearch) to search.
+## Updates
-It supports searching clicking the search button or pressing enter.
+You can get the last updates in my [blog](https://diegocarrasco.com/project-nikola-flexsearch-plugin), or regularly checking here.
-The s`earch_index.json` is (re)generated on `nikola build`
+### v0.2
-# How to use
+- Check the CHANGELOG.md (new file in the repo) for a summary of the changes.
+- NOTE: I hope I didn't forget to document anything.
-There are 2 options:
+### v0.1
-- Use a normal div and add the search results there.
-- Use an overlay with the search results.
+- Initial version.
-Here is an example of the overlay:
+## Features
-
+- Fast client-side searching with FlexSearch
+- Configurable indexing of posts and/or pages
+- Support for both overlay and inline search results display
+- Displays content type (post/page) in search results
+- Search across titles, content, and tags
+- Keyboard navigation (ESC to close overlay, Enter to search)
+- Analytics tracking via UTM parameters (just a `?utm_source=internal_search` appended to the url in the results)
-For a live example check https://diegocarrasco.com
+## Installation
+To install the plugin, use the Nikola plugin installation command:
-## Use a normal div and extend and populate it withthe results
+```shell
+nikola plugin -i flexsearch_plugin
+```
-Append this to your `BODY_END` in `conf.py`
+**IMPORTANT**
+Version 0.2 has `FLEXSEARCH_INDEX_PAGES = False` by default to maintain the default search behaviour of the previous version.
-```html
-
-
+```python
+# Content indexing options
+FLEXSEARCH_INDEX_POSTS = True # Index posts (default: True)
+FLEXSEARCH_INDEX_PAGES = False # Index pages (default: False)
+FLEXSEARCH_INDEX_DRAFTS = False # Index draft content (default: False)
```
-then add this where you want the search to appear
+There are two provided search implementations that you can use:
-```html
-
-
-
+1. **FLEXSEARCH_EXTEND**: A simpler implementation that adds search results to a div (default)
+2. **FLEXSEARCH_OVERLAY**: A more advanced implementation with overlay display (the one I use in https://diegocarrasco.com)
+
+Add one of these to your `BODY_END` in `conf.py`:
+
+```python
+# Add the search script to your BODY_END
+BODY_END = BODY_END + FLEXSEARCH_EXTEND # Or use FLEXSEARCH_OVERLAY
```
+## How to Use
-## Use an overlay for the results
+There are 2 options for displaying search results:
-This js allows to click the button or press enter to search
+Here is an example of the overlay:
-```html
-
-
-
+### Option 1: Inline Search Results
+
+Add this HTML where you want the search box to appear:
+
+```html
+
+
+
```
-add this to your template where you want the search to appear
+This will display search results directly in the `search_results` div.
+
+### Option 2: Overlay Search Results
+
+Add this HTML where you want the search box to appear:
```html
-
+
+
+
+
-
+
+
Search Results
+
+
-
-
```
-add css for the overlay
+This will display search results in a modal overlay that appears when search is triggered.
-```html
-/* flexsearch_plugin*/
+## CSS Styling
+Add this CSS to your site's stylesheet for proper styling of the search overlay:
+
+```css
+/* Basic overlay structure */
#search_overlay {
- position: fixed; /* Fixed position to cover the whole screen */
+ position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
- background: rgba(0, 0, 0, 0.8); /* Semi-transparent background */
- z-index: 1000; /* Make sure it's on top of other content */
- display: flex;
+ background: rgba(0, 0, 0, 0.8);
+ z-index: 1000;
+ display: none;
justify-content: center;
- align-items: center;
+ align-items: flex-start;
+ overflow-y: auto;
+ padding: 20px;
+ box-sizing: border-box;
}
#search_content {
background: white;
- padding: 20px;
width: 90%;
- max-width: 600px; /* Limit the width on larger screens */
- border-radius: 5px;
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+ max-width: 800px;
+ border-radius: 8px;
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
+ max-height: 85vh;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ margin-top: 40px;
}
-#search_results {
- margin-top: 20px;
- width: 100%;
+/* Header */
+#search_header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px 20px;
+ border-bottom: 1px solid #eee;
+ position: sticky;
+ top: 0;
+ z-index: 5;
}
-#search_results ul {
- list-style-type: circle; /* Ensuring list style is visible */
- padding-left: 20px; /* Adequate space for bullets */
+.search-title {
+ font-size: 1.3rem;
+ font-weight: bold;
}
-#search_results li {
- padding: 10px; /* Padding for each list item */
- border-bottom: 1px solid #ccc; /* Optional: add a separator between items */
+.close-button {
+ background: none;
+ border: none;
+ font-size: 1.5rem;
+ cursor: pointer;
+ padding: 0;
+ width: 30px;
+ height: 30px;
display: flex;
- align-items: left; /* Center items vertically */
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ background: lightgray;
}
-#search_results li a {
- text-decoration: none; /* Optional: remove underline from links */
- color: #333; /* Dark grey color for text */
- flex-grow: 1; /* Make link fill the li element */
- margin-left: 10px; /* Space between icon and text */
- text-align: left;
+.close-button:hover {
+ background-color: #f0f0f0;
}
-/* Use this if you want to use a font-awesome icon. This is just an example.
-
-#search_results li::before {
- content: '\f007'; *//* FontAwesome user icon *//*
- font-family: 'FontAwesome';
- color: #5A5A5A; *//* Icon color *//*
- font-size: 1.2em; *//* Larger icon size *//*
+/* Results area */
+#search_results {
+ padding: 10px 20px;
+ flex: 1;
+ overflow-y: auto;
}
-*/
-#search_results li:hover {
- background-color: lightgray;
+/* Type badge styling */
+.badge {
+ display: inline-block;
+ padding: 2px 6px;
+ border-radius: 4px;
+ font-size: 0.7rem;
+ background-color: #e9ecef;
+ color: #495057;
+ margin-right: 8px;
+ vertical-align: middle;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
}
-button {
- cursor: pointer;
+/* Mobile responsiveness */
+@media (max-width: 576px) {
+ #search_content {
+ width: 95%;
+ margin-top: 20px;
+ max-height: 90vh;
+ }
+
+ a {
+ text-wrap: auto !important;
+ }
}
```
-## License
+For the complete CSS with additional styling options, see the included file `flexsearch.css`.
-this plugin is under the MIT License
+## How It Works
+
+1. The plugin generates a `search_index.json` file in your site's output directory
+2. The JSON file contains all your posts and/or pages with their metadata
+3. When a user searches, the FlexSearch library searches through this JSON
+4. Results are displayed either inline or in an overlay
+
+## Troubleshooting
+
+If search isn't working:
+
+1. Check your browser console for JavaScript errors
+2. Verify that `search_index.json` exists in your site's output directory
+3. Make sure HTML elements have the correct IDs (`search_input`, `search_button`, `search_results`)
+4. Check the network tab in developer tools to ensure the JSON file loads correctly
+
+## License
-MIT License
+This plugin is under the MIT License.
Copyright (c) [2024] [Diego Carrasco G.]
@@ -258,4 +246,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+SOFTWARE.
\ No newline at end of file
diff --git a/v8/flexsearch_plugin/conf.py.sample b/v8/flexsearch_plugin/conf.py.sample
index fc29aee7..cb5bd727 100644
--- a/v8/flexsearch_plugin/conf.py.sample
+++ b/v8/flexsearch_plugin/conf.py.sample
@@ -1,141 +1,499 @@
-# Use this to add the search results to an overlay
-# In this case you need the following in your template. Check the readme for a base css.:
-#
-#
-#
-#
-#
-#
-#
-#
+# Set what content types you want to index
+FLEXSEARCH_INDEX_POSTS = True # Index posts (default: True)
+FLEXSEARCH_INDEX_PAGES = True # Index pages (default: False)
+FLEXSEARCH_INDEX_DRAFTS = False # Index draft content (default: False)
FLEXSEARCH_OVERLAY = """
-
+
"""
-# use this to add the results to a dive, effectively expanding that div.
+# Use this to add the results to a div, effectively expanding that div.
# In this case you need to add the following to your template. The search results will be added to #search_results:
#
#
#
FLEXSEARCH_EXTEND = """
-
+
"""
-
+# Add the chosen script to your BODY_END
BODY_END = BODY_END + FLEXSEARCH_EXTEND
\ No newline at end of file
diff --git a/v8/flexsearch_plugin/flexsearch.css b/v8/flexsearch_plugin/flexsearch.css
new file mode 100644
index 00000000..0e43640b
--- /dev/null
+++ b/v8/flexsearch_plugin/flexsearch.css
@@ -0,0 +1,235 @@
+/* flexsearch_plugin styles */
+
+/* Basic overlay structure */
+#search_overlay {
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ background: rgba(0, 0, 0, 0.8);
+ z-index: 1000;
+ display: none;
+ justify-content: center;
+ align-items: flex-start; /* Align to top instead of center */
+ overflow-y: auto; /* Allow scrolling */
+ padding: 20px;
+ box-sizing: border-box;
+}
+
+#search_content {
+ background: white;
+ width: 90%;
+ max-width: 800px; /* Wider content area */
+ border-radius: 8px;
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
+ max-height: 85vh; /* Limit height to enable scrolling */
+ overflow-y: auto; /* Enable scrolling within the content */
+ display: flex;
+ flex-direction: column;
+ margin-top: 40px; /* Space from top of screen */
+}
+
+/* Header */
+#search_header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px 20px;
+ border-bottom: 1px solid #eee;
+ position: sticky;
+ top: 0;
+ z-index: 5;
+}
+
+.search-title {
+ font-size: 1.3rem;
+ font-weight: bold;
+}
+
+.close-button {
+ background: none;
+ border: none;
+ font-size: 1.5rem;
+ cursor: pointer;
+ padding: 0;
+ width: 30px;
+ height: 30px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ background: lightgray;
+}
+
+.close-button:hover {
+ background-color: #f0f0f0;
+}
+
+/* Filters */
+.search-filters {
+ padding: 12px 20px;
+ border-bottom: 1px solid #eee;
+ background-color: #f9f9f9;
+ position: sticky;
+ top: 54px; /* Position right below the header */
+ z-index: 4;
+}
+
+.search-filter-group {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.filter-label {
+ font-size: 0.9rem;
+ color: #666;
+ margin-right: 4px;
+}
+
+.filter-button {
+ background-color: #f0f0f0;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ padding: 4px 10px;
+ font-size: 0.85rem;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.filter-button:hover {
+ background-color: #e0e0e0;
+}
+
+.filter-button.active {
+ background-color: #007bff;
+ color: white;
+ border-color: #0069d9;
+}
+
+/* Results header */
+.search-results-header {
+ padding: 10px 20px;
+ font-size: 0.9rem;
+ color: #666;
+ border-bottom: 1px solid #eee;
+}
+
+.search-stats {
+ display: flex;
+ justify-content: space-between;
+}
+
+/* Results area */
+#search_results {
+ padding: 10px 20px;
+ flex: 1;
+ overflow-y: auto;
+}
+
+/* Individual result items */
+.search-result-item {
+ padding: 12px 0;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.search-result-item:last-child {
+ border-bottom: none;
+}
+
+.search-result-item a {
+ display: block;
+ color: inherit;
+ text-decoration: none;
+}
+
+.search-result-item a:hover .result-title {
+ color: #0056b3;
+ text-decoration: underline;
+}
+
+.badge {
+ display: inline-block;
+ padding: 2px 6px;
+ border-radius: 4px;
+ font-size: 0.7rem;
+ background-color: #e9ecef;
+ color: #495057;
+ margin-right: 8px;
+ vertical-align: middle;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.badge + .result-title {
+ vertical-align: middle;
+}
+
+.result-title {
+ font-weight: 500;
+ color: #212529;
+ font-size: 1rem;
+}
+
+.result-snippet {
+ margin-top: 6px;
+ font-size: 0.9rem;
+ color: #666;
+ padding-left: 8px;
+ border-left: 2px solid #eee;
+ margin-left: 22px; /* Align with the title after the badge */
+}
+
+.search-highlight {
+ background-color: #fff0b3;
+ font-weight: bold;
+ padding: 1px 2px;
+ border-radius: 2px;
+}
+
+/* Info boxes */
+.results-info, .results-limit-message {
+ padding: 10px 15px;
+ background-color: #f8f9fa;
+ border-left: 3px solid #6c757d;
+ margin: 10px 0;
+ font-size: 0.9rem;
+ color: #495057;
+}
+
+.no-results {
+ padding: 20px 0;
+ text-align: center;
+ color: #666;
+ font-style: italic;
+}
+
+/* Responsive adjustments */
+@media (max-width: 576px) {
+ #search_content {
+ width: 95%;
+ margin-top: 20px;
+ max-height: 90vh;
+ }
+
+ .search-filter-group {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .filter-label {
+ margin-bottom: 5px;
+ }
+
+ .filter-button {
+ width: 100%;
+ text-align: center;
+ margin-bottom: 5px;
+ }
+
+ a {
+ text-wrap: auto !important;
+ }
+}
\ No newline at end of file
diff --git a/v8/flexsearch_plugin/flexsearch_plugin.plugin b/v8/flexsearch_plugin/flexsearch_plugin.plugin
index 4474cbdc..f1fe5537 100644
--- a/v8/flexsearch_plugin/flexsearch_plugin.plugin
+++ b/v8/flexsearch_plugin/flexsearch_plugin.plugin
@@ -4,11 +4,11 @@ Module = flexsearch_plugin
[Documentation]
Author = Diego Carrasco G.
-Version = 0.1
+Version = 0.2
Website = https://plugins.getnikola.com/#flexsearch_plugin
-Description = Adds FlexSearch full-text search capabilities to Nikola static sites.
+Description = Adds FlexSearch full-text search capabilities to Nikola static sites, indexing both posts and pages.
[Nikola]
-MinVersion = 8.0.0 # I haven't tested it with older versions
+MinVersion = 8.0.0
MaxVersion = 8.3.1
PluginCategory = LateTask
\ No newline at end of file
diff --git a/v8/flexsearch_plugin/flexsearch_plugin.py b/v8/flexsearch_plugin/flexsearch_plugin.py
index e030da77..6ccd6a9b 100644
--- a/v8/flexsearch_plugin/flexsearch_plugin.py
+++ b/v8/flexsearch_plugin/flexsearch_plugin.py
@@ -43,15 +43,39 @@ def gen_tasks(self):
index_file_path = os.path.join(output_path, 'search_index.json')
def build_index():
- """Build the entire search index from scratch."""
+ """Build the entire search index from scratch, including both posts and pages."""
index = {}
- for post in self.site.timeline:
- if post.is_post and not post.is_draft:
- index[post.meta('slug')] = {
- 'title': post.title(),
- 'content': post.text(strip_html=True),
- 'url': post.permalink()
+
+ # Get configuration for what content to include
+ # Default to True for backward compatibility
+ index_posts = self.site.config.get('FLEXSEARCH_INDEX_POSTS', True)
+ index_pages = self.site.config.get('FLEXSEARCH_INDEX_PAGES', False)
+ index_drafts = self.site.config.get('FLEXSEARCH_INDEX_DRAFTS', False)
+
+ for item in self.site.timeline:
+ # Skip draft items unless configured to include them
+ if item.is_draft and not index_drafts:
+ continue
+
+ # Include posts if configured
+ if item.is_post and index_posts:
+ index[item.meta('slug')] = {
+ 'title': item.title(),
+ # 'content': item.text(strip_html=True),
+ 'tags': item.meta('tags'),
+ 'url': item.permalink(),
+ 'type': 'post'
}
+ # Include pages if configured
+ elif not item.is_post and index_pages:
+ index[item.meta('slug')] = {
+ 'title': item.title(),
+ # 'content': item.text(strip_html=True),
+ 'tags': item.meta('tags'),
+ 'url': item.permalink(),
+ 'type': 'page'
+ }
+
with open(index_file_path, 'w', encoding='utf-8') as f:
json.dump(index, f, ensure_ascii=False)
diff --git a/v8/flexsearch_plugin/imgs/example_overlay.png b/v8/flexsearch_plugin/imgs/example_overlay.png
index 7c2e5ddc..3b767098 100644
Binary files a/v8/flexsearch_plugin/imgs/example_overlay.png and b/v8/flexsearch_plugin/imgs/example_overlay.png differ