Add caption, semantic description to search
This commit is contained in:
parent
e21ed7942b
commit
8a03ea8217
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -21,6 +21,7 @@
|
|||||||
"headlessui",
|
"headlessui",
|
||||||
"hgetall",
|
"hgetall",
|
||||||
"hset",
|
"hset",
|
||||||
|
"ILIKE",
|
||||||
"jpgs",
|
"jpgs",
|
||||||
"Lightbox",
|
"Lightbox",
|
||||||
"Makernote",
|
"Makernote",
|
||||||
|
|||||||
@ -27,6 +27,7 @@ export type CommandKSection = {
|
|||||||
accessory?: ReactNode
|
accessory?: ReactNode
|
||||||
items: {
|
items: {
|
||||||
label: string
|
label: string
|
||||||
|
keywords?: string[]
|
||||||
annotation?: ReactNode
|
annotation?: ReactNode
|
||||||
annotationAria?: string
|
annotationAria?: string
|
||||||
accessory?: ReactNode
|
accessory?: ReactNode
|
||||||
@ -157,8 +158,13 @@ export default function CommandKClient({
|
|||||||
open={isOpen}
|
open={isOpen}
|
||||||
onOpenChange={setIsOpen}
|
onOpenChange={setIsOpen}
|
||||||
label="Global Command Menu"
|
label="Global Command Menu"
|
||||||
filter={(value, search) =>
|
filter={(value, search, keywords) => {
|
||||||
value.toLowerCase().includes(search.toLowerCase()) ? 1 : 0}
|
const searchFormatted = search.trim().toLocaleLowerCase();
|
||||||
|
return (
|
||||||
|
value.toLocaleLowerCase().includes(searchFormatted) ||
|
||||||
|
keywords?.includes(searchFormatted)
|
||||||
|
) ? 1 : 0 ;
|
||||||
|
}}
|
||||||
loop
|
loop
|
||||||
>
|
>
|
||||||
<Modal
|
<Modal
|
||||||
@ -223,16 +229,18 @@ export default function CommandKClient({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{items.map(({
|
{items.map(({
|
||||||
accessory,
|
|
||||||
label,
|
label,
|
||||||
|
keywords,
|
||||||
annotation,
|
annotation,
|
||||||
annotationAria,
|
annotationAria,
|
||||||
|
accessory,
|
||||||
path,
|
path,
|
||||||
action,
|
action,
|
||||||
}) =>
|
}) =>
|
||||||
<Command.Item
|
<Command.Item
|
||||||
key={`${heading} ${label}`}
|
key={`${heading} ${label}`}
|
||||||
value={`${heading} ${label}`}
|
value={`${heading} ${label}`}
|
||||||
|
keywords={keywords}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'px-2',
|
'px-2',
|
||||||
accessory ? 'py-2' : 'py-1',
|
accessory ? 'py-2' : 'py-1',
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Photo,
|
Photo,
|
||||||
|
altTextForPhoto,
|
||||||
shouldShowCameraDataForPhoto,
|
shouldShowCameraDataForPhoto,
|
||||||
shouldShowExifDataForPhoto,
|
shouldShowExifDataForPhoto,
|
||||||
titleForPhoto,
|
titleForPhoto,
|
||||||
@ -54,7 +55,7 @@ export default function PhotoLarge({
|
|||||||
contentMain={
|
contentMain={
|
||||||
<ImageLarge
|
<ImageLarge
|
||||||
className="w-full"
|
className="w-full"
|
||||||
alt={titleForPhoto(photo)}
|
alt={altTextForPhoto(photo)}
|
||||||
href={pathForPhoto(photo, primaryTag)}
|
href={pathForPhoto(photo, primaryTag)}
|
||||||
src={photo.url}
|
src={photo.url}
|
||||||
aspectRatio={photo.aspectRatio}
|
aspectRatio={photo.aspectRatio}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Photo, titleForPhoto } from '.';
|
import { Photo, altTextForPhoto } from '.';
|
||||||
import ImageSmall from '@/components/ImageSmall';
|
import ImageSmall from '@/components/ImageSmall';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
@ -34,7 +34,7 @@ export default function PhotoSmall({
|
|||||||
aspectRatio={photo.aspectRatio}
|
aspectRatio={photo.aspectRatio}
|
||||||
blurData={photo.blurData}
|
blurData={photo.blurData}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
alt={titleForPhoto(photo)}
|
alt={altTextForPhoto(photo)}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Photo, titleForPhoto } from '.';
|
import { Photo, altTextForPhoto } from '.';
|
||||||
import ImageTiny from '@/components/ImageTiny';
|
import ImageTiny from '@/components/ImageTiny';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
@ -31,7 +31,7 @@ export default function PhotoTiny({
|
|||||||
src={photo.url}
|
src={photo.url}
|
||||||
aspectRatio={photo.aspectRatio}
|
aspectRatio={photo.aspectRatio}
|
||||||
blurData={photo.blurData}
|
blurData={photo.blurData}
|
||||||
alt={titleForPhoto(photo)}
|
alt={altTextForPhoto(photo)}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -47,7 +47,9 @@ export default function useAiImageQueries(
|
|||||||
const hasRunAllQueriesOnce = useRef(false);
|
const hasRunAllQueriesOnce = useRef(false);
|
||||||
|
|
||||||
const request = useCallback(async () => {
|
const request = useCallback(async () => {
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
console.log('RUNNING ALL AI QUERIES');
|
console.log('RUNNING ALL AI QUERIES');
|
||||||
|
}
|
||||||
hasRunAllQueriesOnce.current = true;
|
hasRunAllQueriesOnce.current = true;
|
||||||
requestTitleCaption();
|
requestTitleCaption();
|
||||||
requestTags();
|
requestTags();
|
||||||
|
|||||||
@ -168,6 +168,9 @@ export const translatePhotoId = (id: string) =>
|
|||||||
export const titleForPhoto = (photo: Photo) =>
|
export const titleForPhoto = (photo: Photo) =>
|
||||||
photo.title || 'Untitled';
|
photo.title || 'Untitled';
|
||||||
|
|
||||||
|
export const altTextForPhoto = (photo: Photo) =>
|
||||||
|
photo.semanticDescription || titleForPhoto(photo);
|
||||||
|
|
||||||
export const photoLabelForCount = (count: number) =>
|
export const photoLabelForCount = (count: number) =>
|
||||||
count === 1 ? 'Photo' : 'Photos';
|
count === 1 ? 'Photo' : 'Photos';
|
||||||
|
|
||||||
@ -247,3 +250,9 @@ export const shouldShowCameraDataForPhoto = (photo: Photo) =>
|
|||||||
|
|
||||||
export const shouldShowExifDataForPhoto = (photo: Photo) =>
|
export const shouldShowExifDataForPhoto = (photo: Photo) =>
|
||||||
SHOW_EXIF_DATA && photoHasExifData(photo);
|
SHOW_EXIF_DATA && photoHasExifData(photo);
|
||||||
|
|
||||||
|
export const getKeywordsForPhoto = (photo: Photo) =>
|
||||||
|
(photo.caption ?? '').split(' ')
|
||||||
|
.concat((photo.semanticDescription ?? '').split(' '))
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(keyword => keyword.toLocaleLowerCase());
|
||||||
|
|||||||
@ -294,7 +294,7 @@ export type GetPhotosOptions = {
|
|||||||
sortBy?: 'createdAt' | 'takenAt' | 'priority'
|
sortBy?: 'createdAt' | 'takenAt' | 'priority'
|
||||||
limit?: number
|
limit?: number
|
||||||
offset?: number
|
offset?: number
|
||||||
title?: string
|
query?: string
|
||||||
tag?: string
|
tag?: string
|
||||||
camera?: Camera
|
camera?: Camera
|
||||||
simulation?: FilmSimulation
|
simulation?: FilmSimulation
|
||||||
@ -344,7 +344,7 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => {
|
|||||||
sortBy = PRIORITY_ORDER_ENABLED ? 'priority' : 'takenAt',
|
sortBy = PRIORITY_ORDER_ENABLED ? 'priority' : 'takenAt',
|
||||||
limit = PHOTO_DEFAULT_LIMIT,
|
limit = PHOTO_DEFAULT_LIMIT,
|
||||||
offset = 0,
|
offset = 0,
|
||||||
title,
|
query,
|
||||||
tag,
|
tag,
|
||||||
camera,
|
camera,
|
||||||
simulation,
|
simulation,
|
||||||
@ -370,9 +370,10 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => {
|
|||||||
wheres.push(`taken_at <= $${valueIndex++}`);
|
wheres.push(`taken_at <= $${valueIndex++}`);
|
||||||
values.push(takenAfterInclusive.toISOString());
|
values.push(takenAfterInclusive.toISOString());
|
||||||
}
|
}
|
||||||
if (title) {
|
if (query) {
|
||||||
wheres.push(`LOWER(title) LIKE $${valueIndex++}`);
|
// eslint-disable-next-line max-len
|
||||||
values.push(`%${title.toLowerCase()}%`);
|
wheres.push(`CONCAT(title, ' ', caption, ' ', semantic_description) ILIKE $${valueIndex++}`);
|
||||||
|
values.push(`%${query.toLocaleLowerCase()}%`);
|
||||||
}
|
}
|
||||||
if (tag) {
|
if (tag) {
|
||||||
wheres.push(`$${valueIndex++}=ANY(tags)`);
|
wheres.push(`$${valueIndex++}=ANY(tags)`);
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import {
|
|||||||
import { formatCameraText } from '@/camera';
|
import { formatCameraText } from '@/camera';
|
||||||
import { authCached } from '@/auth/cache';
|
import { authCached } from '@/auth/cache';
|
||||||
import { getPhotos } from '@/services/vercel-postgres';
|
import { getPhotos } from '@/services/vercel-postgres';
|
||||||
import { photoQuantityText, titleForPhoto } from '@/photo';
|
import { getKeywordsForPhoto, photoQuantityText, titleForPhoto } from '@/photo';
|
||||||
import PhotoTiny from '@/photo/PhotoTiny';
|
import PhotoTiny from '@/photo/PhotoTiny';
|
||||||
import { formatDate } from '@/utility/date';
|
import { formatDate } from '@/utility/date';
|
||||||
import { formatCount, formatCountDescriptive } from '@/utility/string';
|
import { formatCount, formatCountDescriptive } from '@/utility/string';
|
||||||
@ -139,15 +139,14 @@ export default async function CommandK() {
|
|||||||
]}
|
]}
|
||||||
onQueryChange={async (query) => {
|
onQueryChange={async (query) => {
|
||||||
'use server';
|
'use server';
|
||||||
const photos = (await getPhotos({ title: query, limit: 10 }))
|
const photos = (await getPhotos({ query, limit: 10 }));
|
||||||
.filter(({ title }) => Boolean(title));
|
|
||||||
return photos.length > 0
|
return photos.length > 0
|
||||||
? [{
|
? [{
|
||||||
heading: 'Photos',
|
heading: 'Photos',
|
||||||
accessory: <TbPhoto size={14} />,
|
accessory: <TbPhoto size={14} />,
|
||||||
items: photos.map(photo => ({
|
items: photos.map(photo => ({
|
||||||
accessory: <PhotoTiny photo={photo} />,
|
|
||||||
label: titleForPhoto(photo),
|
label: titleForPhoto(photo),
|
||||||
|
keywords: getKeywordsForPhoto(photo),
|
||||||
annotation: <>
|
annotation: <>
|
||||||
<span className="hidden sm:inline-block">
|
<span className="hidden sm:inline-block">
|
||||||
{formatDate(photo.takenAt)}
|
{formatDate(photo.takenAt)}
|
||||||
@ -156,6 +155,7 @@ export default async function CommandK() {
|
|||||||
{formatDate(photo.takenAt, true)}
|
{formatDate(photo.takenAt, true)}
|
||||||
</span>
|
</span>
|
||||||
</>,
|
</>,
|
||||||
|
accessory: <PhotoTiny photo={photo} />,
|
||||||
path: pathForPhoto(photo),
|
path: pathForPhoto(photo),
|
||||||
})),
|
})),
|
||||||
}]
|
}]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user