claudecode-omc 5.6.6 → 5.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.local/skills/THIRD_PARTY_LICENSES/AvdLee-SwiftUI-Agent-Skill.LICENSE +21 -0
- package/.local/skills/THIRD_PARTY_LICENSES/Dimillian-Skills.LICENSE +21 -0
- package/.local/skills/THIRD_PARTY_LICENSES/README.md +36 -0
- package/.local/skills/THIRD_PARTY_LICENSES/twostraws-swiftui-agent-skill.LICENSE +21 -0
- package/.local/skills/h5-to-swiftui/SKILL.md +201 -0
- package/.local/skills/h5-to-swiftui/assets/calibration/README.md +176 -0
- package/.local/skills/h5-to-swiftui/assets/calibration/h5-twin/index.html +52 -0
- package/.local/skills/h5-to-swiftui/assets/calibration/h5-twin/style.css +133 -0
- package/.local/skills/h5-to-swiftui/assets/calibration/swiftui-twin/Package.swift +26 -0
- package/.local/skills/h5-to-swiftui/assets/calibration/swiftui-twin/Sources/CalibrationScreen/CalibrationScreen.swift +142 -0
- package/.local/skills/h5-to-swiftui/assets/calibration/swiftui-twin-divergent/Package.swift +32 -0
- package/.local/skills/h5-to-swiftui/assets/calibration/swiftui-twin-divergent/Sources/CalibrationScreenDivergent/CalibrationScreenDivergent.swift +122 -0
- package/.local/skills/h5-to-swiftui/assets/calibration/tokens.json +42 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/index.html +14 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/package.json +20 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/public/api/articles/001.json +96 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/public/api/articles/index.json +89 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/App.jsx +22 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/App.module.css +11 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/components/ArticleCard.jsx +53 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/components/ArticleCard.module.css +139 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/components/NavBar.jsx +37 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/components/NavBar.module.css +72 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/components/TagCloud.jsx +30 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/components/TagCloud.module.css +50 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/components/TrendChart.jsx +159 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/components/TrendChart.module.css +21 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/main.jsx +12 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/screens/ArticleScreen.jsx +182 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/screens/ArticleScreen.module.css +294 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/screens/FeedScreen.jsx +147 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/screens/FeedScreen.module.css +161 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/styles/global.css +50 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/styles/tokens.css +103 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-react/vite.config.js +6 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-vanilla/data/tasks.js +67 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-vanilla/index.html +26 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-vanilla/router.js +73 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-vanilla/screens/detail.js +164 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-vanilla/screens/home.js +53 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-vanilla/screens/list.js +87 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-vanilla/styles/app.css +342 -0
- package/.local/skills/h5-to-swiftui/assets/sample-h5-vanilla/styles/tokens.css +68 -0
- package/.local/skills/h5-to-swiftui/references/css-to-swiftui-map.md +205 -0
- package/.local/skills/h5-to-swiftui/references/design-token-extraction.md +209 -0
- package/.local/skills/h5-to-swiftui/references/high-risk-triage.md +209 -0
- package/.local/skills/h5-to-swiftui/references/render-equivalence-calibration.md +193 -0
- package/.local/skills/h5-to-swiftui/references/stack-detection.md +160 -0
- package/.local/skills/h5-to-swiftui/references/visual-diff-loop-protocol.md +365 -0
- package/.local/skills/h5-to-swiftui/scripts/_calib-consts.mjs +150 -0
- package/.local/skills/h5-to-swiftui/scripts/_imglib.mjs +547 -0
- package/.local/skills/h5-to-swiftui/scripts/_provenance.mjs +123 -0
- package/.local/skills/h5-to-swiftui/scripts/calibrate-render.mjs +625 -0
- package/.local/skills/h5-to-swiftui/scripts/capture-reference.mjs +386 -0
- package/.local/skills/h5-to-swiftui/scripts/detect-stack.mjs +305 -0
- package/.local/skills/h5-to-swiftui/scripts/evaluate-convergence.mjs +1093 -0
- package/.local/skills/h5-to-swiftui/scripts/extract-tokens.mjs +600 -0
- package/.local/skills/h5-to-swiftui/scripts/mark-overlay.mjs +379 -0
- package/.local/skills/h5-to-swiftui/scripts/pixel-diff.mjs +530 -0
- package/.local/skills/h5-to-swiftui/scripts/sim-screenshot.sh +544 -0
- package/.local/skills/ios-debugger-agent/SKILL.md +51 -0
- package/.local/skills/ios-debugger-agent/agents/openai.yaml +4 -0
- package/.local/skills/swift-concurrency-expert/SKILL.md +105 -0
- package/.local/skills/swift-concurrency-expert/agents/openai.yaml +4 -0
- package/.local/skills/swift-concurrency-expert/references/approachable-concurrency.md +63 -0
- package/.local/skills/swift-concurrency-expert/references/swift-6-2-concurrency.md +272 -0
- package/.local/skills/swift-concurrency-expert/references/swiftui-concurrency-tour-wwdc.md +33 -0
- package/.local/skills/swiftui-expert-skill/SKILL.md +162 -0
- package/.local/skills/swiftui-expert-skill/references/accessibility-patterns.md +215 -0
- package/.local/skills/swiftui-expert-skill/references/animation-advanced.md +403 -0
- package/.local/skills/swiftui-expert-skill/references/animation-basics.md +284 -0
- package/.local/skills/swiftui-expert-skill/references/animation-transitions.md +326 -0
- package/.local/skills/swiftui-expert-skill/references/charts-accessibility.md +135 -0
- package/.local/skills/swiftui-expert-skill/references/charts.md +602 -0
- package/.local/skills/swiftui-expert-skill/references/focus-patterns.md +299 -0
- package/.local/skills/swiftui-expert-skill/references/image-optimization.md +203 -0
- package/.local/skills/swiftui-expert-skill/references/latest-apis.md +488 -0
- package/.local/skills/swiftui-expert-skill/references/layout-best-practices.md +266 -0
- package/.local/skills/swiftui-expert-skill/references/liquid-glass.md +423 -0
- package/.local/skills/swiftui-expert-skill/references/list-patterns.md +446 -0
- package/.local/skills/swiftui-expert-skill/references/macos-scenes.md +318 -0
- package/.local/skills/swiftui-expert-skill/references/macos-views.md +357 -0
- package/.local/skills/swiftui-expert-skill/references/macos-window-styling.md +303 -0
- package/.local/skills/swiftui-expert-skill/references/performance-patterns.md +403 -0
- package/.local/skills/swiftui-expert-skill/references/scroll-patterns.md +293 -0
- package/.local/skills/swiftui-expert-skill/references/sheet-navigation-patterns.md +363 -0
- package/.local/skills/swiftui-expert-skill/references/state-management.md +388 -0
- package/.local/skills/swiftui-expert-skill/references/text-patterns.md +32 -0
- package/.local/skills/swiftui-expert-skill/references/trace-analysis.md +295 -0
- package/.local/skills/swiftui-expert-skill/references/trace-recording.md +134 -0
- package/.local/skills/swiftui-expert-skill/references/view-structure.md +780 -0
- package/.local/skills/swiftui-expert-skill/scripts/__pycache__/analyze_trace.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/__pycache__/record_trace.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/analyze_trace.py +301 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__init__.py +1 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/__init__.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/causes.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/correlate.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/events.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/hangs.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/hitches.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/summary.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/swiftui.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/time_profiler.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/xctrace.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/xml_utils.cpython-313.pyc +0 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/causes.py +187 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/correlate.py +179 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/events.py +291 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/hangs.py +108 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/hitches.py +145 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/summary.py +243 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/swiftui.py +195 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/time_profiler.py +135 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/xctrace.py +117 -0
- package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/xml_utils.py +224 -0
- package/.local/skills/swiftui-expert-skill/scripts/record_trace.py +252 -0
- package/.local/skills/swiftui-liquid-glass/SKILL.md +90 -0
- package/.local/skills/swiftui-liquid-glass/agents/openai.yaml +4 -0
- package/.local/skills/swiftui-liquid-glass/references/liquid-glass.md +280 -0
- package/.local/skills/swiftui-performance-audit/SKILL.md +106 -0
- package/.local/skills/swiftui-performance-audit/agents/openai.yaml +4 -0
- package/.local/skills/swiftui-performance-audit/references/code-smells.md +150 -0
- package/.local/skills/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/.local/skills/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/.local/skills/swiftui-performance-audit/references/profiling-intake.md +44 -0
- package/.local/skills/swiftui-performance-audit/references/report-template.md +47 -0
- package/.local/skills/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
- package/.local/skills/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
- package/.local/skills/swiftui-pro/SKILL.md +108 -0
- package/.local/skills/swiftui-pro/agents/openai.yaml +10 -0
- package/.local/skills/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
- package/.local/skills/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
- package/.local/skills/swiftui-pro/references/accessibility.md +13 -0
- package/.local/skills/swiftui-pro/references/api.md +39 -0
- package/.local/skills/swiftui-pro/references/data.md +43 -0
- package/.local/skills/swiftui-pro/references/design.md +32 -0
- package/.local/skills/swiftui-pro/references/hygiene.md +9 -0
- package/.local/skills/swiftui-pro/references/navigation.md +14 -0
- package/.local/skills/swiftui-pro/references/performance.md +46 -0
- package/.local/skills/swiftui-pro/references/swift.md +56 -0
- package/.local/skills/swiftui-pro/references/views.md +36 -0
- package/.local/skills/swiftui-ui-patterns/SKILL.md +95 -0
- package/.local/skills/swiftui-ui-patterns/agents/openai.yaml +4 -0
- package/.local/skills/swiftui-ui-patterns/references/app-wiring.md +201 -0
- package/.local/skills/swiftui-ui-patterns/references/async-state.md +96 -0
- package/.local/skills/swiftui-ui-patterns/references/components-index.md +50 -0
- package/.local/skills/swiftui-ui-patterns/references/controls.md +57 -0
- package/.local/skills/swiftui-ui-patterns/references/deeplinks.md +66 -0
- package/.local/skills/swiftui-ui-patterns/references/focus.md +90 -0
- package/.local/skills/swiftui-ui-patterns/references/form.md +97 -0
- package/.local/skills/swiftui-ui-patterns/references/grids.md +71 -0
- package/.local/skills/swiftui-ui-patterns/references/haptics.md +71 -0
- package/.local/skills/swiftui-ui-patterns/references/input-toolbar.md +51 -0
- package/.local/skills/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
- package/.local/skills/swiftui-ui-patterns/references/list.md +86 -0
- package/.local/skills/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
- package/.local/skills/swiftui-ui-patterns/references/macos-settings.md +71 -0
- package/.local/skills/swiftui-ui-patterns/references/matched-transitions.md +59 -0
- package/.local/skills/swiftui-ui-patterns/references/media.md +73 -0
- package/.local/skills/swiftui-ui-patterns/references/menu-bar.md +101 -0
- package/.local/skills/swiftui-ui-patterns/references/navigationstack.md +159 -0
- package/.local/skills/swiftui-ui-patterns/references/overlay.md +45 -0
- package/.local/skills/swiftui-ui-patterns/references/performance.md +62 -0
- package/.local/skills/swiftui-ui-patterns/references/previews.md +48 -0
- package/.local/skills/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
- package/.local/skills/swiftui-ui-patterns/references/scrollview.md +87 -0
- package/.local/skills/swiftui-ui-patterns/references/searchable.md +71 -0
- package/.local/skills/swiftui-ui-patterns/references/sheets.md +155 -0
- package/.local/skills/swiftui-ui-patterns/references/split-views.md +72 -0
- package/.local/skills/swiftui-ui-patterns/references/tabview.md +114 -0
- package/.local/skills/swiftui-ui-patterns/references/theming.md +71 -0
- package/.local/skills/swiftui-ui-patterns/references/title-menus.md +93 -0
- package/.local/skills/swiftui-ui-patterns/references/top-bar.md +49 -0
- package/.local/skills/swiftui-view-refactor/SKILL.md +202 -0
- package/.local/skills/swiftui-view-refactor/agents/openai.yaml +4 -0
- package/.local/skills/swiftui-view-refactor/references/mv-patterns.md +161 -0
- package/bundled/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
import styles from './ArticleCard.module.css';
|
|
4
|
+
|
|
5
|
+
export default function ArticleCard({ article }) {
|
|
6
|
+
const navigate = useNavigate();
|
|
7
|
+
const { id, title, summary, author, publishedAt, readingTime, tag, imageAlt } = article;
|
|
8
|
+
|
|
9
|
+
const formattedDate = new Date(publishedAt).toLocaleDateString('en-US', {
|
|
10
|
+
month: 'short',
|
|
11
|
+
day: 'numeric',
|
|
12
|
+
year: 'numeric',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<article
|
|
17
|
+
className={styles.card}
|
|
18
|
+
onClick={() => navigate(`/article/${id}`)}
|
|
19
|
+
role="button"
|
|
20
|
+
tabIndex={0}
|
|
21
|
+
onKeyDown={e => e.key === 'Enter' && navigate(`/article/${id}`)}
|
|
22
|
+
>
|
|
23
|
+
<div className={styles.imagePlaceholder} aria-label={imageAlt}>
|
|
24
|
+
<span className={styles.imagePlaceholderText}>{imageAlt}</span>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div className={styles.body}>
|
|
28
|
+
<div className={styles.meta}>
|
|
29
|
+
<span className={styles.tag}>{tag}</span>
|
|
30
|
+
<span className={styles.readingTime}>{readingTime} min read</span>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<h2 className={styles.title}>{title}</h2>
|
|
34
|
+
<p className={styles.summary}>{summary}</p>
|
|
35
|
+
|
|
36
|
+
<div className={styles.footer}>
|
|
37
|
+
<div className={styles.authorArea}>
|
|
38
|
+
<div className={styles.avatar} aria-hidden="true">
|
|
39
|
+
{author.name.slice(0, 1)}
|
|
40
|
+
</div>
|
|
41
|
+
<div className={styles.authorInfo}>
|
|
42
|
+
<span className={styles.authorName}>{author.name}</span>
|
|
43
|
+
<span className={styles.authorRole}>{author.role}</span>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
<time className={styles.date} dateTime={publishedAt}>
|
|
47
|
+
{formattedDate}
|
|
48
|
+
</time>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</article>
|
|
52
|
+
);
|
|
53
|
+
}
|
package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/components/ArticleCard.module.css
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
.card {
|
|
2
|
+
background-color: var(--color-bg-card);
|
|
3
|
+
border-radius: var(--radius-lg);
|
|
4
|
+
box-shadow: var(--shadow-card);
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
cursor: pointer;
|
|
7
|
+
transition: box-shadow var(--duration-normal) var(--easing-default),
|
|
8
|
+
transform var(--duration-normal) var(--easing-default);
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.card:hover {
|
|
14
|
+
box-shadow: var(--shadow-elevated);
|
|
15
|
+
transform: translateY(-2px);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.card:focus-visible {
|
|
19
|
+
outline: 2px solid var(--color-accent);
|
|
20
|
+
outline-offset: 2px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.imagePlaceholder {
|
|
24
|
+
width: 100%;
|
|
25
|
+
aspect-ratio: 16 / 9;
|
|
26
|
+
background-color: var(--color-bg-secondary);
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
border-bottom: 1px solid var(--color-divider);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.imagePlaceholderText {
|
|
35
|
+
font-size: var(--font-size-xs);
|
|
36
|
+
color: var(--color-text-caption);
|
|
37
|
+
text-align: center;
|
|
38
|
+
padding: var(--space-2);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.body {
|
|
42
|
+
padding: var(--space-5);
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
gap: var(--space-3);
|
|
46
|
+
flex-grow: 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.meta {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: row;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: space-between;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tag {
|
|
57
|
+
font-size: var(--font-size-xs);
|
|
58
|
+
font-weight: var(--font-weight-semibold);
|
|
59
|
+
color: var(--color-text-accent);
|
|
60
|
+
text-transform: uppercase;
|
|
61
|
+
letter-spacing: 0.6px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.readingTime {
|
|
65
|
+
font-size: var(--font-size-xs);
|
|
66
|
+
color: var(--color-text-caption);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.title {
|
|
70
|
+
font-family: var(--font-family-display);
|
|
71
|
+
font-size: var(--font-size-subhead);
|
|
72
|
+
font-weight: var(--font-weight-bold);
|
|
73
|
+
color: var(--color-text-primary);
|
|
74
|
+
line-height: var(--line-height-tight);
|
|
75
|
+
letter-spacing: -0.3px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.summary {
|
|
79
|
+
font-size: var(--font-size-body);
|
|
80
|
+
color: var(--color-text-secondary);
|
|
81
|
+
line-height: var(--line-height-normal);
|
|
82
|
+
display: -webkit-box;
|
|
83
|
+
-webkit-line-clamp: 3;
|
|
84
|
+
-webkit-box-orient: vertical;
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.footer {
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-direction: row;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
margin-top: auto;
|
|
94
|
+
padding-top: var(--space-3);
|
|
95
|
+
border-top: 1px solid var(--color-divider);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.authorArea {
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: row;
|
|
101
|
+
align-items: center;
|
|
102
|
+
gap: var(--space-2);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.avatar {
|
|
106
|
+
width: 32px;
|
|
107
|
+
height: 32px;
|
|
108
|
+
border-radius: var(--radius-full);
|
|
109
|
+
background-color: var(--color-accent);
|
|
110
|
+
color: var(--color-text-on-accent);
|
|
111
|
+
font-size: var(--font-size-sm);
|
|
112
|
+
font-weight: var(--font-weight-bold);
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: center;
|
|
116
|
+
flex-shrink: 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.authorInfo {
|
|
120
|
+
display: flex;
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
gap: 1px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.authorName {
|
|
126
|
+
font-size: var(--font-size-sm);
|
|
127
|
+
font-weight: var(--font-weight-semibold);
|
|
128
|
+
color: var(--color-text-primary);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.authorRole {
|
|
132
|
+
font-size: var(--font-size-xs);
|
|
133
|
+
color: var(--color-text-caption);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.date {
|
|
137
|
+
font-size: var(--font-size-xs);
|
|
138
|
+
color: var(--color-text-caption);
|
|
139
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useNavigate, useLocation } from 'react-router-dom';
|
|
3
|
+
import styles from './NavBar.module.css';
|
|
4
|
+
|
|
5
|
+
const TABS = [
|
|
6
|
+
{ id: 'feed', label: 'Feed', path: '/' },
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
export default function NavBar({ activeTab, onTabChange }) {
|
|
10
|
+
const navigate = useNavigate();
|
|
11
|
+
const location = useLocation();
|
|
12
|
+
|
|
13
|
+
function handleTab(tab) {
|
|
14
|
+
onTabChange(tab.id);
|
|
15
|
+
navigate(tab.path);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<header className={styles.navbar}>
|
|
20
|
+
<div className={styles.logoArea}>
|
|
21
|
+
<span className={styles.logoMark}>C</span>
|
|
22
|
+
<span className={styles.logoText}>Chronicle</span>
|
|
23
|
+
</div>
|
|
24
|
+
<nav className={styles.tabs}>
|
|
25
|
+
{TABS.map(tab => (
|
|
26
|
+
<button
|
|
27
|
+
key={tab.id}
|
|
28
|
+
className={`${styles.tab} ${location.pathname === tab.path ? styles.tabActive : ''}`}
|
|
29
|
+
onClick={() => handleTab(tab)}
|
|
30
|
+
>
|
|
31
|
+
{tab.label}
|
|
32
|
+
</button>
|
|
33
|
+
))}
|
|
34
|
+
</nav>
|
|
35
|
+
</header>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
.navbar {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: row;
|
|
4
|
+
align-items: center;
|
|
5
|
+
justify-content: space-between;
|
|
6
|
+
padding: var(--space-3) var(--space-6);
|
|
7
|
+
background-color: var(--color-bg-primary);
|
|
8
|
+
border-bottom: 1px solid var(--color-border);
|
|
9
|
+
position: sticky;
|
|
10
|
+
top: 0;
|
|
11
|
+
z-index: 100;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.logoArea {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: row;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: var(--space-2);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.logoMark {
|
|
22
|
+
width: 28px;
|
|
23
|
+
height: 28px;
|
|
24
|
+
border-radius: var(--radius-sm);
|
|
25
|
+
background-color: var(--color-accent);
|
|
26
|
+
color: var(--color-text-on-accent);
|
|
27
|
+
font-family: var(--font-family-display);
|
|
28
|
+
font-size: var(--font-size-sm);
|
|
29
|
+
font-weight: var(--font-weight-bold);
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.logoText {
|
|
36
|
+
font-family: var(--font-family-display);
|
|
37
|
+
font-size: var(--font-size-headline);
|
|
38
|
+
font-weight: var(--font-weight-semibold);
|
|
39
|
+
color: var(--color-text-primary);
|
|
40
|
+
letter-spacing: -0.5px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.tabs {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: row;
|
|
46
|
+
gap: var(--space-1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.tab {
|
|
50
|
+
padding: var(--space-2) var(--space-4);
|
|
51
|
+
border-radius: var(--radius-full);
|
|
52
|
+
font-size: var(--font-size-sm);
|
|
53
|
+
font-weight: var(--font-weight-medium);
|
|
54
|
+
color: var(--color-text-secondary);
|
|
55
|
+
transition: background-color var(--duration-fast) var(--easing-default),
|
|
56
|
+
color var(--duration-fast) var(--easing-default);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.tab:hover {
|
|
60
|
+
background-color: var(--color-bg-secondary);
|
|
61
|
+
color: var(--color-text-primary);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.tabActive {
|
|
65
|
+
background-color: var(--color-accent);
|
|
66
|
+
color: var(--color-text-on-accent);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.tabActive:hover {
|
|
70
|
+
background-color: var(--color-accent-hover);
|
|
71
|
+
color: var(--color-text-on-accent);
|
|
72
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './TagCloud.module.css';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TagCloud — a flex-wrap tag gallery with non-uniform item widths.
|
|
6
|
+
*
|
|
7
|
+
* CSS uses `flex-wrap: wrap` with variable-length tag labels.
|
|
8
|
+
* This pattern has NO stock SwiftUI equivalent:
|
|
9
|
+
* "flex-wrap: wrap → HStack never wraps; LazyHGrid/LazyVGrid are scroll
|
|
10
|
+
* containers, not inline wrapping layouts" (css-to-swiftui-map.md)
|
|
11
|
+
*
|
|
12
|
+
* CUSTOM-LAYOUT: flex-wrap:wrap — the variable-width tags must reflow onto
|
|
13
|
+
* multiple rows at arbitrary breakpoints determined by available width, which
|
|
14
|
+
* HStack cannot do. SwiftUI port requires the FlowLayout custom Layout protocol.
|
|
15
|
+
*/
|
|
16
|
+
export default function TagCloud({ tags, onTagClick, selectedTag }) {
|
|
17
|
+
return (
|
|
18
|
+
<div className={styles.cloud}>
|
|
19
|
+
{tags.map(tag => (
|
|
20
|
+
<button
|
|
21
|
+
key={tag}
|
|
22
|
+
className={`${styles.tag} ${selectedTag === tag ? styles.tagSelected : ''}`}
|
|
23
|
+
onClick={() => onTagClick(tag === selectedTag ? null : tag)}
|
|
24
|
+
>
|
|
25
|
+
{tag}
|
|
26
|
+
</button>
|
|
27
|
+
))}
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/* CUSTOM-LAYOUT: flex-wrap:wrap
|
|
2
|
+
*
|
|
3
|
+
* flex-wrap: wrap causes tags with non-uniform widths to reflow onto multiple
|
|
4
|
+
* rows. The break points are data-driven (label length) and width-driven
|
|
5
|
+
* (viewport). HStack in SwiftUI never wraps — this MUST use a custom Layout
|
|
6
|
+
* (FlowLayout) implementing the Layout protocol (iOS 16+).
|
|
7
|
+
*
|
|
8
|
+
* Trigger quote from css-to-swiftui-map.md:
|
|
9
|
+
* "flex-wrap: wrap → Custom Layout protocol (iOS 16+) — FlowLayout
|
|
10
|
+
* No LazyHGrid/LazyVGrid equivalent for true wrapping"
|
|
11
|
+
*/
|
|
12
|
+
.cloud {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: row;
|
|
15
|
+
flex-wrap: wrap; /* CUSTOM-LAYOUT: flex-wrap:wrap */
|
|
16
|
+
gap: var(--space-2) var(--space-2);
|
|
17
|
+
padding: var(--space-4) 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.tag {
|
|
21
|
+
display: inline-flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
padding: var(--space-1) var(--space-3);
|
|
24
|
+
border-radius: var(--radius-full);
|
|
25
|
+
background-color: var(--color-bg-tag);
|
|
26
|
+
color: var(--color-text-accent);
|
|
27
|
+
font-size: var(--font-size-sm);
|
|
28
|
+
font-weight: var(--font-weight-medium);
|
|
29
|
+
white-space: nowrap;
|
|
30
|
+
border: 1px solid transparent;
|
|
31
|
+
transition: background-color var(--duration-fast) var(--easing-default),
|
|
32
|
+
border-color var(--duration-fast) var(--easing-default);
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.tag:hover {
|
|
37
|
+
background-color: var(--color-bg-secondary);
|
|
38
|
+
border-color: var(--color-border);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.tagSelected {
|
|
42
|
+
background-color: var(--color-accent);
|
|
43
|
+
color: var(--color-text-on-accent);
|
|
44
|
+
border-color: var(--color-accent);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.tagSelected:hover {
|
|
48
|
+
background-color: var(--color-accent-hover);
|
|
49
|
+
border-color: var(--color-accent-hover);
|
|
50
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import React, { useRef, useEffect } from 'react';
|
|
2
|
+
import styles from './TrendChart.module.css';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TrendChart — WebGL sparkline chart rendered on a <canvas> element.
|
|
6
|
+
*
|
|
7
|
+
* {/* TIER3: WebGL canvas — uses WebGLRenderingContext to draw a sparkline.
|
|
8
|
+
* The rendering pipeline is entirely GPU-driven via custom GLSL shaders.
|
|
9
|
+
* There is NO SwiftUI equivalent for arbitrary WebGL content.
|
|
10
|
+
* SwiftUI port options:
|
|
11
|
+
* (a) Replace with a native Swift Charts (iOS 16+) line mark — preferred
|
|
12
|
+
* if data semantics are equivalent.
|
|
13
|
+
* (b) Wrap in WKWebView if exact visual must be preserved (accepts
|
|
14
|
+
* partial fidelity loss: no ProMotion, extra process, no Dynamic Type).
|
|
15
|
+
* Risk triage MUST flag this surface and NOT silently generate wrong code.
|
|
16
|
+
* */
|
|
17
|
+
export default function TrendChart({ data, label, color = '#0071e3' }) {
|
|
18
|
+
const canvasRef = useRef(null);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const canvas = canvasRef.current;
|
|
22
|
+
if (!canvas) return;
|
|
23
|
+
|
|
24
|
+
// Attempt WebGL; fall back to 2D canvas if unavailable (e.g. jsdom in tests)
|
|
25
|
+
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
|
26
|
+
|
|
27
|
+
if (!gl) {
|
|
28
|
+
// Graceful 2D fallback (no WebGL in test env)
|
|
29
|
+
const ctx = canvas.getContext('2d');
|
|
30
|
+
if (!ctx || !data?.length) return;
|
|
31
|
+
drawFallback2D(ctx, canvas, data, color);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// WebGL sparkline — minimal shader pair
|
|
36
|
+
const vsSource = `
|
|
37
|
+
attribute vec2 a_position;
|
|
38
|
+
uniform vec2 u_resolution;
|
|
39
|
+
void main() {
|
|
40
|
+
vec2 clip = (a_position / u_resolution) * 2.0 - 1.0;
|
|
41
|
+
gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const fsSource = `
|
|
46
|
+
precision mediump float;
|
|
47
|
+
uniform vec4 u_color;
|
|
48
|
+
void main() {
|
|
49
|
+
gl_FragColor = u_color;
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const vs = compileShader(gl, gl.VERTEX_SHADER, vsSource);
|
|
54
|
+
const fs = compileShader(gl, gl.FRAGMENT_SHADER, fsSource);
|
|
55
|
+
if (!vs || !fs) return;
|
|
56
|
+
|
|
57
|
+
const program = gl.createProgram();
|
|
58
|
+
gl.attachShader(program, vs);
|
|
59
|
+
gl.attachShader(program, fs);
|
|
60
|
+
gl.linkProgram(program);
|
|
61
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) return;
|
|
62
|
+
|
|
63
|
+
gl.useProgram(program);
|
|
64
|
+
|
|
65
|
+
// Map data to canvas coordinates
|
|
66
|
+
const w = canvas.width;
|
|
67
|
+
const h = canvas.height;
|
|
68
|
+
const min = Math.min(...data);
|
|
69
|
+
const max = Math.max(...data);
|
|
70
|
+
const range = max - min || 1;
|
|
71
|
+
const pad = 8;
|
|
72
|
+
|
|
73
|
+
const vertices = new Float32Array(data.length * 2);
|
|
74
|
+
data.forEach((v, i) => {
|
|
75
|
+
vertices[i * 2] = pad + (i / (data.length - 1)) * (w - pad * 2);
|
|
76
|
+
vertices[i * 2 + 1] = h - pad - ((v - min) / range) * (h - pad * 2);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const buf = gl.createBuffer();
|
|
80
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
|
81
|
+
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
|
82
|
+
|
|
83
|
+
const posLoc = gl.getAttribLocation(program, 'a_position');
|
|
84
|
+
gl.enableVertexAttribArray(posLoc);
|
|
85
|
+
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
86
|
+
|
|
87
|
+
const resLoc = gl.getUniformLocation(program, 'u_resolution');
|
|
88
|
+
gl.uniform2f(resLoc, w, h);
|
|
89
|
+
|
|
90
|
+
const colLoc = gl.getUniformLocation(program, 'u_color');
|
|
91
|
+
const [r, g, b] = hexToRgb(color);
|
|
92
|
+
gl.uniform4f(colLoc, r, g, b, 1.0);
|
|
93
|
+
|
|
94
|
+
gl.viewport(0, 0, w, h);
|
|
95
|
+
gl.clearColor(0, 0, 0, 0);
|
|
96
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
97
|
+
gl.lineWidth(2);
|
|
98
|
+
gl.drawArrays(gl.LINE_STRIP, 0, data.length);
|
|
99
|
+
|
|
100
|
+
return () => {
|
|
101
|
+
gl.deleteProgram(program);
|
|
102
|
+
gl.deleteBuffer(buf);
|
|
103
|
+
};
|
|
104
|
+
}, [data, color]);
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<div className={styles.chartWrapper}>
|
|
108
|
+
{label && <span className={styles.chartLabel}>{label}</span>}
|
|
109
|
+
<canvas
|
|
110
|
+
ref={canvasRef}
|
|
111
|
+
width={280}
|
|
112
|
+
height={80}
|
|
113
|
+
className={styles.canvas}
|
|
114
|
+
aria-label={`Trend chart: ${label}`}
|
|
115
|
+
role="img"
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
function compileShader(gl, type, source) {
|
|
124
|
+
const shader = gl.createShader(type);
|
|
125
|
+
gl.shaderSource(shader, source);
|
|
126
|
+
gl.compileShader(shader);
|
|
127
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
128
|
+
gl.deleteShader(shader);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
return shader;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function hexToRgb(hex) {
|
|
135
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
136
|
+
return result
|
|
137
|
+
? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255]
|
|
138
|
+
: [0, 0.44, 0.89];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function drawFallback2D(ctx, canvas, data, color) {
|
|
142
|
+
const w = canvas.width;
|
|
143
|
+
const h = canvas.height;
|
|
144
|
+
const min = Math.min(...data);
|
|
145
|
+
const max = Math.max(...data);
|
|
146
|
+
const range = max - min || 1;
|
|
147
|
+
const pad = 8;
|
|
148
|
+
|
|
149
|
+
ctx.clearRect(0, 0, w, h);
|
|
150
|
+
ctx.beginPath();
|
|
151
|
+
ctx.strokeStyle = color;
|
|
152
|
+
ctx.lineWidth = 2;
|
|
153
|
+
data.forEach((v, i) => {
|
|
154
|
+
const x = pad + (i / (data.length - 1)) * (w - pad * 2);
|
|
155
|
+
const y = h - pad - ((v - min) / range) * (h - pad * 2);
|
|
156
|
+
i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
|
|
157
|
+
});
|
|
158
|
+
ctx.stroke();
|
|
159
|
+
}
|
package/.local/skills/h5-to-swiftui/assets/sample-h5-react/src/components/TrendChart.module.css
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.chartWrapper {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--space-2);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.chartLabel {
|
|
8
|
+
font-size: var(--font-size-xs);
|
|
9
|
+
font-weight: var(--font-weight-semibold);
|
|
10
|
+
color: var(--color-text-secondary);
|
|
11
|
+
text-transform: uppercase;
|
|
12
|
+
letter-spacing: 0.5px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.canvas {
|
|
16
|
+
display: block;
|
|
17
|
+
border-radius: var(--radius-sm);
|
|
18
|
+
background-color: var(--color-bg-secondary);
|
|
19
|
+
width: 100%;
|
|
20
|
+
height: 80px;
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom/client';
|
|
3
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
4
|
+
import App from './App.jsx';
|
|
5
|
+
|
|
6
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
7
|
+
<React.StrictMode>
|
|
8
|
+
<BrowserRouter>
|
|
9
|
+
<App />
|
|
10
|
+
</BrowserRouter>
|
|
11
|
+
</React.StrictMode>
|
|
12
|
+
);
|