mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-23 18:17:44 +02:00
Bundles the markdown rendering overhaul plus in-flight mobile feature
work as a single WIP for review.
Markdown work (the new direction):
- Swap internal Markdown component from hand-rolled marked walker to
react-native-enriched-markdown (Software Mansion, native md4c).
Public API <Markdown content={...} /> unchanged; consumers untouched.
Mention links degrade to colored links + onLinkPress routing.
- Pre-swap fixes that landed first: 3-layer inline code (later corrected),
Shiki via react-native-shiki-engine wired (now bypassed; code retained
for selective re-enable on code blocks), code block copy button with
expo-clipboard + expo-haptics, inline SVG copy/check icons, header
scale calibrated to Apple HIG, paragraph leading-6 for CJK, list
bullet column 24->16, lineBreakStrategyIOS="hangul-word" on outer
paragraph Text.
- Preprocess: <br> -> " \n" (CommonMark HardBreak) so md4c respects
intentional breaks without misreading bare \n.
- Drop the Expo Go compatibility constraint from CLAUDE.md and
markdown-renderer-research.md (project runs on dev client).
- New apps/mobile/docs/markdown-renderer-research.md captures the
RN nested-Text rendering constraints (#10775 / #45925 / #6728), the
CJK amplification mechanism, the typography scale calibration, and
every decision-log entry from the engine evolution.
Other in-flight mobile features included:
- Issue detail timeline polish, comment composer + action sheet,
mention suggestion bar, emoji picker sheet, reaction bar.
- Status / priority / assignee / label / due date picker sheets.
- My Issues filter sheet + view store.
- Realtime layer (ws-client, realtime-provider, use-inbox-realtime).
- Data layer additions (queries, mutations, schemas, attribute chips).
Cross-package:
- packages/core/api/schemas.ts: export IssueSchema for mobile use.
Build: native rebuild required after pulling (enriched-markdown is
a native Fabric module).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
75 lines
2.7 KiB
TypeScript
75 lines
2.7 KiB
TypeScript
import "../global.css";
|
|
|
|
import { useEffect, useRef } from "react";
|
|
import { Stack, router } from "expo-router";
|
|
import { StatusBar } from "expo-status-bar";
|
|
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
|
import { QueryClientProvider, useQueryClient } from "@tanstack/react-query";
|
|
import { api } from "@/data/api";
|
|
import { queryClient } from "@/data/query-client";
|
|
import { useAuthStore } from "@/data/auth-store";
|
|
import { useWorkspaceStore } from "@/data/workspace-store";
|
|
import { LightboxProvider, prewarmHighlighter } from "@/lib/markdown";
|
|
|
|
// Kick off Shiki highlighter init at module load — fires once per process,
|
|
// finishes before the user navigates to any screen with a code block. If
|
|
// init fails (engine unavailable) the highlighter falls back to plain
|
|
// text; nothing here is allowed to throw.
|
|
prewarmHighlighter();
|
|
|
|
function AuthInitializer({ children }: { children: React.ReactNode }) {
|
|
const initialize = useAuthStore((s) => s.initialize);
|
|
const qc = useQueryClient();
|
|
// Idempotent guard: 401 on multiple in-flight requests would otherwise
|
|
// logout/navigate repeatedly during the same session-expire moment.
|
|
const signingOutRef = useRef(false);
|
|
|
|
useEffect(() => {
|
|
// Wire 401 handling onto the shared ApiClient singleton. Must be set
|
|
// before any request fires — initialize() below kicks off the first
|
|
// getMe() call, so do this synchronously first.
|
|
api.setOptions({
|
|
onUnauthorized: () => {
|
|
if (signingOutRef.current) return;
|
|
signingOutRef.current = true;
|
|
void (async () => {
|
|
await useAuthStore.getState().logout();
|
|
await useWorkspaceStore.getState().clear();
|
|
qc.clear();
|
|
router.replace("/login");
|
|
// Reset on next tick so a fresh session can hit 401 again later
|
|
// without being silently swallowed.
|
|
setTimeout(() => {
|
|
signingOutRef.current = false;
|
|
}, 0);
|
|
})();
|
|
},
|
|
});
|
|
initialize();
|
|
}, [initialize, qc]);
|
|
|
|
return <>{children}</>;
|
|
}
|
|
|
|
export default function RootLayout() {
|
|
return (
|
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
<SafeAreaProvider>
|
|
<QueryClientProvider client={queryClient}>
|
|
<AuthInitializer>
|
|
<LightboxProvider>
|
|
<StatusBar style="auto" />
|
|
<Stack screenOptions={{ headerShown: false }}>
|
|
<Stack.Screen name="index" />
|
|
<Stack.Screen name="(auth)" />
|
|
<Stack.Screen name="(app)" />
|
|
</Stack>
|
|
</LightboxProvider>
|
|
</AuthInitializer>
|
|
</QueryClientProvider>
|
|
</SafeAreaProvider>
|
|
</GestureHandlerRootView>
|
|
);
|
|
}
|