8000 feat(brand): Lens mark, favicon and app icons from the v1.0 brand kit by adeelahmad · Pull Request #138 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fastapi_backend/app/domain/iiif.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def _prune(d):


def site_label(cfg, base):
return (cfg["iiif"].get("provider") or {}).get("name") or urllib.parse.urlsplit(base).netloc or "Lens Archive"
return (cfg["iiif"].get("provider") or {}).get("name") or urllib.parse.urlsplit(base).netloc or "Lens"


def probe_service(cfg, base, rid, what, heading="Sign in to listen"):
Expand Down
2 changes: 1 addition & 1 deletion nextjs-frontend/__tests__/embed-model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe("player address and snippet", () => {
});
expect(s).toContain('src="https://a.example/embed/1?s=x&t=2"');
expect(s).toContain('width="360" height="520"');
expect(s).toContain('title="Lens Archive player: Episode "12""');
expect(s).toContain('title="Lens player: Episode "12""');
});
});

Expand Down
3 changes: 2 additions & 1 deletion nextjs-frontend/__tests__/page-chat.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ describe("page chat model", () => {
expect(tidyText(" A \t b c \n\n\n\n d ", 100)).toBe("A b c\n\nd");
expect(tidyText("abcdef", 3)).toBe("abc");
expect(pageTitle("Library · Lens Archive")).toBe("Library");
expect(pageTitle("Library · Lens")).toBe("Library");
expect(pageTitle("Lens Archive")).toBe("Lens Archive");
});

Expand Down Expand Up @@ -130,7 +131,7 @@ describe("PageChat", () => {
beforeEach(() => {
path = "/library";
localStorage.clear();
document.title = "Library · Lens Archive";
document.title = "Library · Lens";
m(Chats.chatCapabilities).mockReturnValue(
ok({ configured: true, model: "m", tools: false, max_steps: 6, check: true, models: ["m"] }),
);
Expand Down
2 changes: 1 addition & 1 deletion nextjs-frontend/app/(app)/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { startTransition } from "react";
import { ErrorState, isUnreachable, ServerUnreachable } from "@/components/errors/error-states";
import { Button } from "@/components/ui/button";

/** Errors inside the app: "Can't reach Lens Archive" with automatic retries, or what failed and what to do next (AC6). */
/** Errors inside the app: "Can't reach Lens" with automatic retries, or what failed and what to do next (AC6). */
export default function AppError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
const router = useRouter();
const retry = () =>
Expand Down
Binary file added nextjs-frontend/app/apple-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added nextjs-frontend/app/favicon.ico
Binary file not shown.
2 changes: 1 addition & 1 deletion nextjs-frontend/app/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 16 additions & 3 deletions nextjs-frontend/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";

import { THEME_SCRIPT } from "@/components/app-shell/theme";
import "./globals.css";

export const metadata: Metadata = {
title: { default: "Lens Archive", template: "%s · Lens Archive" },
description: "An archive for recorded speech and video.",
title: { default: "Lens", template: "%s · Lens" },
description: "Your life. Your AI.",
// Setting icons here replaces Next's app/icon.svg and app/apple-icon.png links, so all of them are listed.
icons: {
icon: [{ url: "/icon.svg", type: "image/svg+xml" }],
apple: "/apple-icon.png",
other: [{ rel: "mask-icon", url: "/safari-pinned-tab.svg", color: "#1A73E8" }],
},
};

export const viewport: Viewport = {
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "#FFFFFF" },
{ media: "(prefers-color-scheme: dark)", color: "#202124" },
],
};

export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
Expand Down
23 changes: 23 additions & 0 deletions nextjs-frontend/app/manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { MetadataRoute } from "next";

/* The installable-app manifest, from the brand kit's site.webmanifest. Icons live in public/pwa. */
export default function manifest(): MetadataRoute.Manifest {
const icon = (purpose: "any" | "maskable", size: number) => ({
src: `/pwa/lens-light-${purpose}-${size}.png`,
sizes: `${size}x${size}`,
type: "image/png",
purpose,
});
return {
id: "/",
name: "Lens",
short_name: "Lens",
description: "Your life. Your AI.",
start_url: "/",
scope: "/",
display: "standalone",
background_color: "#FFFFFF",
theme_color: "#1A73E8",
icons: [icon("any", 192), icon("any", 512), icon("maskable", 192), icon("maskable", 512)],
};
}
2 changes: 1 addition & 1 deletion nextjs-frontend/app/styles/tokens.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Lens Archive design tokens.
* Lens design tokens.
* Base: the Aladdin design system (colour is semantic: blue = working/selection, green = ready, red = failed/destructive,
* gold = needs a person's decision, grey = waiting). Lens adds a serif for transcripts, a dark theme, speaker and emotion
* palettes, and highlight tokens. Generated from the design handoff (la-kit.js + _ds/tokens); edit here, not in components.
Expand Down
10 changes: 4 additions & 6 deletions nextjs-frontend/components/auth/auth-card.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import type { ReactNode } from "react";

import { Signature } from "@/components/ui/loop";
import { LensMark } from "@/components/ui/lens-mark";
import { cn } from "@/lib/utils";

/** The four-dot signature and the wordmark, as on the sign-in card (Access AC1–AC2). */
/** The Lens mark and wordmark, as on the sign-in card (Access AC1–AC2). */
export function AuthBrand({ className, suffix }: { className?: string; suffix?: ReactNode }) {
return (
<div className={cn("flex items-center gap-[9px]", className)}>
<Signature grid size={8} gap={3} />
<span className="whitespace-nowrap text-[18px] font-extrabold leading-none tracking-[-0.03em] text-blue">
Lens Archive
</span>
<LensMark size={24} />
<span className="whitespace-nowrap text-[20px] font-bold leading-none tracking-[-0.02em] text-fg">Lens</span>
{suffix && <span className="text-[12px] text-fg-muted">{suffix}</span>}
</div>
);
Expand Down
32 changes: 12 additions & 20 deletions nextjs-frontend/components/brand.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import Link from "next/link";

import { Signature } from "@/components/ui/loop";
import { LensMark } from "@/components/ui/lens-mark";
import { cn } from "@/lib/utils";

/**
* The Lens Archive wordmark: DM Sans 800, -0.03em, blue, over a four-segment loop bar
* (blue → red → green → gold). Collapsed, it becomes the four-dot signature.
* The Lens lockup from the brand kit: the Search Lens mark beside "Lens" in DM Sans, ink on light and
* white on dark. Collapsed, it is the mark alone.
*/
export function Brand({
className,
Expand All @@ -19,26 +19,18 @@ export function Brand({
size?: number;
}) {
return (
<Link href={href} aria-label="Lens Archive home" className={cn("inline-flex items-center", className)}>
{collapsed ? (
<span className="px-1.5 py-0.5">
<Signature grid size={8} gap={4} />
</span>
) : (
<span className="inline-flex flex-col gap-1">
<Link href={href} aria-label="Lens home" className={cn("inline-flex items-center", className)}>
<span className="inline-flex items-center" style={{ gap: Math.round(size * 0.4) }}>
<LensMark size={Math.round(size * 1.35)} />
{!collapsed && (
<span
className="whitespace-nowrap font-extrabold leading-none tracking-[-0.03em] text-blue"
style={{ fontSize: size }}
className="whitespace-nowrap font-bold leading-none tracking-[-0.02em] text-fg"
style={{ fontSize: size * 1.1 }}
>
Lens Archive
Lens
</span>
<span aria-hidden className="flex h-[3px] overflow-hidden rounded-pill">
{["var(--aladdin-blue)", "var(--aladdin-red)", "var(--aladdin-green)", "var(--aladdin-gold)"].map((c) => (
<span key={c} className="flex-1" style={{ background: c }} />
))}
</span>
</span>
)}
)}
</span>
</Link>
);
}
2 changes: 1 addition & 1 deletion nextjs-frontend/components/errors/error-states.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export function ServerUnreachable({
className={className}
icon={<ServerOff />}
code={unreachableCode(error)}
title="Can’t reach Lens Archive"
title="Can’t reach Lens"
bar={
<div
role="alert"
Expand Down
4 changes: 2 additions & 2 deletions nextjs-frontend/components/home/assistant-home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useEffect, useState, type ReactNode } from "react";

import { useChats } from "@/components/chat/data";
import { greeting } from "@/components/home/attention";
import { Signature } from "@/components/ui/loop";
import { LensMark } from "@/components/ui/lens-mark";
import { useToast } from "@/components/ui/toast";
import { relative } from "@/lib/format";
import { useArchive } from "@/lib/hooks/session";
Expand Down Expand Up @@ -74,7 +74,7 @@ export function AssistantHome({ switcher }: { switcher?: ReactNode }) {
{switcher && <div className="flex justify-end pt-5 md:pt-6">{switcher}</div>}
<div className="mx-auto flex w-full max-w-[640px] flex-1 flex-col items-center justify-center gap-7 pb-[10vh] pt-8">
<div className="flex flex-col items-center gap-3">
<Signature grid size={12} gap={6} />
<LensMark size={48} />
<h1 className="text-center text-[30px] font-extrabold leading-[1.15] tracking-[-.025em] text-fg md:text-[36px]">
{now ? `${greeting(now)}${first ? `, ${first}` : ""}` : "Welcome back"}
</h1>
Expand Down
4 changes: 2 additions & 2 deletions nextjs-frontend/components/iiif/import.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,8 @@ export function IiifImport() {

{noAudio && (
<Banner tone="warning" title="Nothing here to listen to.">
None of its {canvases.length} items has audio or captions. Lens Archive imports audio (and audio with
transcripts) only.
None of its {canvases.length} items has audio or captions. Lens imports audio (and audio with transcripts)
only.
</Banner>
)}

Expand Down
2 changes: 1 addition & 1 deletion nextjs-frontend/components/public/recording-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function PublicRecordingView({
});
const title = q.data?.title;
useEffect(() => {
if (title) document.title = `${title} · Lens Archive`;
if (title) document.title = `${title} · Lens`;
}, [title]);

if (q.isPending) return <PageSkeleton />;
Expand Down
2 changes: 1 addition & 1 deletion nextjs-frontend/components/recording/recording-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function RecordingPage({
const player = usePlayer(id, state.phase === "processing" ? 5000 : false);
const title = rec.data?.title;
useEffect(() => {
if (title) document.title = `${title} · Lens Archive`;
if (title) document.title = `${title} · Lens`;
}, [title]);

if (rec.isError || player.isError) {
Expand Down
2 changes: 1 addition & 1 deletion nextjs-frontend/components/recording/selection-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export function SelectionToolbar({ box }: { box: RefObject<HTMLDivElement | null
open={moment != null}
=> !o && setMoment(null)}
title="Share a moment"
description="A IIIF link that opens this time range in Lens Archive and in any viewer that supports content state."
description="A IIIF link that opens this time range in Lens and in any viewer that supports content state."
>
{moment && <ShareMoment recordingId={id} initial={moment} />}
</Dialog>
Expand Down
4 changes: 2 additions & 2 deletions nextjs-frontend/components/reports/recording-report.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ function Paper({ rec, player }: { rec: Recording; player: Player }) {
return (
<article className="report-paper flex min-h-[1123px] w-[794px] max-w-full flex-col gap-[18px] px-[68px] py-16 font-sans shadow-[0_2px_12px_rgba(0,0,0,.14)] max-md:px-6 max-md:py-8">
<div className="flex justify-between text-[11px] font-medium uppercase leading-none tracking-[.04em] text-fg-secondary">
<span>Lens Archive · {rec.namespace}</span>
<span>Lens · {rec.namespace}</span>
<span>Recording report</span>
</div>
<h2 className="text-[28px] font-bold leading-[1.2] tracking-[-.015em]">{rec.title || "Untitled"}</h2>
Expand Down Expand Up @@ -160,7 +160,7 @@ function Paper({ rec, player }: { rec: Recording; player: Player }) {
<span className="flex-1" />
<div className="flex justify-between border-t border-border pt-2 text-[10px] leading-none text-fg-muted">
<span>Printed {shortDate(new Date().toISOString())} · Recording report (built-in)</span>
<span>Lens Archive</span>
<span>Lens</span>
</div>
</article>
);
Expand Down
2 changes: 1 addition & 1 deletion nextjs-frontend/components/settings/section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ export function SettingsSection({
{count(manifests)} Manifest{manifests === 1 ? "" : "s"} and {count(namespaces.length + 1)} Collections
</b>
. Links saved in other viewers, harvesters and citations stop resolving unless the old address keeps
redirecting — set that up on your web server; Lens Archive can’t redirect it itself.
redirecting — set that up on your web server; Lens can’t redirect it itself.
</span>
</div>
<div className="grid grid-cols-[48px_minmax(0,1fr)] gap-1.5 font-mono text-[12.5px] leading-normal">
Expand Down
2 changes: 1 addition & 1 deletion nextjs-frontend/components/sharing/embed-builder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function Preview({ src, size, title }: { src: string | null; size: Size; title:
<iframe
key={src}
src={src}
title={`Lens Archive player: ${title} (preview)`}
title={`Lens player: ${title} (preview)`}
width={width}
height={height}
className="origin-top-left rounded-md border border-border bg-background"
Expand Down
2 changes: 1 addition & 1 deletion nextjs-frontend/components/sharing/embed-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const escUrl = (s: string) => s.replace(/"/g, "%22").replace(/</g, "%3C");
/** The iframe snippet people paste into their site. */
export function iframeSnippet({ src, size, title }: { src: string; size: Size; title: string }): string {
const { width, height } = SIZES[size];
return `<iframe src="${escUrl(src)}"\n width="${width}" height="${height}" title="Lens Archive player: ${esc(title)}"\n loading="lazy" style="border:0"></iframe>`;
return `<iframe src="${escUrl(src)}"\n width="${width}" height="${height}" title="Lens player: ${esc(title)}"\n loading="lazy" style="border:0"></iframe>`;
}

/** "blog.example.com" or "https://blog.example.com/post" → "https://blog.example.com"; null when it isn't a site. */
Expand Down
40 changes: 40 additions & 0 deletions nextjs-frontend/components/ui/lens-mark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { SVGProps } from "react";

/* The Search Lens mark from the v1.0 brand kit: red, green and blue ring, blue memory core, gold handle.
The fills are the kit's exact brand colours, which it uses on light and dark surfaces alike. */
const PATHS = [
["#EA4335", "M79.388 21.838A94 94 0 0 1 199.399 75.396L169.646 87.176A62 62 0 0 0 90.49 51.851Z"],
["#34A853", "M199.155 74.787A94 94 0 0 1 199.155 145.213A16 16 0 0 1 169.485 133.226A62 62 0 0 0 169.485 86.774Z"],
["#1A73E8", "M112 204A94 94 0 0 1 79.85 21.669A16 16 0 0 1 90.795 51.739A62 62 0 0 0 112 172A16 16 0 0 1 112 204Z"],
["#1A73E8", "M87 110A25 25 0 1 0 137 110A25 25 0 1 0 87 110Z"],
[
"#F9AB00",
"M159.979 183.021L203.979 227.021A17 17 0 0 0 228.021 202.979L184.021 158.979A17 17 0 0 0 159.979 183.021Z",
],
] as const;

/** The Lens logo mark. Decorative unless given a label; monochrome follows the text colour. */
export function LensMark({
size = 24,
monochrome,
label,
...props
}: SVGProps<SVGSVGElement> & { size?: number; monochrome?: boolean; label?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
width={size}
height={size}
role={label ? "img" : undefined}
aria-label={label}
aria-hidden={label ? undefined : true}
focusable="false"
{...props}
>
{PATHS.map(([fill, d]) => (
<path key={d} fill={monochrome ? "currentColor" : fill} d={d} />
))}
</svg>
);
}
2 changes: 1 addition & 1 deletion nextjs-frontend/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ export const config = {
// Under /iiif only the backend's IIIF resources are skipped (collection, discovery, auth, /iiif/<id>/…); the app's own
// IIIF pages (/iiif, /iiif/collections/…, /iiif/import, /iiif/metadata/…) need the session like any other page.
matcher: [
"/((?!api/|\\.well-known/|mcp(?:/|$)|_next/|embed/|s/|id/|ns$|iiif/(?:collection(?!s)|discovery|auth|\\d)|reports/|static/|fonts/|favicon\\.ico|icon\\.svg|robots\\.txt).*)",
"/((?!api/|\\.well-known/|mcp(?:/|$)|_next/|embed/|s/|id/|ns$|iiif/(?:collection(?!s)|discovery|auth|\\d)|reports/|static/|fonts/|favicon\\.ico|icon\\.svg|apple-icon\\.png|manifest\\.webmanifest|safari-pinned-tab\\.svg|pwa/|robots\\.txt).*)",
],
};
Binary file added nextjs-frontend/public/pwa/lens-light-any-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added nextjs-frontend/public/pwa/lens-light-any-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions nextjs-frontend/public/safari-pinned-tab.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
0