Skip to content

Commit

Permalink
feat(web): improve search result styles
Browse files Browse the repository at this point in the history
  • Loading branch information
dnlzrgz committed Nov 29, 2024
1 parent c6cc793 commit baf5837
Show file tree
Hide file tree
Showing 12 changed files with 276 additions and 516 deletions.
Binary file modified .github/screenshot_web.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Release

on:
release:
types: ["published"]

jobs:
run:
name: Release
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: uv.lock

- name: Set up Python
run: uv python install 3.13

- name: Build
run: uv build

- name: Publish
run: uv publish -t ${{ secrets.PYPI_TOKEN }}
18 changes: 0 additions & 18 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,5 @@ update:
uv lock --upgrade
uv sync

index:
uv run housaku index

search:
uv run housaku search

web:
uv run housaku web

web-dev:
uv run fastapi dev src/housaku/web/app.py

tui:
uv run housaku tui

tui-dev:
uv run textual run --dev src/housaku/tui/app.py

test:
pytest -v
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ After you have configured the list of directories containing the documents you w
housaku index
```

### Filtering content
#### Filtering content

To index only your files, use the following command:

Expand All @@ -185,7 +185,7 @@ housaku index --include feeds

> You can specify both options to index files and feeds together, but this is equivalent to simply running the `index` command without any options.
### Parallelism
#### Parallelism

You can also change the number of threads being used when indexing your files and documents:

Expand Down Expand Up @@ -235,6 +235,8 @@ Housaku also has a very simple Web UI that you can access by running:
housaku web
```

![Screenshot of the Web](./.github/screenshot_web.png)

> The default port is `4242`.
This searching method have some limitations. For example, you can't open results that link to your personal files. In the future, I will try to solve this limitations, but for now please keep this in mind.
Expand Down
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "housaku"
version = "v0.7.7"
version = "v0.7.9"
description = "A personal search engine built on top of SQLite's FTS5."
readme = "README.md"
license = "MIT"
Expand All @@ -13,10 +13,12 @@ dependencies = [
"selectolax>=0.3.21",
"rich-click>=1.8.3",
"pymupdf>=1.24.10",
"fastapi[standard]>=0.115.0",
"python-multipart>=0.0.12",
"textual>=0.82.0",
"aiohttp[speedups]>=3.11.7",
"uvicorn>=0.32.1",
"starlette>=0.41.3",
"aiofiles>=24.1.0",
]

[project.urls]
Expand Down
44 changes: 26 additions & 18 deletions src/housaku/web/app.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,43 @@
from pathlib import Path
from typing import Annotated
from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from starlette.applications import Starlette
from starlette.routing import Mount, Route
from starlette.staticfiles import StaticFiles
from starlette.responses import HTMLResponse, JSONResponse
from housaku.db import init_db
from housaku.settings import Settings
from housaku.search import search

settings = Settings()
init_db(settings.sqlite_url)

app = FastAPI()

base_dir = Path(__file__).resolve().parent
templates = Jinja2Templates(directory=base_dir / "templates")


@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
return templates.TemplateResponse(request=request, name="index.html", context={})
async def homepage(_):
index_page = base_dir / "index.html"
return HTMLResponse(index_page.open().read())


@app.post("/search/", response_class=HTMLResponse)
async def search_results(request: Request, query: Annotated[str, Form()]):
async def search_results(request):
data = await request.json()
query = data["query"]
try:
results = search(settings.sqlite_url, query)
results = search(settings.sqlite_url, query, 100)
return JSONResponse(
{
"query": query,
"results": results,
}
)
except Exception as e:
return 404, {"detail": f"{e}"}

return templates.TemplateResponse(
request=request,
name="results.html",
context={"query": query, "results": results},
)

routes = [
Route("/", homepage, methods=["GET"]),
Route("/search", search_results, methods=["POST"]),
Mount("/static", app=StaticFiles(directory=base_dir / "static"), name="static"),
]


app = Starlette(routes=routes)
169 changes: 169 additions & 0 deletions src/housaku/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>housaku</title>
<meta name="description" content="A personal search engine." />
<meta name="color-scheme" content="light dark" />

<style>
:root {
--accent: #3ea1cc;
--black: #0a0a0a;
--white: #f5f5f5;
--background: var(--white);
--foreground: var(--black);
--font-weight-medium: 500;
--spacing-0-5: 0.125rem;
--spacing-1-5: 0.375rem;
--spacing-3: 0.75rem;
--spacing-5: 1.25rem;
}

#root,
#__next {
isolation: isolate;
}

* {
font-size: 100%;
margin: 0;
min-width: 0;
}

*,
*::before,
*::after {
box-sizing: border-box;
}

html {
-moz-text-size-adjust: none;
-webkit-text-size-adjust: none;
color-scheme: light dark;
text-size-adjust: none;
}

body {
background: light-dark(var(--background), var(--foreground));
color: light-dark(var(--foreground), var(--background));
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1.125rem;
line-height: 1.6;
min-height: 100svh;
min-height: 100vh; /* Fallback */
text-rendering: optimizeSpeed;
}

input {
font-family: inherit;
font-size: inherit;
line-height: 1.1;
}

body {
margin-block-end: 0;
}

.header {
padding: var(--spacing-3);
}

.input {
background: light-dark(var(--background), var(--foreground));
border: none;
border-bottom: 1px solid
light-dark(var(--foreground), var(--background));
max-width: 65ch;
padding: var(--spacing-1-5);
width: 100%;
}

.results__result {
display: grid;
grid-template-columns: 5ch 1fr;
gap: var(--spacing-5);
padding: var(--spacing-0-5) var(--spacing-3);
}

.result__type {
font-weight: var(--font-weight-medium);
}

.result__title {
overflow: hidden;
text-overflow: ellipsis;
user-select: none;
white-space: nowrap;

a {
color: var(--accent);
font-weight: var(--font-weight-medium);
margin-right: var(--spacing-1-5);

&:visited {
color: light-dark(var(--foreground), var(--background));
}
}

span {
opacity: 80%;
}
}
</style>
<script src="/static/alpine.min.js" defer></script>
</head>
<body
x-data="{
query: '',
results: [],
search() {
fetch('/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: this.query }),
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
this.results = data.results;
})
.catch(error => {
console.log(error);
});
},
}"
x-init="$watch('query', () => search())"
>
<header class="header">
<input
type="search"
placeholder="Search..."
value=""
x-model.debounce.500ms="query"
class="input"
/>
</header>
<main class="main" class="results" role="list">
<template x-for="result in results" :key="result[0]">
<li class="results__result" role="listitem">
<p x-text="result[2]" class="result__type"></p>
<div headers="document" class="result__title">
<a
:href="result[2] === 'http' || result[2] == 'https' ? result[0] : 'file://' + result[0]"
x-text="result[1]"
></a>
<span x-text="result[3]"></span>
</div>
</li>
</template>
</main>
</body>
</html>
5 changes: 5 additions & 0 deletions src/housaku/web/static/alpine.min.js

Large diffs are not rendered by default.

Loading

0 comments on commit baf5837

Please sign in to comment.