UI: Allow editing of title (#246)
- make <h1> title contentEditable - title setting persists across reloads in localStorage
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { useEffect, useCallback } from "react";
|
||||||
import { BrowserRouter as Router, Routes, Route, Navigate, NavLink } from "react-router-dom";
|
import { BrowserRouter as Router, Routes, Route, Navigate, NavLink } from "react-router-dom";
|
||||||
import { useTheme } from "./contexts/ThemeProvider";
|
import { useTheme } from "./contexts/ThemeProvider";
|
||||||
import { APIProvider } from "./contexts/APIProvider";
|
import { APIProvider } from "./contexts/APIProvider";
|
||||||
@@ -6,9 +7,23 @@ import ModelPage from "./pages/Models";
|
|||||||
import ActivityPage from "./pages/Activity";
|
import ActivityPage from "./pages/Activity";
|
||||||
import ConnectionStatus from "./components/ConnectionStatus";
|
import ConnectionStatus from "./components/ConnectionStatus";
|
||||||
import { RiSunFill, RiMoonFill } from "react-icons/ri";
|
import { RiSunFill, RiMoonFill } from "react-icons/ri";
|
||||||
|
import { usePersistentState } from "./hooks/usePersistentState";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { isNarrow, toggleTheme, isDarkMode } = useTheme();
|
const { isNarrow, toggleTheme, isDarkMode } = useTheme();
|
||||||
|
const [appTitle, setAppTitle] = usePersistentState("app-title", "llama-swap");
|
||||||
|
|
||||||
|
const handleTitleChange = useCallback(
|
||||||
|
(newTitle: string) => {
|
||||||
|
setAppTitle(newTitle);
|
||||||
|
document.title = newTitle;
|
||||||
|
},
|
||||||
|
[setAppTitle]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = appTitle; // Set initial title
|
||||||
|
}, [appTitle]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router basename="/ui/">
|
<Router basename="/ui/">
|
||||||
@@ -16,7 +31,28 @@ function App() {
|
|||||||
<div className="flex flex-col h-screen">
|
<div className="flex flex-col h-screen">
|
||||||
<nav className="bg-surface border-b border-border p-2 h-[75px]">
|
<nav className="bg-surface border-b border-border p-2 h-[75px]">
|
||||||
<div className="flex items-center justify-between mx-auto px-4 h-full">
|
<div className="flex items-center justify-between mx-auto px-4 h-full">
|
||||||
{!isNarrow && <h1 className="flex items-center p-0">llama-swap</h1>}
|
{!isNarrow && (
|
||||||
|
<h1
|
||||||
|
contentEditable
|
||||||
|
suppressContentEditableWarning
|
||||||
|
className="flex items-center p-0 outline-none hover:bg-gray-100 dark:hover:bg-gray-700 rounded px-1"
|
||||||
|
onBlur={(e) =>
|
||||||
|
handleTitleChange(e.currentTarget.textContent?.replace(/\n/g, "").trim() || "llama-swap")
|
||||||
|
}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
const sanitizedText =
|
||||||
|
e.currentTarget.textContent?.replace(/\n/g, "").trim().substring(0, 25) || "llama-swap";
|
||||||
|
handleTitleChange(sanitizedText);
|
||||||
|
e.currentTarget.textContent = sanitizedText;
|
||||||
|
e.currentTarget.blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{appTitle}
|
||||||
|
</h1>
|
||||||
|
)}
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<NavLink to="/" className={({ isActive }) => (isActive ? "navlink active" : "navlink")}>
|
<NavLink to="/" className={({ isActive }) => (isActive ? "navlink active" : "navlink")}>
|
||||||
Logs
|
Logs
|
||||||
|
|||||||
Reference in New Issue
Block a user