+
README Completion
+
+
+
+ {progress.percentage}%
+
+ {progress.percentage === 100 && '🎉 Perfect!'}
+ {progress.percentage >= 75 && progress.percentage < 100 && '🚀 Great start!'}
+ {progress.percentage >= 40 && progress.percentage < 75 && '📝 Getting there...'}
+ {progress.percentage < 40 && '✍️ Just starting'}
+
+
+
+
+ {progress.missingCore.length > 0 && (
+
+
Recommended Sections:
+
+ {progress.missingCore.map(id => {
+ const labelMap = {
+ title: 'Title',
+ description: 'Description',
+ techstack: 'Tech Stack',
+ installation: 'Installation',
+ author: 'Author/License'
+ };
+ return (
+
+ );
+ })}
+
+
+ )}
+
+
Templates
diff --git a/src/styles/index.css b/src/styles/index.css
index bf61a68..6bf21e4 100644
--- a/src/styles/index.css
+++ b/src/styles/index.css
@@ -716,6 +716,98 @@ body.light-mode .hero-title {
box-shadow: 0 0 10px var(--accent);
}
+/* README Progress Indicator */
+.progress-section {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.progress-container {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.progress-bar-bg {
+ width: 100%;
+ height: 8px;
+ background: var(--surface2);
+ border-radius: 4px;
+ overflow: hidden;
+ position: relative;
+}
+
+.progress-bar-fill {
+ height: 100%;
+ background: linear-gradient(90deg, var(--accent) 0%, var(--green) 100%);
+ border-radius: 4px;
+ transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.progress-info {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 12px;
+}
+
+.progress-percent {
+ font-weight: 700;
+ color: var(--text);
+ font-family: "JetBrains Mono", monospace;
+}
+
+.progress-status {
+ color: var(--muted);
+ font-size: 11px;
+}
+
+.missing-sections-container {
+ margin-top: 4px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.missing-title {
+ font-size: 11px;
+ font-weight: 500;
+ color: var(--text);
+ opacity: 0.8;
+}
+
+.missing-chips {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+
+.missing-chip {
+ background: var(--surface2);
+ border: 1px solid var(--border);
+ color: var(--yellow);
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 11px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+}
+
+.missing-chip:hover {
+ background: var(--surface3);
+ border-color: var(--yellow);
+ transform: translateY(-1px);
+}
+
+.missing-chip:active {
+ transform: translateY(0);
+}
+
/* Section toggles */
.section-toggles {
display: flex;
diff --git a/src/utils/markdownUtils.js b/src/utils/markdownUtils.js
index feb852c..f154c8b 100644
--- a/src/utils/markdownUtils.js
+++ b/src/utils/markdownUtils.js
@@ -327,3 +327,59 @@ export function getWordCount(text) {
if (!text) return 0;
return text.trim().split(/\s+/).filter(w => w && w !== '###' && w !== '-').length;
}
+
+export function calculateProgress({ formData, sectionState, selectedTechs, screenshots }) {
+ const isFilled = (id) => {
+ if (!sectionState[id]) return false;
+ switch (id) {
+ case 'title':
+ return !!formData.projName?.trim();
+ case 'description':
+ return !!formData.description?.trim();
+ case 'features':
+ return !!formData.features?.trim();
+ case 'techstack':
+ return selectedTechs?.size > 0 || !!formData.customTech?.trim();
+ case 'installation':
+ return !!formData.installCmds?.trim() || !!formData.usageCmd?.trim();
+ case 'structure':
+ return !!formData.rawStructure?.trim();
+ case 'screenshots':
+ return (screenshots && screenshots.length > 0) || !!formData.imageUrls?.trim() || !!formData.videoUrl?.trim();
+ case 'api':
+ return !!formData.apiDocs?.trim();
+ case 'contributing':
+ return true;
+ case 'author':
+ return !!formData.authorName?.trim() || !!formData.authorGh?.trim();
+ case 'support':
+ return !!formData.supportMsg?.trim() || !!formData.supportBmac?.trim() || !!formData.supportKofi?.trim() || !!formData.supportPatreon?.trim() || !!formData.supportGhSponsors?.trim();
+ case 'academic':
+ return !!formData.abstractText?.trim() || !!formData.paperLink?.trim();
+ default:
+ return false;
+ }
+ };
+
+ const coreSections = ['title', 'description', 'techstack', 'installation', 'author'];
+ const optionalSections = ['features', 'structure', 'screenshots', 'api', 'contributing', 'academic', 'support'];
+
+ let coreScore = 0;
+ coreSections.forEach(id => {
+ if (isFilled(id)) coreScore += 15;
+ });
+
+ let optionalScore = 0;
+ optionalSections.forEach(id => {
+ if (isFilled(id)) optionalScore += 5;
+ });
+ optionalScore = Math.min(25, optionalScore);
+
+ const totalProgress = coreScore + optionalScore;
+ const missingCore = coreSections.filter(id => !isFilled(id));
+
+ return {
+ percentage: totalProgress,
+ missingCore,
+ };
+}