diff --git a/.github/workflows/build-canary-image.yml b/.github/workflows/build-canary-image.yml index de8f5919aa94e..15212eca0fe35 100644 --- a/.github/workflows/build-canary-image.yml +++ b/.github/workflows/build-canary-image.yml @@ -8,6 +8,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.repository }} cancel-in-progress: true +permissions: + contents: read + jobs: build-frontend: runs-on: ubuntu-latest diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000000000..e6cb58c28b0a0 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,37 @@ +name: Release Please + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: write + issues: write + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - name: Check release token + env: + RELEASE_PLEASE_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN }} + run: | + if [ -z "$RELEASE_PLEASE_TOKEN" ]; then + echo "RELEASE_PLEASE_TOKEN must be set to a fine-grained PAT so release-please tags can trigger release.yml." >&2 + exit 1 + fi + + - name: Run release-please + uses: googleapis/release-please-action@v4 + with: + # Use a fine-grained PAT so generated tags trigger release.yml. + token: ${{ secrets.RELEASE_PLEASE_TOKEN }} + config-file: release-please-config.json + manifest-file: .release-please-manifest.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 298266b1d4564..88c718f27a8cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + env: GO_VERSION: "1.26.1" NODE_VERSION: "24" @@ -243,7 +246,6 @@ jobs: with: tag_name: ${{ needs.prepare.outputs.tag }} name: ${{ needs.prepare.outputs.tag }} - generate_release_notes: true prerelease: ${{ needs.prepare.outputs.is_prerelease == 'true' }} files: artifacts/* diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000000000..eb65bfe9ee34f --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.26.2" +} diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000000000..2d8f383b366e1 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "bump-minor-pre-major": true, + "packages": { + ".": { + "release-type": "go", + "package-name": "memos", + "changelog-path": "CHANGELOG.md", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "revert", + "section": "Reverts" + } + ] + } + } +} diff --git a/web/src/components/MemoMetadata/Location/LocationDialog.tsx b/web/src/components/MemoMetadata/Location/LocationDialog.tsx index bc8822e3b0f82..9b85ef873b9b8 100644 --- a/web/src/components/MemoMetadata/Location/LocationDialog.tsx +++ b/web/src/components/MemoMetadata/Location/LocationDialog.tsx @@ -35,7 +35,7 @@ export const LocationDialog = ({ return ( - + @@ -47,7 +47,7 @@ export const LocationDialog = ({
- +
diff --git a/web/src/components/map/LocationPicker.tsx b/web/src/components/map/LocationPicker.tsx index 7980a4fa82e7d..14513c03cb4da 100644 --- a/web/src/components/map/LocationPicker.tsx +++ b/web/src/components/map/LocationPicker.tsx @@ -6,26 +6,25 @@ import { MapContainer, Marker, useMap, useMapEvents } from "react-leaflet"; import { cn } from "@/lib/utils"; import { defaultMarkerIcon, ThemedTileLayer } from "./map-utils"; -interface MarkerProps { +interface LocationMarkerProps { position: LatLng | undefined; onChange: (position: LatLng) => void; readonly?: boolean; } -const LocationMarker = (props: MarkerProps) => { - const [position, setPosition] = useState(props.position); +const LocationMarker = ({ position: initialPosition, onChange, readonly: readOnly }: LocationMarkerProps) => { + const [position, setPosition] = useState(initialPosition); const initializedRef = useRef(false); const map = useMapEvents({ click(e) { - if (props.readonly) { + if (readOnly) { return; } setPosition(e.latlng); map.locate(); - // Call the parent onChange function. - props.onChange(e.latlng); + onChange(e.latlng); }, locationfound() {}, }); @@ -37,15 +36,14 @@ const LocationMarker = (props: MarkerProps) => { } }, [map]); - // Keep marker and map in sync with external position updates useEffect(() => { - if (props.position) { - setPosition(props.position); - map.setView(props.position); + if (initialPosition) { + setPosition(initialPosition); + map.setView(initialPosition); } else { setPosition(undefined); } - }, [props.position, map]); + }, [initialPosition, map]); return position === undefined ? null : ; }; @@ -197,22 +195,29 @@ const MapCleanup = () => { return null; }; -interface MapProps { +interface LocationPickerProps { readonly?: boolean; latlng?: LatLng; onChange?: (position: LatLng) => void; + className?: string; } const DEFAULT_CENTER_LAT_LNG = new LatLng(48.8584, 2.2945); +const noopOnLocationChange = () => {}; -const LeafletMap = (props: MapProps) => { - const position = props.latlng || DEFAULT_CENTER_LAT_LNG; - const statusLabel = props.readonly ? "Pinned location" : props.latlng ? "Selected location" : "Choose a location"; +const LocationPicker = ({ readonly: readOnly = false, latlng, onChange = noopOnLocationChange, className }: LocationPickerProps) => { + const position = latlng || DEFAULT_CENTER_LAT_LNG; + const statusLabel = readOnly ? "Pinned location" : latlng ? "Selected location" : "Choose a location"; return ( -
+
{ attributionControl={false} > - {}} /> - + + @@ -234,4 +239,4 @@ const LeafletMap = (props: MapProps) => { ); }; -export default LeafletMap; +export default LocationPicker; diff --git a/web/src/locales/it.json b/web/src/locales/it.json index d020a7120d08e..b00cd9d2ff5cb 100644 --- a/web/src/locales/it.json +++ b/web/src/locales/it.json @@ -28,7 +28,7 @@ "basic": "Base", "beta": "Beta", "calendar": "Calendario", - "cancel": "Cancella", + "cancel": "Annulla", "change": "Cambia", "clear": "Pulisci", "close": "Chiudi", diff --git a/web/src/utils/theme.ts b/web/src/utils/theme.ts index 156662613aa6d..a2004d2f5220b 100644 --- a/web/src/utils/theme.ts +++ b/web/src/utils/theme.ts @@ -182,6 +182,17 @@ const updateThemeColorMeta = (theme: ResolvedTheme): void => { } }; +const isDarkTheme = (theme: ResolvedTheme): boolean => { + return theme.endsWith("-dark") || theme.endsWith(".dark"); +}; + +/** + * Updates the browser native control color scheme to match the current theme. + */ +const updateColorScheme = (theme: ResolvedTheme): void => { + document.documentElement.style.colorScheme = isDarkTheme(theme) ? "dark" : "light"; +}; + // ============================================================================ // Main Theme Loading // ============================================================================ @@ -193,7 +204,8 @@ const updateThemeColorMeta = (theme: ResolvedTheme): void => { * 2. Resolves "system" to actual theme * 3. Injects theme CSS * 4. Sets data-theme attribute - * 5. Persists to localStorage + * 5. Updates browser native UI colors + * 6. Persists to localStorage */ export const loadTheme = (themeName: string): void => { const validTheme = validateTheme(themeName); @@ -202,6 +214,7 @@ export const loadTheme = (themeName: string): void => { injectThemeStyle(resolvedTheme); setThemeAttribute(resolvedTheme); updateThemeColorMeta(resolvedTheme); + updateColorScheme(resolvedTheme); setStoredTheme(validTheme); // Store original theme preference (not resolved) };