feat: update project documentation and configuration files; enhance permissions and layout styles

This commit is contained in:
alexsparkes
2026-05-06 14:14:32 +01:00
parent 3762ce11f8
commit 390474d670
10 changed files with 90 additions and 68 deletions

View File

@@ -1,5 +1,8 @@
{
"permissions": {
"allow": ["Bash(perl -i -pe:*)", "Bash(bun run:*)"]
"allow": [
"Bash(perl -i -pe:*)",
"Bash(bun run:*)"
]
}
}

7
.superset/config.json Normal file
View File

@@ -0,0 +1,7 @@
{
"setup": [],
"teardown": [],
"run": [
"bun run dev"
]
}

View File

@@ -23,14 +23,22 @@ bun run translations:unused # Find unused translation keys
Build outputs: `build/chrome/`, `build/firefox/`, `safari/Mue Extension/Resources/`. Vite's `prepareBuilds` plugin (in `vite.config.mjs`) copies dist + manifests + icons into each browser folder and creates versioned zips.
There are no tests in this project.
E2E tests use Cypress:
```bash
bun run test:e2e # Open Cypress UI
bun run test:e2e:headless # Run headlessly (CI)
```
Tests live in `cypress/e2e/`.
## Architecture
### Bootstrap Flow
1. `src/index.jsx` - Initializes i18n from localStorage language, sets up Sentry, runs data migrations, exposes global `window.t()`, renders `<ErrorBoundary><App/></ErrorBoundary>`
2. `src/App.jsx` - `useAppSetup()` checks first-run state, calls `loadSettings()` (which applies theme, fonts, custom CSS to the DOM), listens for EventBus `'refresh'` events. Renders: `<TranslationProvider>``<Background>` + `<CustomWidgets>` + `<Widgets>` + `<Modals>`
1. `src/index.jsx` - Initializes i18n from localStorage language, sets up Sentry, runs data migrations, exposes global `window.t()`, renders `<RouterProvider router={router} />`
2. `src/router/` - Hash-based router (`createHashRouter`) required for extension compatibility (no physical file routing). `ModalRouter.jsx` and `RouterBridge.jsx` handle modal deep-linking.
3. `src/App.jsx` - `useAppSetup()` checks first-run state, calls `loadSettings()` (which applies theme, fonts, custom CSS to the DOM), listens for EventBus `'refresh'` events. Renders: `<TranslationProvider>``<Background>` + `<CustomWidgets>` + `<Widgets>` + `<Modals>`
### State Management (no Redux/Zustand)
@@ -54,7 +62,7 @@ Pattern: register in `useEffect`, clean up with `EventBus.off()` on unmount.
### Feature Organization (`src/features/`)
Each feature (background, time, quote, greeting, weather, search, quicklinks, message, navbar, stats, marketplace, welcome) follows this structure:
Each feature (background, time, quote, greeting, weather, search, quicklinks, message, navbar, stats, marketplace, welcome, helpers, modals) follows this structure:
```
feature/

45
GEMINI.md Normal file
View File

@@ -0,0 +1,45 @@
# Mue Engineering Standards (GEMINI.md)
This file contains project-specific instructions and guidelines for Mue.
## Stack & Commands
- **Package Manager:** Always use `bun`. Never use `npm` or `yarn`.
- **Framework:** React 19, Vite 7, Manifest V3.
- **Core Commands:**
- `bun install`: Install dependencies.
- `bun run dev`: Start dev server.
- `bun run build`: Production build for Chrome, Firefox, Safari.
- `bun run lint`: Run ESLint and Stylelint.
- `bun run lint:fix`: Auto-fix lint issues.
- `bun run pretty`: Format with Prettier.
- `bun run translations`: Sync locale files from `en_GB.json`.
- `bun run translations:percentages`: Update translation stats.
- `bun run translations:unused`: Find unused translation keys.
- `bun run test:e2e:headless`: Run Cypress tests headlessly.
## Architecture & Patterns
- **State Management:** Use **localStorage** for persistent state.
- **Communication:** Use `src/utils/eventbus.js` for inter-widget/UI communication.
- Register in `useEffect`, cleanup with `EventBus.off()`.
- **I18n:** All user-visible strings MUST use `src/i18n/locales/en_GB.json` as the base.
- Use `useT()` hook or `getMessage()` helper.
- **Routing:** Hash-based router (`createHashRouter`) in `src/router/`.
- **Path Aliases:** Use `@/`, `components/`, `features/`, etc. (see `vite.config.mjs`).
## Coding Rules
- **No Comments:** Keep code self-explanatory through descriptive naming.
- **No Emojis:** Do not use emojis in code strings or logs (except user-facing toasts).
- **Naming:** Concise names. Use `const [open, setOpen] = useState(false)`, not `isOpen`.
- **Commits:** Conventional commits (`feat:`, `fix:`, etc.) are enforced.
- **Translations:** Edit `en_GB.json` first, then run `bun run translations`.
## Directory Structure
- `src/features/`: Feature-based organization (background, time, quote, etc.).
- `src/components/`: Shared UI elements and layouts.
- `src/utils/`: Shared utilities and helpers.
- `manifest/`: Extension manifest templates.
- `safari/`: Safari-specific project files.

View File

@@ -444,7 +444,6 @@ function PhotoInformation({ info, url, api }) {
<Suspense fallback={<div className="location-map-container" />}>
<LocationMap latitude={latitude} longitude={longitude} crosshair={crosshair} />
</Suspense>
<div className="section-divider" />
</>
)}

View File

@@ -614,6 +614,7 @@
.location-map-section {
width: 100%;
grid-column: 1 / -1;
a {
display: block;

View File

@@ -1,6 +1,5 @@
import { useEffect, useRef, useState, useMemo } from 'react';
import Modal from 'react-modal';
import { MdInfoOutline } from 'react-icons/md';
import { ShareModal } from 'components/Elements';
import { useQuoteState, useQuoteLoader, useQuoteActions } from './hooks';
@@ -135,7 +134,6 @@ export default function Quote() {
<AuthorInfo
author={quoteData.author}
authorOccupation={quoteData.authorOccupation}
authorlink={quoteData.authorlink}
authorimg={quoteData.authorimg}
authorimglicense={quoteData.authorimglicense}
onCopy={copyQuote}
@@ -146,10 +144,6 @@ export default function Quote() {
/>
))}
<div className="quote-info-text" onClick={() => toggleInfoModal(true)}>
<MdInfoOutline />
<span>About this quote</span>
</div>
</div>
);
}

View File

@@ -1,16 +1,10 @@
import { MdPerson } from 'react-icons/md';
import { HiMiniArrowUpRight } from 'react-icons/hi2';
import { Tooltip } from 'components/Elements';
import { useT } from 'contexts';
import QuoteButtons from './QuoteButtons';
/**
* Author information component (modern style)
*/
export default function AuthorInfo({
author,
authorOccupation,
authorlink,
authorimg,
authorimglicense,
onCopy,
@@ -19,9 +13,7 @@ export default function AuthorInfo({
onInfo,
isFavourited,
}) {
const t = useT();
const showAuthorImg = localStorage.getItem('authorImg') !== 'false';
const hasLink = authorOccupation !== 'Unknown' && authorlink !== null;
return (
<div className="author-holder">
@@ -40,21 +32,8 @@ export default function AuthorInfo({
))}
{author ? (
<div className="author-content">
{hasLink ? (
<a
href={authorlink}
className="author-name-link"
target="_blank"
rel="noopener noreferrer"
aria-label={t('widgets.quote.author_info_aria')}
>
<span className="title">{author}</span>
<HiMiniArrowUpRight className="author-arrow" />
</a>
) : (
<span className="title">{author}</span>
)}
<div className="author-content" onClick={onInfo}>
<span className="title">{author}</span>
{authorOccupation && authorOccupation !== 'Unknown' && (
<span className="subtitle">{authorOccupation}</span>
)}
@@ -66,14 +45,12 @@ export default function AuthorInfo({
</div>
)}
<div className="quote-buttons">
<QuoteButtons
onCopy={onCopy}
onFavourite={onFavourite}
onShare={onShare}
isFavourited={isFavourited}
/>
</div>
<QuoteButtons
onCopy={onCopy}
onFavourite={onFavourite}
onShare={onShare}
isFavourited={isFavourited}
/>
</div>
</div>
);

View File

@@ -31,8 +31,12 @@ export default function QuoteButtons({ onCopy, onFavourite, onShare, isFavourite
};
}, []);
if (!showCopy && !showShare && !showFavourite) {
return null;
}
return (
<>
<div className="quote-buttons">
{showCopy && (
<Tooltip title={t('widgets.quote.copy')}>
<button onClick={onCopy} aria-label={t('widgets.quote.copy')}>
@@ -65,6 +69,6 @@ export default function QuoteButtons({ onCopy, onFavourite, onShare, isFavourite
</button>
</Tooltip>
)}
</>
</div>
);
}

View File

@@ -106,6 +106,12 @@ h1.quoteauthor {
align-items: flex-start;
padding: 20px;
text-align: left;
cursor: pointer;
transition: opacity 0.2s ease;
&:hover {
opacity: 0.8;
}
.title,
.subtitle {
@@ -113,28 +119,6 @@ h1.quoteauthor {
}
}
.author-name-link {
display: flex;
align-items: center;
gap: 4px;
text-decoration: none;
color: inherit;
transition: opacity 0.2s ease;
cursor: pointer;
&:hover {
opacity: 0.8;
}
.author-arrow {
font-size: 1rem;
transition: transform 0.2s ease;
}
&:hover .author-arrow {
transform: translateX(3px);
}
}
.author-holder {
display: flex;
@@ -158,7 +142,7 @@ h1.quoteauthor {
display: flex;
align-items: center;
gap: 10px;
padding: 20px 20px 20px 0;
padding: 20px;
a {
display: flex;