diff --git a/SessionZero/Pages/Library/CharacterTemplates/Templates.razor b/SessionZero/Pages/Library/CharacterTemplates/Templates.razor
new file mode 100644
index 0000000..02bbec8
--- /dev/null
+++ b/SessionZero/Pages/Library/CharacterTemplates/Templates.razor
@@ -0,0 +1,35 @@
+@page "/library/character-templates"
+
+
+
+ Back to Library
+
+
+
Character Templates
+
+
+
+
+

+
Create
+
Create a new template
+
+
+
+

+
Manage
+
Manage and edit saved templates
+
+
+
+

+
SessionZeroDB
+
Search online for new templates (NOT FUNCTIONAL)
+
+
+
+
+
+@code {
+
+}
\ No newline at end of file
diff --git a/SessionZero/Pages/Library/Datasets.razor b/SessionZero/Pages/Library/Datasets.razor
deleted file mode 100644
index 0c65ccf..0000000
--- a/SessionZero/Pages/Library/Datasets.razor
+++ /dev/null
@@ -1,10 +0,0 @@
-@page "/Library/Datasets"
-
-
-
- Back to Library
-
-
-Datasets
-
-@_message
\ No newline at end of file
diff --git a/SessionZero/Pages/Library/Datasets.razor.cs b/SessionZero/Pages/Library/Datasets.razor.cs
deleted file mode 100644
index 2f753ce..0000000
--- a/SessionZero/Pages/Library/Datasets.razor.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Microsoft.AspNetCore.Components;
-
-namespace SessionZero.Pages.Library;
-
-public partial class Datasets : ComponentBase
-{
- private string _message = "Datasets Page Loaded Successfully!";
-}
\ No newline at end of file
diff --git a/SessionZero/Pages/Library/Datasets/Datasets.razor b/SessionZero/Pages/Library/Datasets/Datasets.razor
new file mode 100644
index 0000000..87d053d
--- /dev/null
+++ b/SessionZero/Pages/Library/Datasets/Datasets.razor
@@ -0,0 +1,275 @@
+@page "/Library/Datasets"
+@using SessionZero.Data
+@using SessionZero.Models
+@inject NavigationManager NavigationManager
+@inject SessionZero.Services.ISzfStorageService SzfStorageService
+@inject SzfParser SzfParser
+@inject SzfGenerator SzfGenerator
+
+
+
+ Back to Library
+
+
+Datasets
+
+@if (!string.IsNullOrEmpty(_message))
+{
+
+}
+
+@switch (CurrentView)
+{
+ case DatasetView.Overview:
+
+
+

+
Create
+
Create a new dataset
+
+
+
+

+
Manage
+
Manage and edit saved datasets
+
+
+
+

+
SessionZeroDB
+
Search online for new datasets (NOT FUNCTIONAL)
+
+
+ break;
+
+ case DatasetView.CreateDataset:
+ Create New Dataset
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ break;
+
+ case DatasetView.ManageDatasets:
+ Manage Datasets
+ @if (SavedDatasets == null)
+ {
+ Loading datasets...
+ }
+ else if (!SavedDatasets.Any())
+ {
+
+
No datasets saved yet. Click "Create" to add one.
+
+ }
+ else
+ {
+
+
+
+ Name |
+ Type |
+ Version |
+ Author |
+ Actions |
+
+
+
+ @foreach (var dataset in SavedDatasets)
+ {
+
+ @dataset.Metadata.Name |
+ @dataset.DatasetType |
+ @dataset.Metadata.Version |
+ @dataset.Metadata.Author |
+
+
+
+
+
+ |
+
+ }
+
+
+ }
+
+
+
+ break;
+}
+
+@code {
+ private enum DatasetView { Overview, CreateDataset, ManageDatasets }
+ private DatasetView CurrentView = DatasetView.Overview;
+ private SessionZero.Data.Dataset NewDataset = new();
+ private IEnumerable? SavedDatasets;
+ private string _message = string.Empty;
+
+ // New property to handle Version binding
+ private string DatasetVersionString
+ {
+ get => NewDataset.Metadata.Version?.ToString() ?? string.Empty;
+ set
+ {
+ if (Version.TryParse(value, out var parsedVersion))
+ {
+ NewDataset.Metadata.Version = parsedVersion;
+ }
+ else
+ {
+ // Handle invalid version string, e.g., set to null or a default, or show an error
+ // For simplicity, we'll just leave it as is if parsing fails.
+ // A better approach would be to use a CustomValidator or more robust input handling.
+ }
+ }
+ }
+
+ protected override async Task OnInitializedAsync()
+ {
+ await LoadDatasets();
+ }
+
+ private void ShowOverview()
+ {
+ CurrentView = DatasetView.Overview;
+ _message = string.Empty; // Clear any messages
+ NewDataset = new(); // Reset the new dataset form
+ StateHasChanged();
+ }
+
+ private void ShowCreateDataset()
+ {
+ CurrentView = DatasetView.CreateDataset;
+ _message = string.Empty;
+ NewDataset = new() // Initialize with default values if needed
+ {
+ Metadata = new()
+ {
+ Guid = Guid.NewGuid(),
+ Version = new Version(1, 0, 0) // Ensure Version is initialized
+ }
+ };
+ StateHasChanged();
+ }
+
+ private async Task ShowManageDatasets()
+ {
+ CurrentView = DatasetView.ManageDatasets;
+ _message = string.Empty;
+ await LoadDatasets(); // Reload datasets when entering management view
+ StateHasChanged();
+ }
+
+ private async Task HandleCreateDataset()
+ {
+ try
+ {
+ var validationResult = NewDataset.Validate();
+ if (!validationResult.IsValid)
+ {
+ _message = "Validation errors: " + string.Join("; ", validationResult.Errors);
+ return;
+ }
+
+ SzfFile szfFile = new SzfFile
+ {
+ Guid = NewDataset.Metadata.Guid.ToString(),
+ Content = SzfGenerator.Generate(NewDataset),
+ Type = "dataset",
+ };
+ await SzfStorageService.SaveSzfFileAsync(szfFile);
+ _message = $"Dataset '{NewDataset.Metadata.Name}' created successfully!";
+ await LoadDatasets(); // Refresh the list of datasets
+ ShowOverview(); // Go back to overview after creation
+ }
+ catch (Exception ex)
+ {
+ _message = $"Error creating dataset: {ex.Message}";
+ }
+ }
+
+ private async Task LoadDatasets()
+ {
+ try
+ {
+ var allSzfDatasetFiles = await SzfStorageService.GetSzfFilesByTypeAsync("dataset");
+ var allSzfObjects = allSzfDatasetFiles.Select(file => SzfParser.Parse(file.Content));
+
+ SavedDatasets = allSzfObjects.OfType().ToList();
+ }
+ catch (Exception ex)
+ {
+ _message = $"Error loading datasets: {ex.Message}";
+ SavedDatasets = new List(); // Ensure it's not null on error
+ }
+ StateHasChanged();
+ }
+
+ private async Task EditDataset(string guid)
+ {
+ _message = $"Attempting to edit dataset with GUID: {guid}";
+ var datasetFileToEdit = await SzfStorageService.GetSzfFileByGuidAsync(guid);
+ var datasetToEdit = SzfParser.Parse(datasetFileToEdit.Content);
+ if (datasetToEdit is Dataset ds)
+ {
+ NewDataset = ds; // Populate the form with the dataset to edit
+ CurrentView = DatasetView.CreateDataset; // Re-use the create form for editing
+ _message = $"Editing dataset: {ds.Metadata.Name}";
+ }
+ else
+ {
+ _message = "Dataset not found for editing.";
+ }
+ StateHasChanged();
+ }
+
+ private async Task DeleteDataset(string guid)
+ {
+ try
+ {
+ await SzfStorageService.DeleteSzfFileAsync(guid);
+ _message = "Dataset deleted successfully!";
+ await LoadDatasets(); // Refresh the list after deletion
+ }
+ catch (Exception ex)
+ {
+ _message = $"Error deleting dataset: {ex.Message}";
+ }
+ StateHasChanged();
+ }
+}
\ No newline at end of file
diff --git a/SessionZero/Pages/Library/Datasets/Datasets.razor.cs b/SessionZero/Pages/Library/Datasets/Datasets.razor.cs
new file mode 100644
index 0000000..b6da6ea
--- /dev/null
+++ b/SessionZero/Pages/Library/Datasets/Datasets.razor.cs
@@ -0,0 +1,8 @@
+using Microsoft.AspNetCore.Components;
+
+namespace SessionZero.Pages.Library.Datasets;
+
+public partial class Datasets : ComponentBase
+{
+
+}
\ No newline at end of file
diff --git a/SessionZero/Pages/Library/Datasets.razor.css b/SessionZero/Pages/Library/Datasets/Datasets.razor.css
similarity index 100%
rename from SessionZero/Pages/Library/Datasets.razor.css
rename to SessionZero/Pages/Library/Datasets/Datasets.razor.css
diff --git a/SessionZero/Pages/Library/Library.razor b/SessionZero/Pages/Library/Library.razor
index 2024cc2..98bd909 100644
--- a/SessionZero/Pages/Library/Library.razor
+++ b/SessionZero/Pages/Library/Library.razor
@@ -1,9 +1,11 @@
@page "/Library"
@inject NavigationManager NavigationManager
+
+
Library
-
+
Character Sheet Templates
Create and manage Character Sheet Templates
@@ -13,40 +15,4 @@
Datasets
Create and manage Datasets
-
-
-
-
-
-@code {
- private void NavigateToTemplates()
- {
-
- }
-}
\ No newline at end of file
+
\ No newline at end of file
diff --git a/SessionZero/Pages/Library/Templates.razor b/SessionZero/Pages/Library/Templates.razor
deleted file mode 100644
index 064269b..0000000
--- a/SessionZero/Pages/Library/Templates.razor
+++ /dev/null
@@ -1,12 +0,0 @@
-@page "/library/templates"
-
-
-
- Back to Library
-
-
-Templates
-
-@code {
-
-}
\ No newline at end of file
diff --git a/SessionZero/wwwroot/css/app.css b/SessionZero/wwwroot/css/app.css
index 527890f..c4729a1 100644
--- a/SessionZero/wwwroot/css/app.css
+++ b/SessionZero/wwwroot/css/app.css
@@ -100,6 +100,30 @@ html, body, #app {
font-size: 0.9rem;
}
+.library-card-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1.5rem;
+ justify-content: flex-start;
+}
+
+.library-card-container .card {
+ flex: 1;
+ min-width: 250px;
+ max-width: calc(50% - 0.75rem);
+ padding: 1.5rem;
+}
+
+.library-card-container .card p {
+ color: var(--neutral-medium);
+}
+
+.library-card-container .card h1 {
+ font-size: 1.6em;
+ color: var(--heading-color);
+ font-weight: normal;
+}
+
.button-main {
background-color: var(--primary-color);
padding: 0.6rem 1.2rem;
@@ -145,6 +169,267 @@ html, body, #app {
transition: all 0.2s ease;
}
+/* Forms and Input Elements */
+.form-group {
+ margin-bottom: 1rem;
+}
+
+.form-label {
+ display: block;
+ margin-bottom: 0.5rem;
+ color: var(--heading-color);
+ font-weight: 600;
+ font-size: 0.9rem;
+}
+
+.form-control {
+ width: 100%;
+ padding: 0.8rem 1rem;
+ background-color: var(--form-background);
+ border: 1px solid var(--primary-color);
+ border-radius: 4px;
+ color: var(--text-color);
+ font-size: 1rem;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+}
+
+.form-control:focus {
+ outline: none;
+ border-color: var(--accent-color);
+ box-shadow: 0 0 0 3px rgba(91, 137, 179, 0.3); /* Accent color with transparency */
+}
+
+.form-control::placeholder {
+ color: var(--neutral-medium);
+}
+
+textarea.form-control {
+ min-height: 100px;
+ resize: vertical;
+}
+
+select.form-control {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background-image: url('../res/icons/arrow-left.svg'); /* Using local arrow-left.svg */
+ background-repeat: no-repeat;
+ background-position: right 1rem center;
+ background-size: 1em;
+ padding-right: 2.5rem; /* Make space for the arrow */
+}
+
+/* Checkboxes and Radios */
+.form-check {
+ display: flex;
+ align-items: center;
+ margin-bottom: 0.5rem;
+}
+
+.form-check-input {
+ margin-right: 0.75rem;
+ width: 1.25rem;
+ height: 1.25rem;
+ border: 1px solid var(--primary-color-light);
+ border-radius: 3px;
+ background-color: var(--form-background);
+ cursor: pointer;
+ position: relative;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ transition: background-color 0.2s ease, border-color 0.2s ease;
+}
+
+.form-check-input:checked {
+ background-color: var(--accent-color);
+ border-color: var(--accent-color);
+}
+
+.form-check-input[type="checkbox"]:checked::after {
+ content: '';
+ position: absolute;
+ top: 3px;
+ left: 3px;
+ width: 6px;
+ height: 10px;
+ border: solid var(--text-color);
+ border-width: 0 2px 2px 0;
+ transform: rotate(45deg);
+}
+
+.form-check-input[type="radio"] {
+ border-radius: 50%;
+}
+
+.form-check-input[type="radio"]:checked::after {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--text-color);
+ transform: translate(-50%, -50%);
+}
+
+.form-check-label {
+ color: var(--text-color);
+ cursor: pointer;
+ font-size: 1rem;
+}
+
+/* Tables */
+.data-table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-top: 1.5rem;
+ background-color: var(--secondary-color);
+ border-radius: 8px;
+ overflow: hidden; /* Ensures rounded corners apply to content */
+}
+
+.data-table th,
+.data-table td {
+ padding: 1rem 1.2rem;
+ text-align: left;
+ border-bottom: 1px solid var(--neutral-dark);
+}
+
+.data-table thead {
+ background-color: var(--primary-color);
+}
+
+.data-table th {
+ color: var(--heading-color);
+ font-weight: 600;
+ font-size: 0.9rem;
+ text-transform: uppercase;
+}
+
+.data-table tbody tr {
+ transition: background-color 0.2s ease;
+}
+
+.data-table tbody tr:nth-child(even) {
+ background-color: var(--secondary-color);
+}
+
+.data-table tbody tr:nth-child(odd) {
+ background-color: var(--form-background);
+}
+
+.data-table tbody tr:hover {
+ background-color: var(--primary-color-light);
+ cursor: pointer;
+}
+
+.data-table td {
+ color: var(--text-color);
+ font-size: 0.95rem;
+}
+
+/* Lists (e.g., for items or dynamic content) */
+.custom-list {
+ list-style: none;
+ padding: 0;
+ margin: 1.5rem 0;
+ background-color: var(--secondary-color);
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.custom-list-item {
+ padding: 1rem 1.2rem;
+ border-bottom: 1px solid var(--neutral-dark);
+ color: var(--text-color);
+ transition: background-color 0.2s ease;
+}
+
+.custom-list-item:last-child {
+ border-bottom: none;
+}
+
+.custom-list-item:hover {
+ background-color: var(--primary-color-light);
+ cursor: pointer;
+}
+
+.custom-list-item-header {
+ font-weight: 600;
+ color: var(--heading-color);
+ margin-bottom: 0.5rem;
+}
+
+.custom-list-item-meta {
+ font-size: 0.85rem;
+ color: var(--neutral-medium);
+}
+
+/* Custom Visual Elements (e.g., panels, containers) */
+.panel {
+ background-color: var(--secondary-color);
+ padding: 1.5rem;
+ border-radius: 8px;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
+ margin-bottom: 1.5rem;
+}
+
+.panel-header {
+ font-size: 1.5rem;
+ color: var(--heading-color);
+ margin-bottom: 1rem;
+ border-bottom: 1px solid var(--neutral-dark);
+ padding-bottom: 0.5rem;
+}
+
+.panel-content {
+ color: var(--text-color);
+ line-height: 1.6;
+}
+
+.flex-container {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+.flex-item {
+ flex: 1;
+ min-width: 250px;
+}
+
+/* Action Buttons within forms/lists */
+.button-action {
+ background-color: var(--success-color);
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+ border: none;
+ cursor: pointer;
+ font-size: 0.85rem;
+ color: var(--button-text);
+ transition: background-color 0.2s ease;
+}
+
+.button-action:hover {
+ background-color: var(--success-color-hover);
+}
+
+.button-danger {
+ background-color: var(--danger-color);
+}
+
+.button-danger:hover {
+ background-color: var(--danger-color-hover);
+}
+
+.button-group {
+ display: flex;
+ gap: 0.5rem;
+ margin-top: 1rem;
+}
+
/*
-------------------------------------------------
*/
diff --git a/new-szf-docs.md b/new-szf-docs.md
index 3efada9..44da1cc 100644
--- a/new-szf-docs.md
+++ b/new-szf-docs.md
@@ -79,16 +79,16 @@ FieldName (text) = Some Value # This is an inline comment.
The .szf format supports a variety of field types to handle different kinds of data. These types are declared in character_template and dataset files.
-| Type | Description | Example Value |
-| :---- | :---- | :---- |
-| text | A single line of text. | Elara the Brave |
-| text-field | A multi-line block of text. | A long sword forged by...\nIt glows faintly. |
-| number | An integer or floating-point number. | 16 or 3.5 |
-| bool | A boolean value. | true or false |
-| calculated | A value derived from a formula. | (Strength - 10) / 2 |
-| system | A special field that controls application behavior. | items |
-| entry-reference | A reference to a single entry from a dataset. | ClassData or ItemData.Longsword |
-| entry-reference-list | A comma-separated list of references to entries from a dataset. | CoreFeats.PowerAttack, CoreFeats.Cleave |
+| Type | Description | Example Value |
+|:---------------------|:----------------------------------------------------------------|:---------------------------------------------|
+| text | A single line of text. | Elara the Brave |
+| text-field | A multi-line block of text. | A long sword forged by...\nIt glows faintly. |
+| number | An integer or floating-point number. | 16 or 3.5 |
+| bool | A boolean value. | true or false |
+| calculated | A value derived from a formula. | (Strength - 10) / 2 |
+| system | A special field that controls application behavior. | |
+| entry-reference | A reference to a single entry from a dataset. | ClassData or ItemData.Longsword |
+| entry-reference-list | A comma-separated list of references to entries from a dataset. | CoreFeats.PowerAttack, CoreFeats.Cleave |
## **4. File Type Specifications**