Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

DiffViewer.tsx

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

DiffViewer.tsxBlame90 lines · 1 contributor
c53cf00Claude1import React from 'react';
2import { View, Text, ScrollView, StyleSheet } from 'react-native';
3import { colors } from '../theme/colors';
4import { fontSizes, fonts } from '../theme/typography';
5
6interface DiffLine {
7 type: 'add' | 'remove' | 'context' | 'header';
8 content: string;
9 lineNo?: number;
10}
11
12interface Props {
13 diff: string;
14}
15
16function parseDiff(diff: string): DiffLine[] {
17 const lines: DiffLine[] = [];
18 for (const raw of diff.split('\n')) {
19 if (raw.startsWith('@@')) {
20 lines.push({ type: 'header', content: raw });
21 } else if (raw.startsWith('+') && !raw.startsWith('+++')) {
22 lines.push({ type: 'add', content: raw.slice(1) });
23 } else if (raw.startsWith('-') && !raw.startsWith('---')) {
24 lines.push({ type: 'remove', content: raw.slice(1) });
25 } else {
26 lines.push({ type: 'context', content: raw.startsWith(' ') ? raw.slice(1) : raw });
27 }
28 }
29 return lines;
30}
31
32function lineStyle(type: DiffLine['type']) {
33 switch (type) {
34 case 'add': return { bg: 'rgba(52,211,153,0.10)', prefix: '+', color: colors.green };
35 case 'remove': return { bg: 'rgba(248,113,113,0.10)', prefix: '-', color: colors.red };
36 case 'header': return { bg: 'rgba(140,109,255,0.10)', prefix: '', color: colors.accent };
37 default: return { bg: 'transparent', prefix: ' ', color: colors.textMuted };
38 }
39}
40
41export function DiffViewer({ diff }: Props) {
42 const lines = parseDiff(diff);
43
44 return (
45 <ScrollView horizontal style={styles.scroll} showsHorizontalScrollIndicator={false}>
46 <View style={styles.container}>
47 {lines.map((line, i) => {
48 const s = lineStyle(line.type);
49 return (
50 <View key={i} style={[styles.line, { backgroundColor: s.bg }]}>
51 <Text style={[styles.prefix, { color: s.color }]}>{s.prefix}</Text>
52 <Text style={[styles.code, { color: s.color }]}>
53 {line.content || ' '}
54 </Text>
55 </View>
56 );
57 })}
58 </View>
59 </ScrollView>
60 );
61}
62
63const styles = StyleSheet.create({
64 scroll: {
65 flex: 1,
66 backgroundColor: colors.bgSecondary,
67 borderRadius: 8,
68 },
69 container: {
70 padding: 4,
71 minWidth: '100%',
72 },
73 line: {
74 flexDirection: 'row',
75 paddingHorizontal: 4,
76 paddingVertical: 1,
77 borderRadius: 2,
78 },
79 prefix: {
80 fontSize: fontSizes.xs,
81 fontFamily: fonts.mono,
82 width: 14,
83 textAlign: 'center',
84 },
85 code: {
86 fontSize: fontSizes.xs,
87 fontFamily: fonts.mono,
88 lineHeight: 18,
89 },
90});