Skip to content

Commit 6357e60

Browse files
Updated docs generator
1 parent 36d1d6e commit 6357e60

17 files changed

Lines changed: 610 additions & 280 deletions
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"packageName": "PicoXLSX",
33
"version": "4.1.0",
4-
"description": "PicoXLSX is a library to generate Microsoft Excel files (XLSX) in an easy and native way. This package is the meta package of PicoXLSX and should be used in most cases as dependency in your project. It uses the dependencies of NanoXLSX"
4+
"description": "PicoXLSX is a library to generate Microsoft Excel files (XLSX) in an easy and native way. This package is the meta package of PicoXLSX and should be used in most cases as dependency in your project. It uses the dependencies of NanoXLSX",
5+
"nuGetUrl": "https://www.nuget.org/packages/PicoXLSX"
56
}

Docs.IndexGenerator/Config/plugin-config.json

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,45 @@
44
"id": "NanoXLSX.Core",
55
"title": "NanoXLSX.Core API",
66
"path": "NanoXLSX.Core",
7-
"description": "Core library",
7+
"description": "Core library: workbooks, worksheets, cells, styles, colors. Has no external dependencies.",
88
"repository": "https://github.com/rabanti-github/NanoXLSX",
99
"repositoryDisplayName": "NanoXLSX",
10-
"bundled": true
10+
"bundled": true,
11+
"apiDocsUrl": "https://rabanti-github.github.io/NanoXLSX/NanoXLSX.Core/",
12+
"nuGetUrl": "https://www.nuget.org/packages/NanoXLSX.Core"
1113
},
1214
{
1315
"id": "NanoXLSX.Writer",
1416
"title": "NanoXLSX.Writer API",
1517
"path": "NanoXLSX.Writer",
16-
"description": "Writer component, to save XLSX files",
18+
"description": "Writer plugin: extension methods to save XLSX files. Depends on Core.",
1719
"repository": "https://github.com/rabanti-github/NanoXLSX",
1820
"repositoryDisplayName": "NanoXLSX",
19-
"bundled": true
21+
"bundled": true,
22+
"apiDocsUrl": "https://rabanti-github.github.io/NanoXLSX/NanoXLSX.Writer/",
23+
"nuGetUrl": "https://www.nuget.org/packages/NanoXLSX.Writer"
2024
},
2125
{
2226
"id": "NanoXLSX.Formatting",
23-
"title": "NanoXLSX.Formatter API",
27+
"title": "NanoXLSX.Formatting API",
2428
"path": "https://rabanti-github.github.io/NanoXLSX.Formatting",
25-
"description": "Extension managing advanced cell formatting",
29+
"description": "Formatting plugin: in-line cell formatting (rich text). Depends on Core. Maintained in an external repository.",
2630
"repository": "https://github.com/rabanti-github/NanoXLSX.Formatting",
2731
"repositoryDisplayName": "NanoXLSX.Formatting",
28-
"bundled": true
32+
"bundled": true,
33+
"apiDocsUrl": "https://rabanti-github.github.io/NanoXLSX.Formatting/",
34+
"nuGetUrl": "https://www.nuget.org/packages/NanoXLSX.Formatting"
2935
},
3036
{
3137
"id": "NanoXLSX.Reader",
3238
"title": "NanoXLSX.Reader API",
3339
"path": "NanoXLSX.Reader",
34-
"description": "Reader component, to load XLSX files",
40+
"description": "Reader plugin: extension methods to load XLSX files. Depends on Core.",
3541
"repository": "https://github.com/rabanti-github/NanoXLSX",
3642
"repositoryDisplayName": "NanoXLSX",
37-
"bundled": false
43+
"bundled": false,
44+
"apiDocsUrl": "https://rabanti-github.github.io/NanoXLSX/NanoXLSX.Reader/",
45+
"nuGetUrl": "https://www.nuget.org/packages/NanoXLSX.Reader"
3846
}
3947
]
40-
}
48+
}
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
{
22
"projectName": "PicoXLSX",
33
"baseDescription": "PicoXLSX is a library to generate Microsoft Excel files (XLSX) in an easy and native way.",
4-
"rootDescription": "PicoXLSX is now a subset of <a href=\"https://github.com/rabanti-github/NanoXLSX\">NanoXLSX</a>, and uses its library parts. The library is modular and consists of one core and several optional dependencies.<br>There is no code anymore in the PicoXLSX repository. The code is maintained in the repository of NanoXLSX, and additional dependency projects.<br>This documentation page contains the links to all relevant documentation sub-pages of NanoXLSX."
4+
"rootDescription": "PicoXLSX is now a subset of <a href=\"https://github.com/rabanti-github/NanoXLSX\">NanoXLSX</a>, and uses its library parts. The library is modular and consists of one core and several optional dependencies.<br>There is no code anymore in the PicoXLSX repository. The code is maintained in the repository of NanoXLSX, and additional dependency projects.<br>This documentation page contains the links to all relevant documentation sub-pages of NanoXLSX.",
5+
"apiDocsUrl": "https://rabanti-github.github.io/PicoXLSX/",
6+
"repositoryUrl": "https://github.com/rabanti-github/PicoXLSX",
7+
"demoRepositoryUrl": "https://github.com/rabanti-github/NanoXLSX.Demo",
8+
"demoRepositoryUseCaseUrl": "https://github.com/rabanti-github/NanoXLSX.Demo/tree/main/PicoXLSX/Demo/UseCases",
9+
"llmsSummary": "A small, dependency-free .NET library to create Microsoft Excel (XLSX) files. Modular plugin architecture: a Core package plus optional Writer, and Formatting plugins. A Reader can be added separately. Targets .NET Standard 2.0 and .NET Framework 4.5+."
510
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* PicoXLSX is a small .NET library to generate XLSX (Microsoft Excel 2007 or newer) files in an easy and native way
3+
* Copyright Raphael Stoeckli © 2026
4+
* This library is licensed under the MIT License.
5+
* You find a copy of the license in project folder or on: http://opensource.org/licenses/MIT
6+
*/
7+
8+
using System;
9+
using System.Text;
10+
using Docs.IndexGenerator.Models;
11+
using Docs.IndexGenerator.Util;
12+
13+
namespace Docs.IndexGenerator.Generation
14+
{
15+
internal static class IndexHtmlGenerator
16+
{
17+
public static string Build(RootConfig root, MetaPackageConfig meta, PluginConfig plugins)
18+
{
19+
return $@"
20+
<!doctype html>
21+
<html lang=""en"">
22+
<head>
23+
<meta charset=""utf-8"">
24+
<meta name=""viewport"" content=""width=device-width,initial-scale=1"">
25+
<title>{HtmlEncoder.Escape(root.ProjectName)} — Documentation</title>
26+
<link rel=""stylesheet"" href=""style.css"">
27+
</head>
28+
<body>
29+
<main>
30+
<header>
31+
<h1>
32+
<img src=""NanoXLSX.png""
33+
alt=""NanoXLSX""
34+
style=""height:48px; vertical-align:middle; margin-right:10px;"">
35+
{HtmlEncoder.Escape(root.ProjectName)}
36+
</h1>
37+
38+
<p>{HtmlEncoder.Escape(root.BaseDescription)}</p>
39+
<p>{HtmlEncoder.Escape(root.RootDescription)}</p>
40+
41+
<hr>
42+
43+
<h2>Meta Package v{HtmlEncoder.Escape(meta.Version)}</h2>
44+
<section>
45+
{RenderMetaPackageItem(meta, root)}
46+
<p>There is no documentation for the meta package. Please see the section <b>Dependency Package Documentation</b> for the complete API documentation.</p>
47+
</section>
48+
49+
<p class=""version"">Version {HtmlEncoder.Escape(meta.Version)}</p>
50+
</header>
51+
52+
<hr>
53+
54+
<section>
55+
<h2>Dependency Package Documentation</h2>
56+
<table class=""list"">
57+
<tr>
58+
<td>Package</td><td>Description</td><td>Bundled</td><td>Repository</td>
59+
</tr>
60+
{RenderListItems(plugins)}
61+
</table>
62+
</section>
63+
</main>
64+
</body>
65+
</html>";
66+
}
67+
68+
private static string RenderListItems(PluginConfig cfg)
69+
{
70+
var sb = new StringBuilder();
71+
foreach (var e in cfg.Entries)
72+
{
73+
string repoUrl = Uri.EscapeUriString(e.Repository ?? string.Empty);
74+
string docUrl = $"{Uri.EscapeUriString(e.Path)}/index.html";
75+
sb.AppendLine(" <tr>");
76+
sb.AppendLine($" <td><a href=\"{docUrl}\"><strong>{HtmlEncoder.Escape(e.Title)}</strong></a></td>");
77+
sb.AppendLine($" <td>{HtmlEncoder.Escape(e.Description ?? "")}</td>");
78+
sb.AppendLine($" <td>{HtmlEncoder.Escape(e.Bundled.ToString())}</td>");
79+
sb.AppendLine($" <td><a href=\"{repoUrl}\" target=\"_blank\" rel=\"noopener\">{HtmlEncoder.Escape(e.RepositoryDisplayName ?? string.Empty)}</a></td>");
80+
sb.AppendLine(" </tr>");
81+
}
82+
return sb.ToString();
83+
}
84+
85+
private static string RenderMetaPackageItem(MetaPackageConfig meta, RootConfig root)
86+
{
87+
var sb = new StringBuilder();
88+
sb.AppendLine("<ul class=\"list\">");
89+
string description = StripBaseDescriptionPrefix(root, meta.Description);
90+
sb.AppendLine($" <li><strong>{HtmlEncoder.Escape(meta.PackageName)}</strong> — {HtmlEncoder.Escape(description ?? "")}</li>");
91+
sb.AppendLine("</ul>");
92+
return sb.ToString();
93+
}
94+
95+
private static string StripBaseDescriptionPrefix(RootConfig root, string input)
96+
{
97+
if (string.IsNullOrEmpty(input))
98+
{
99+
return input;
100+
}
101+
if (input.StartsWith(root.BaseDescription, StringComparison.Ordinal))
102+
{
103+
return input[root.BaseDescription.Length..];
104+
}
105+
return input;
106+
}
107+
}
108+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* PicoXLSX is a small .NET library to generate XLSX (Microsoft Excel 2007 or newer) files in an easy and native way
3+
* Copyright Raphael Stoeckli © 2026
4+
* This library is licensed under the MIT License.
5+
* You find a copy of the license in project folder or on: http://opensource.org/licenses/MIT
6+
*/
7+
8+
using System;
9+
using System.Collections.Generic;
10+
using System.Text;
11+
using Docs.IndexGenerator.Models;
12+
13+
namespace Docs.IndexGenerator.Generation
14+
{
15+
/// <summary>
16+
/// Generates an llms.txt file following the convention at https://llmstxt.org/.
17+
/// Layout: H1 project title, blockquote summary, then linked sections.
18+
/// </summary>
19+
internal static class LlmsTxtGenerator
20+
{
21+
public static string Build(RootConfig root, MetaPackageConfig meta, PluginConfig plugins)
22+
{
23+
var sb = new StringBuilder();
24+
25+
sb.Append("# ").AppendLine(root.ProjectName);
26+
sb.AppendLine();
27+
sb.Append("> ").AppendLine(root.LlmsSummary);
28+
sb.AppendLine();
29+
30+
sb.AppendLine("## Packages");
31+
sb.AppendLine();
32+
sb.Append("Meta package **").Append(meta.PackageName).Append(" v").Append(meta.Version)
33+
.AppendLine("** bundles all of the below.");
34+
sb.AppendLine();
35+
36+
string metaUrl = !string.IsNullOrEmpty(meta.NuGetUrl)
37+
? meta.NuGetUrl
38+
: root.RepositoryUrl;
39+
sb.Append("- [").Append(meta.PackageName).Append("](").Append(metaUrl).Append("): ")
40+
.AppendLine(meta.Description ?? string.Empty);
41+
42+
List<DocEntry> nonBundledEntries = new List<DocEntry>();
43+
44+
foreach (DocEntry e in plugins.Entries)
45+
{
46+
if (!e.Bundled)
47+
{
48+
nonBundledEntries.Add(e);
49+
continue;
50+
}
51+
string url = !string.IsNullOrEmpty(e.NuGetUrl)
52+
? e.NuGetUrl
53+
: (e.Repository ?? string.Empty);
54+
string description = e.Description ?? string.Empty;
55+
string bundledTag = e.Bundled ? " (bundled)" : string.Empty;
56+
sb.Append("- [").Append(e.Id).Append("](").Append(url).Append("): ")
57+
.Append(description).AppendLine(bundledTag);
58+
}
59+
60+
if (nonBundledEntries.Count > 0)
61+
{
62+
sb.AppendLine();
63+
sb.AppendLine("Other available packages (not bundled in meta package):");
64+
sb.AppendLine();
65+
foreach (DocEntry e in nonBundledEntries)
66+
{
67+
string url = !string.IsNullOrEmpty(e.NuGetUrl)
68+
? e.NuGetUrl
69+
: (e.Repository ?? string.Empty);
70+
string description = e.Description ?? string.Empty;
71+
sb.Append("- [").Append(e.Id).Append("](").Append(url).Append("): ")
72+
.Append(description).AppendLine();
73+
}
74+
}
75+
76+
sb.AppendLine();
77+
sb.AppendLine("## API Documentation");
78+
sb.AppendLine();
79+
sb.Append("- Combined documentation portal: ").AppendLine(root.ApiDocsUrl);
80+
foreach (var e in plugins.Entries)
81+
{
82+
if (!string.IsNullOrEmpty(e.ApiDocsUrl))
83+
{
84+
sb.Append("- ").Append(e.Id).Append(": ").AppendLine(e.ApiDocsUrl);
85+
}
86+
}
87+
88+
sb.AppendLine();
89+
sb.AppendLine("## Source");
90+
sb.AppendLine();
91+
sb.Append("- Primary repository: ").AppendLine(root.RepositoryUrl);
92+
foreach (var e in plugins.Entries)
93+
{
94+
if (!string.IsNullOrEmpty(e.Repository) &&
95+
!string.Equals(e.Repository, root.RepositoryUrl, StringComparison.OrdinalIgnoreCase))
96+
{
97+
sb.Append("- ").Append(e.Id).Append(" (external): ").AppendLine(e.Repository);
98+
}
99+
}
100+
101+
sb.AppendLine();
102+
sb.AppendLine("## Demos");
103+
sb.AppendLine();
104+
sb.Append("- Repository with running demo use cases: ").AppendLine(root.DemoRepositoryUrl);
105+
sb.Append("- Direct folder URL with use cases for ").Append(root.ProjectName).Append(": ").AppendLine(root.DemoRepositoryUseCaseUrl);
106+
107+
sb.AppendLine();
108+
sb.AppendLine("## Notes");
109+
sb.AppendLine();
110+
sb.AppendLine("- Docs.IndexGenerator/ is a build utility and not part of the public API.");
111+
sb.AppendLine("- This file is generated automatically on build from Docs.IndexGenerator/Config/*.json — do not edit by hand.");
112+
113+
return sb.ToString();
114+
}
115+
}
116+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* PicoXLSX is a small .NET library to generate XLSX (Microsoft Excel 2007 or newer) files in an easy and native way
3+
* Copyright Raphael Stoeckli © 2026
4+
* This library is licensed under the MIT License.
5+
* You find a copy of the license in project folder or on: http://opensource.org/licenses/MIT
6+
*/
7+
8+
using System;
9+
using System.IO;
10+
11+
namespace Docs.IndexGenerator.Generation
12+
{
13+
internal static class StyleCssGenerator
14+
{
15+
public static void Write(string outputDirectory, string templateDirectory)
16+
{
17+
string source = Path.Combine(templateDirectory, "style.css");
18+
string destination = Path.Combine(outputDirectory, "style.css");
19+
if (!File.Exists(source))
20+
{
21+
throw new FileNotFoundException($"Style template not found: {source}");
22+
}
23+
File.Copy(source, destination, overwrite: true);
24+
}
25+
}
26+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* PicoXLSX is a small .NET library to generateXLSX (Microsoft Excel 2007 or newer) files in an easy and native way
3+
* Copyright Raphael Stoeckli © 2026
4+
* This library is licensed under the MIT License.
5+
* You find a copy of the license in project folder or on: http://opensource.org/licenses/MIT
6+
*/
7+
8+
using System;
9+
using System.IO;
10+
using System.Text.Json;
11+
using Docs.IndexGenerator.Models;
12+
13+
namespace Docs.IndexGenerator.Loading
14+
{
15+
internal sealed class ConfigMissingException : Exception
16+
{
17+
public ConfigMissingException(string message) : base(message) { }
18+
}
19+
20+
internal sealed class ConfigParseException : Exception
21+
{
22+
public ConfigParseException(string message, Exception inner) : base(message, inner) { }
23+
}
24+
25+
internal static class ConfigLoader
26+
{
27+
public static (RootConfig Root, MetaPackageConfig Meta, PluginConfig Plugins) Load(
28+
string rootConfigPath,
29+
string metaPackageConfigPath,
30+
string pluginConfigPath)
31+
{
32+
EnsureExists(rootConfigPath);
33+
EnsureExists(metaPackageConfigPath);
34+
EnsureExists(pluginConfigPath);
35+
36+
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
37+
38+
try
39+
{
40+
var root = JsonSerializer.Deserialize<RootConfig>(File.ReadAllText(rootConfigPath), options)
41+
?? throw new InvalidOperationException("Root config deserialized to null");
42+
var meta = JsonSerializer.Deserialize<MetaPackageConfig>(File.ReadAllText(metaPackageConfigPath), options)
43+
?? throw new InvalidOperationException("Meta-package config deserialized to null");
44+
var plugins = JsonSerializer.Deserialize<PluginConfig>(File.ReadAllText(pluginConfigPath), options)
45+
?? throw new InvalidOperationException("Plugin config deserialized to null");
46+
return (root, meta, plugins);
47+
}
48+
catch (Exception ex) when (ex is JsonException || ex is InvalidOperationException)
49+
{
50+
throw new ConfigParseException("Failed to parse config: " + ex.Message, ex);
51+
}
52+
}
53+
54+
private static void EnsureExists(string path)
55+
{
56+
if (!File.Exists(path))
57+
{
58+
throw new ConfigMissingException($"Config file not found: {path}");
59+
}
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)