mirror of
https://github.com/mue/mue.git
synced 2026-06-05 23:45:53 +02:00
feat: update project documentation and configuration files; enhance permissions and layout styles
This commit is contained in:
@@ -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
7
.superset/config.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"setup": [],
|
||||
"teardown": [],
|
||||
"run": [
|
||||
"bun run dev"
|
||||
]
|
||||
}
|
||||
16
CLAUDE.md
16
CLAUDE.md
@@ -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
45
GEMINI.md
Normal 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.
|
||||
@@ -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" />
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@@ -614,6 +614,7 @@
|
||||
|
||||
.location-map-section {
|
||||
width: 100%;
|
||||
grid-column: 1 / -1;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user