mirror of
https://github.com/tauri-apps/tauri-search.git
synced 2026-02-04 02:41:20 +01:00
docs: cleaned up playground and mildly improved ux
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
"@vueuse/head": "^0.7.5",
|
||||
"date-fns": "^2.28.0",
|
||||
"floating-vue": "^2.0.0-beta.3",
|
||||
"inferred-types": "^0.18.4",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.11",
|
||||
"prism-theme-vars": "^0.2.2",
|
||||
|
||||
@@ -1,50 +1,105 @@
|
||||
<script setup lang="ts">
|
||||
import { Keys } from "inferred-types";
|
||||
import { useSearch } from "~/modules/search";
|
||||
const search = useSearch();
|
||||
const s = useSearch();
|
||||
|
||||
/**
|
||||
* this allows all expected indexes to be shown and in a sensible order
|
||||
*/
|
||||
const knownIndexes = ["api", "prose", "repo", "consolidated"];
|
||||
/** the name of the indexes current known by MeiliSearch */
|
||||
const currentIndexes = computed(() => search.$state.indexes.map(i => i.name));
|
||||
const currentIndexes = computed(() => s.$state.indexes.map(i => i.name));
|
||||
|
||||
/**
|
||||
* we do want to see ANY index that exists though too
|
||||
*/
|
||||
const unknownIndexes = computed(() => {
|
||||
return search.$state.indexes.filter(i => !knownIndexes.includes(i.name));
|
||||
return s.$state.indexes.filter(i => !knownIndexes.includes(i.name));
|
||||
});
|
||||
|
||||
const indexStateOptions = ["consolidated", "individual", "bespoke"] as const;
|
||||
type IndexState = Keys<typeof indexStateOptions>;
|
||||
|
||||
const indexState = computed(() => {
|
||||
if(s.$state.searchUsing.length === 1 && s.$state.searchUsing[0] === "consolidated") {
|
||||
return "consolidated";
|
||||
} else if(s.$state.searchUsing.length === 3 && ["api", "prose", "repo"].every(i => s.$state.searchUsing.includes(i))) {
|
||||
return "individual";
|
||||
} else {
|
||||
return "bespoke";
|
||||
}
|
||||
});
|
||||
|
||||
const changeIndexes = (opt: IndexState) => {
|
||||
switch(opt) {
|
||||
case "consolidated":
|
||||
s.setUseOfIndexes(["consolidated"]);
|
||||
break;
|
||||
case "individual":
|
||||
s.setUseOfIndexes(["api", "prose", "repo"]);
|
||||
}
|
||||
};
|
||||
|
||||
const optionStyle = (opt: IndexState) => computed(() => {
|
||||
const isSelected = indexState.value === opt;
|
||||
const selected = isSelected ? "opacity-100 border-b-1" : "opacity-50";
|
||||
switch(opt) {
|
||||
case "bespoke":
|
||||
return `${selected} cursor-default`;
|
||||
default:
|
||||
return isSelected ? `${selected} cursor-default` : `${selected} cursor-pointer`;
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="search-indexes flex flex-col flex-grow rounded-md bg-gray-100/25 dark:bg-gray-800/25">
|
||||
<div class="search-indexes flex flex-col flex-grow rounded-md bg-gray-100/25 dark:bg-gray-800/25 ">
|
||||
|
||||
<div class="rounded-t-md overflow-hidden">
|
||||
<div class="rounded-t-md overflow-hidden ">
|
||||
<div class="font-bold text-lg rounded-t-md bg-gray-900/10 dark:bg-gray-100/10 py-3 px-4">
|
||||
INDEXES
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="search.$state.health" class="text-xs mt-2" >
|
||||
<div class="text-green-500">MeiliSearch Service Healthy!</div>
|
||||
<div class="db-size">db size: {{search.dbSize}}</div>
|
||||
</div>
|
||||
<span v-else class="text-red-500 ">
|
||||
Meilisearch is not available locally!
|
||||
</span>
|
||||
<div class="index-list p-4 w-full flex flex-col flex-grow ">
|
||||
<div class="index-usage flex flex-col items-center p-1 bg-gray-500/10 rounded mb-4">
|
||||
<div>Index Usage</div>
|
||||
|
||||
<div class="flex mt-2 flex-row space-x-3 text-gray-600 dark:text-gray-400 text-sm ">
|
||||
<div
|
||||
v-for="opt in indexStateOptions"
|
||||
:key="opt"
|
||||
class="option flex"
|
||||
:class="optionStyle(opt).value"
|
||||
@click="() => changeIndexes(opt)"
|
||||
>
|
||||
{{opt}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="index-list p-4 w-full flex flex-col ">
|
||||
<template v-for="idx in knownIndexes" :key="idx">
|
||||
<current-index v-if="currentIndexes.includes(idx)" :idx="search.$state.indexes.find(i => i.name === idx)"/>
|
||||
<missing-index v-else :idx="idx" />
|
||||
<current-index v-if="currentIndexes.includes(idx)" :idx="s.$state.indexes.find(i => i.name === idx)"/>
|
||||
<missing-index v-else :idx="idx" />
|
||||
</template>
|
||||
<template v-for="idx in unknownIndexes" :key="idx">
|
||||
<current-index :idx="idx" :unknown="true" />
|
||||
<current-index :idx="idx" :unknown="true" />
|
||||
</template>
|
||||
|
||||
<div class="mt-8">
|
||||
<div v-if="s.$state.health" class="text-xs mt-2" >
|
||||
<div class="text-green-500">MeiliSearch Service Healthy!</div>
|
||||
<div class="db-size">db size: {{s.dbSize}}</div>
|
||||
</div>
|
||||
<span v-else class="text-red-500 ">
|
||||
Meilisearch is not available locally!
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-grow"></div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -4,20 +4,22 @@ const props = defineProps({
|
||||
query: {type: String, default: ""}
|
||||
});
|
||||
const s = useSearch();
|
||||
|
||||
const notRealQuery = computed(() => props.query.trim().length === 0);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="search-stats flex flex-col rounded-md h-full min-h-128">
|
||||
<div class="search-stats flex flex-col rounded-md h-full min-h-128 bg-gray-100/25 dark:bg-gray-800/25">
|
||||
|
||||
<div class="rounded-t-md overflow-hidden">
|
||||
<div class="grid grid-cols-3 rounded-t-md bg-gray-900/10 dark:bg-gray-100/10 py-3 px-4 ">
|
||||
<div class="block text-sm font-light place-self-start self-center">
|
||||
<div class="rounded-t-md overflow-hidden bg-gray-900/10 dark:bg-gray-100/10 ">
|
||||
<div class="grid grid-cols-3 rounded-t-md py-3 px-4 ">
|
||||
<div class="block text-sm font-light place-self-start self-center" :class="notRealQuery ? 'opacity-50' : ''">
|
||||
{{s.$state.searchUsing.length}} <span class="italic">index(s) searched</span>
|
||||
</div>
|
||||
<div class="block font-bold text-lg place-self-center self-center">
|
||||
Results
|
||||
RESULTS
|
||||
</div>
|
||||
<div class="block text-sm font-light place-self-end self-center">
|
||||
<div class="block text-sm font-light place-self-end self-center" :class="notRealQuery ? 'opacity-50' : ''">
|
||||
{{s.$state.searchResults.length}} <span class="italic">docs found</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -27,7 +29,7 @@ const s = useSearch();
|
||||
<search-hit v-for="hit in s.searchResults" :key="hit.id" :document="hit" />
|
||||
</div>
|
||||
<div v-else class="flex flex-col flex-grow justify-center italic">
|
||||
<div v-if="query.length > 0" >
|
||||
<div v-if="props.query.length > 0" >
|
||||
no results
|
||||
</div>
|
||||
<div v-else>
|
||||
|
||||
@@ -25,6 +25,8 @@ export interface SearchState {
|
||||
|
||||
searchStatus: "ready" | "searching" | "error" | "not-ready";
|
||||
|
||||
searchQuery: string;
|
||||
|
||||
searchResults: {id: string; _idx: string; [key: string]: unknown}[];
|
||||
}
|
||||
|
||||
@@ -35,15 +37,23 @@ export const useSearch = defineStore("search", ({
|
||||
indexSettings: {},
|
||||
searchUsing: ["consolidated"],
|
||||
stats: undefined,
|
||||
searchQuery: "",
|
||||
searchStatus: "not-ready",
|
||||
searchResults: [],
|
||||
}) as SearchState,
|
||||
actions: {
|
||||
async search(text: string) {
|
||||
async search(text: string, force: boolean = false) {
|
||||
if(text.trim()==="") {
|
||||
this.$state.searchResults = [];
|
||||
this.$state.searchQuery = text.trim();
|
||||
return;
|
||||
}
|
||||
if(text.trim() === this.searchQuery.trim() && !force) {
|
||||
// no change
|
||||
return;
|
||||
} else {
|
||||
this.searchQuery = text.trim();
|
||||
}
|
||||
const indexes = this.$state.searchUsing;
|
||||
console.group(`searching for: "${text}"`);
|
||||
console.info(`search on ${indexes.length} indexes`, indexes);
|
||||
@@ -82,10 +92,16 @@ export const useSearch = defineStore("search", ({
|
||||
toggleUseOfIndex(idx: string) {
|
||||
if(this.$state.searchUsing.includes(idx)) {
|
||||
this.$state.searchUsing = this.$state.searchUsing.filter(i => i !== idx);
|
||||
this.search(this.searchQuery, true);
|
||||
} else {
|
||||
this.$state.searchUsing = [...this.$state.searchUsing, idx];
|
||||
this.search(this.searchQuery, true);
|
||||
}
|
||||
},
|
||||
setUseOfIndexes(indexes: string[]) {
|
||||
this.$state.searchUsing = indexes;
|
||||
this.search(this.searchQuery, true);
|
||||
},
|
||||
statsUpdate(s: MeiliSearchStats) {
|
||||
this.$state.stats = s;
|
||||
},
|
||||
@@ -117,6 +133,7 @@ if (import.meta.hot) {
|
||||
import.meta.hot)
|
||||
);
|
||||
}
|
||||
|
||||
//#endregion STORE
|
||||
|
||||
//#region SERVICE
|
||||
@@ -141,7 +158,6 @@ export const install: UserModule = ({ isClient }) => {
|
||||
s.$state.searchStatus = !h ? "not-ready": "ready";
|
||||
|
||||
if(h === true && !isActive.value) {
|
||||
createIndexes();
|
||||
resume();
|
||||
};
|
||||
if(h === false && isActive.value) pause();
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import { onStartTyping } from "@vueuse/core";
|
||||
import { useSearch } from "~/modules/search";
|
||||
const el = ref();
|
||||
const searchText = ref("");
|
||||
const s = useSearch();
|
||||
const searchText = ref(s.$state.searchQuery);
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
@@ -26,28 +26,41 @@ onStartTyping(() => {
|
||||
<h1 class="text-xl mb-4">
|
||||
MeiliSearch Playground
|
||||
</h1>
|
||||
<div class="grid grid-cols-3 px-4 gap-x-4">
|
||||
<div class="left">
|
||||
<!-- -->
|
||||
</div>
|
||||
|
||||
<div class="centered">
|
||||
<input
|
||||
id="input"
|
||||
ref="el"
|
||||
v-model="searchText"
|
||||
:placeholder="t('home.ask-for-search')"
|
||||
:aria-label="t('home.ask-for-search')"
|
||||
type="text"
|
||||
autocomplete="false"
|
||||
spellcheck="false"
|
||||
class="px-auto px-4 py-4 w-350px text-center bg-transparent self-center"
|
||||
border="~ rounded gray-200 dark:gray-700"
|
||||
outline="none active:none"
|
||||
>
|
||||
<label class="hidden" for="input">{{ t('home.ask-for-search') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="right flex flex-col items-center p-1 rounded">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<input
|
||||
id="input"
|
||||
ref="el"
|
||||
v-model="searchText"
|
||||
:placeholder="t('home.ask-for-search')"
|
||||
:aria-label="t('home.ask-for-search')"
|
||||
type="text"
|
||||
autocomplete="false"
|
||||
spellcheck="false"
|
||||
class="px-auto px-4 py-3 w-350px text-center bg-transparent self-center"
|
||||
border="~ rounded gray-200 dark:gray-700"
|
||||
outline="none active:none"
|
||||
>
|
||||
<label class="hidden" for="input">{{ t('home.ask-for-search') }}</label>
|
||||
|
||||
<div class="results-area grid grid-cols-3 gap-4 h-full mt-8 mx-4">
|
||||
<div class="flex flex-col col-span-1 space-y-4">
|
||||
<search-indexes class="" />
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 rounded-md bg-gray-100/25 dark:bg-gray-900/25">
|
||||
<div class="col-span-2 rounded-md">
|
||||
<search-results :query="searchText" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"push-typescript": "node bin/push-typescript.js",
|
||||
"push-consolidated": "node bin/push-consolidated.js",
|
||||
"restart": "docker compose restart",
|
||||
"build": "run-p build:*",
|
||||
"build:cli": "tsup src/cli/*.ts --format=esm,cjs --clean --sourcemap -d bin",
|
||||
"build:npm": "tsup src/index.ts --dts --format=esm,cjs --sourcemap --clean -d dist ",
|
||||
"test": "vitest run",
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { githubPipeline } from "~/pipelines/githubPipeline";
|
||||
|
||||
(async () => {
|
||||
await githubPipeline();
|
||||
})();
|
||||
@@ -1,19 +0,0 @@
|
||||
import { GithubMapper } from "~/mappers/GithubMapper";
|
||||
import { RepoModel } from "~/models/RepoModel";
|
||||
import { IMeilisearchAddOrReplace } from "~/types";
|
||||
import { getRepo } from "~/utils/github/getRepo";
|
||||
|
||||
/**
|
||||
* Iterates over all marked repos and updates Meilisearch documents
|
||||
* associated to them.
|
||||
*/
|
||||
export async function githubPipeline() {
|
||||
const model = await RepoModel;
|
||||
const waitFor: Promise<IMeilisearchAddOrReplace>[] = [];
|
||||
for (const repo of REPOS) {
|
||||
const resp = await getRepo(repo);
|
||||
waitFor.push(model.query.addOrReplaceDoc(GithubMapper(resp)));
|
||||
}
|
||||
|
||||
await Promise.all(waitFor);
|
||||
}
|
||||
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
@@ -40,6 +40,7 @@ importers:
|
||||
eslint-plugin-cypress: ^2.12.1
|
||||
floating-vue: ^2.0.0-beta.3
|
||||
https-localhost: ^4.7.0
|
||||
inferred-types: ^0.18.4
|
||||
jsdom: ^19.0.0
|
||||
markdown-it-collapsible: ^1.0.0
|
||||
markdown-it-link-attributes: ^4.0.0
|
||||
@@ -73,6 +74,7 @@ importers:
|
||||
'@vueuse/head': 0.7.5_vue@3.2.29
|
||||
date-fns: 2.28.0
|
||||
floating-vue: 2.0.0-beta.3_vue@3.2.29
|
||||
inferred-types: 0.18.4
|
||||
nprogress: 0.2.0
|
||||
pinia: 2.0.11_typescript@4.5.5+vue@3.2.29
|
||||
prism-theme-vars: 0.2.2
|
||||
|
||||
Reference in New Issue
Block a user