Skip to content

Commit 838a76a

Browse files
committed
Hello, jsonCMP
0 parents  commit 838a76a

77 files changed

Lines changed: 7652 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy.yml

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
name: Publish
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
7+
concurrency:
8+
group: main-${{ github.sha }}
9+
cancel-in-progress: true
10+
11+
permissions:
12+
contents: write
13+
packages: write
14+
pages: write
15+
id-token: write
16+
17+
jobs:
18+
test-and-coverage:
19+
name: Tests & Coverage
20+
runs-on: ubuntu-latest
21+
outputs:
22+
coverage: ${{ steps.coverage.outputs.percentage }}
23+
steps:
24+
- uses: actions/checkout@v4
25+
26+
- uses: actions/setup-java@v4
27+
with:
28+
distribution: zulu
29+
java-version: 17
30+
31+
- uses: gradle/actions/setup-gradle@v4
32+
33+
- name: Run tests with coverage
34+
run: >
35+
./gradlew
36+
:json-cmp:jvmTest
37+
koverXmlReport
38+
39+
- name: Parse coverage percentage
40+
id: coverage
41+
run: |
42+
REPORT="build/reports/kover/report.xml"
43+
if [ -f "$REPORT" ]; then
44+
MISSED=$(grep -oP 'type="INSTRUCTION"[^/]*missed="\K[0-9]+' "$REPORT" | head -1)
45+
COVERED=$(grep -oP 'type="INSTRUCTION"[^/]*covered="\K[0-9]+' "$REPORT" | head -1)
46+
TOTAL=$((MISSED + COVERED))
47+
if [ "$TOTAL" -gt 0 ]; then
48+
PERCENTAGE=$((COVERED * 100 / TOTAL))
49+
else
50+
PERCENTAGE=0
51+
fi
52+
else
53+
PERCENTAGE=0
54+
fi
55+
echo "percentage=$PERCENTAGE" >> "$GITHUB_OUTPUT"
56+
57+
- name: Upload coverage report
58+
uses: actions/upload-artifact@v4
59+
with:
60+
name: coverage-report
61+
path: build/reports/kover/
62+
63+
update-badges:
64+
name: Update Badges
65+
needs: test-and-coverage
66+
runs-on: ubuntu-latest
67+
steps:
68+
- uses: actions/checkout@v4
69+
with:
70+
ref: main
71+
72+
- name: Determine coverage badge color
73+
id: badge
74+
run: |
75+
COVERAGE=${{ needs.test-and-coverage.outputs.coverage }}
76+
if [ "$COVERAGE" -ge 80 ]; then
77+
COLOR="brightgreen"
78+
elif [ "$COVERAGE" -ge 60 ]; then
79+
COLOR="green"
80+
elif [ "$COVERAGE" -ge 40 ]; then
81+
COLOR="yellowgreen"
82+
elif [ "$COVERAGE" -ge 20 ]; then
83+
COLOR="orange"
84+
else
85+
COLOR="red"
86+
fi
87+
echo "color=$COLOR" >> "$GITHUB_OUTPUT"
88+
89+
- name: Update README badges
90+
run: |
91+
COVERAGE=${{ needs.test-and-coverage.outputs.coverage }}
92+
COLOR=${{ steps.badge.outputs.color }}
93+
REPO=${{ github.repository }}
94+
95+
BUILD_BADGE="[![Build](https://github.com/${REPO}/actions/workflows/main.yml/badge.svg)](https://github.com/${REPO}/actions/workflows/main.yml)"
96+
COVERAGE_BADGE="[![Coverage](https://img.shields.io/badge/coverage-${COVERAGE}%25-${COLOR})](https://github.com/${REPO}/actions/workflows/main.yml)"
97+
98+
BADGE_LINE="${BUILD_BADGE} ${COVERAGE_BADGE}"
99+
100+
if grep -q '^\[!\[Build\]' README.md; then
101+
sed -i "1,/^\[!\[Build\]/s|^\[!\[Build\].*|${BADGE_LINE}|" README.md
102+
elif grep -q '^\[!\[Coverage\]' README.md; then
103+
sed -i "1,/^\[!\[Coverage\]/s|^\[!\[Coverage\].*|${BADGE_LINE}|" README.md
104+
else
105+
sed -i "1a\\\\${BADGE_LINE}\n" README.md
106+
fi
107+
108+
- name: Commit badge updates
109+
run: |
110+
git config user.name "github-actions[bot]"
111+
git config user.email "github-actions[bot]@users.noreply.github.com"
112+
git add README.md
113+
git diff --staged --quiet || git commit -m "ci: Update README badges [skip ci]"
114+
git push
115+
116+
publish:
117+
name: Publish SDK
118+
needs: test-and-coverage
119+
runs-on: macos-latest
120+
steps:
121+
- uses: actions/checkout@v4
122+
123+
- uses: actions/setup-java@v4
124+
with:
125+
distribution: zulu
126+
java-version: 17
127+
128+
- uses: gradle/actions/setup-gradle@v4
129+
130+
- name: Publish to GitHub Packages
131+
env:
132+
MAVEN_REPO_USERNAME: ${{ github.actor }}
133+
MAVEN_REPO_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
134+
run: ./gradlew publishAllPublicationsToGitHubPackagesRepository
135+
136+
deploy-docs:
137+
name: Deploy Docs
138+
needs: test-and-coverage
139+
runs-on: ubuntu-latest
140+
environment:
141+
name: github-pages
142+
url: ${{ steps.deployment.outputs.page_url }}
143+
steps:
144+
- uses: actions/checkout@v4
145+
146+
- uses: actions/setup-python@v5
147+
with:
148+
python-version: "3.x"
149+
150+
- name: Install MkDocs and theme
151+
run: pip install mkdocs-material
152+
153+
- name: Build docs
154+
run: mkdocs build
155+
156+
- name: Setup Pages
157+
uses: actions/configure-pages@v5
158+
159+
- name: Upload Pages artifact
160+
uses: actions/upload-pages-artifact@v3
161+
with:
162+
path: site/
163+
164+
- name: Deploy to GitHub Pages
165+
id: deployment
166+
uses: actions/deploy-pages@v4

.github/workflows/test.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: Main
2+
3+
on:
4+
pull_request:
5+
branches: [ main ]
6+
7+
concurrency:
8+
group: pr-${{ github.event.pull_request.number }}
9+
cancel-in-progress: true
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
detekt:
16+
name: Detekt
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- uses: actions/setup-java@v4
22+
with:
23+
distribution: zulu
24+
java-version: 17
25+
26+
- uses: gradle/actions/setup-gradle@v4
27+
28+
- name: Run Detekt
29+
run: ./gradlew detektAll
30+
31+
test-jvm:
32+
name: Tests (JVM)
33+
runs-on: ubuntu-latest
34+
steps:
35+
- uses: actions/checkout@v4
36+
37+
- uses: actions/setup-java@v4
38+
with:
39+
distribution: zulu
40+
java-version: 17
41+
42+
- uses: gradle/actions/setup-gradle@v4
43+
44+
- name: Run JVM tests
45+
run: ./gradlew :json-cmp:jvmTest
46+
47+
test-ios:
48+
name: Tests (iOS Simulator)
49+
runs-on: macos-latest
50+
steps:
51+
- uses: actions/checkout@v4
52+
53+
- uses: actions/setup-java@v4
54+
with:
55+
distribution: zulu
56+
java-version: 17
57+
58+
- uses: gradle/actions/setup-gradle@v4
59+
60+
- name: Run iOS simulator tests
61+
run: ./gradlew :json-cmp:iosSimulatorArm64Test

.gitignore

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Gradle files
2+
.gradle/
3+
build/
4+
5+
# Local configuration file (sdk path, etc)
6+
local.properties
7+
8+
# MkDocs build output
9+
site/
10+
11+
# Log/OS Files
12+
*.log
13+
14+
# Android Studio generated files and folders
15+
captures/
16+
.externalNativeBuild/
17+
.cxx/
18+
*.aab
19+
*.apk
20+
output-metadata.json
21+
22+
# IntelliJ
23+
*.iml
24+
.idea/
25+
misc.xml
26+
deploymentTargetDropDown.xml
27+
render.experimental.xml
28+
29+
# Keystore files
30+
*.jks
31+
*.keystore
32+
33+
# Google Services (e.g. APIs or Firebase)
34+
google-services.json
35+
36+
# Android Profiling
37+
*.hprof
38+
/.kotlin/

README.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# JsonCMP
2+
3+
Kotlin Multiplatform Compose JSON viewer and editor component for Android, iOS, and JVM Desktop.
4+
5+
## Features
6+
7+
- **JSON Viewer** — Syntax-highlighted, foldable JSON tree with line numbers
8+
- **JSON Editor** — Editable JSON with real-time validation, formatting, and sorting
9+
- **Search** — Highlight matching text across the JSON document
10+
- **Multiple Themes** — Dark, Light, Monokai, Dracula, Solarized Dark
11+
- **KMP** — Android, iOS, and JVM Desktop support via Compose Multiplatform
12+
13+
## Installation
14+
15+
```kotlin
16+
// build.gradle.kts
17+
dependencies {
18+
implementation("dev.skymansandy:json-cmp:<version>")
19+
}
20+
```
21+
22+
## Quick Start
23+
24+
```kotlin
25+
@Composable
26+
fun MyScreen() {
27+
val state = rememberJsonEditorState(
28+
initialJson = """{"name": "John", "age": 30}""",
29+
)
30+
31+
JsonCMP(
32+
modifier = Modifier.fillMaxSize(),
33+
state = state,
34+
)
35+
}
36+
```
37+
38+
## Editor Mode
39+
40+
```kotlin
41+
@Composable
42+
fun MyEditor() {
43+
val state = rememberJsonEditorState(
44+
initialJson = """{"name": "John", "age": 30}""",
45+
isEditing = true,
46+
)
47+
48+
JsonCMP(
49+
modifier = Modifier.fillMaxSize(),
50+
state = state,
51+
onJsonChange = { json, parsed, error ->
52+
// React to JSON changes
53+
},
54+
)
55+
}
56+
```
57+
58+
## Themes
59+
60+
```kotlin
61+
JsonCMP(
62+
state = state,
63+
colors = JsonCmpColors.Monokai, // Dark, Light, Monokai, Dracula, SolarizedDark
64+
)
65+
```
66+
67+
## API
68+
69+
### JsonCMP
70+
71+
```kotlin
72+
@Composable
73+
fun JsonCMP(
74+
modifier: Modifier = Modifier,
75+
state: JsonEditorState,
76+
searchQuery: String = "",
77+
colors: JsonCmpColors = JsonCmpColors.Dark,
78+
onJsonChange: (json: String, parsed: JsonNode?, error: JsonError?) -> Unit = { _, _, _ -> },
79+
)
80+
```
81+
82+
### JsonEditorState
83+
84+
```kotlin
85+
val state = rememberJsonEditorState(
86+
initialJson = "...",
87+
isEditing = false,
88+
)
89+
90+
state.updateRawJson(newJson) // Update JSON content
91+
state.format(compact = false) // Pretty-print or minify
92+
state.sortKeys(ascending = true) // Sort object keys
93+
state.collapseAll() // Collapse all foldable nodes
94+
state.expandAll() // Expand all nodes
95+
state.isEditing = true // Toggle editor mode
96+
```

0 commit comments

Comments
 (0)