@model ImportIndexViewModel @{ ViewData["Title"] = "JSON Import"; var preview = Model.Preview; }

Tools

JSON Import

Paste or upload a Phase 1D package, preview it, confirm the summary, then import a new project.

Download sample JSON Back to projects
@if (!string.IsNullOrWhiteSpace(Model.StatusMessage)) {
Import paused

@Model.StatusMessage

@if (preview is { Package: not null, HasDuplicateProjectTitle: true }) {

Duplicate imports will be renamed to @preview.DuplicateImportProjectTitle.

} @if (!string.IsNullOrWhiteSpace(Model.TechnicalDetail)) {
Technical detail

@Model.TechnicalDetail

}
}
Uploading a file replaces the pasted text for this preview.
Supported JSON schema

Required

project.title, each book.title, each book.seriesOrder, each chapter.title, each chapter.order, each scene.title, and each scene.order.

Optional

packageVersion, source, top-level characters, top-level locations, top-level plotLines, plot line threads, thread events, POV/location mapping, date/time text, purpose/outcome text, and sceneCharacters.

Not imported yet

No AI extraction, Word/text parsing, story assets, asset states/custody, dependencies, relationship graphs, merge/update, or export logic. Thread events link to scenes with bookOrder, chapterOrder, and sceneOrder; sceneRef is accepted as future-friendly text only.

@if (preview is not null) {

Preview

@(preview.Package?.Project.Title ?? "Import package")

@preview.BookCount books @preview.ChapterCount chapters @preview.SceneCount scenes @preview.CharacterCount characters @preview.LocationCount locations @preview.SceneCharacterAppearanceCount appearances @preview.PlotLineCount plot lines @preview.PlotThreadCount threads @preview.ResolvedThreadEventCount events
@if (preview.Validation.Errors.Any()) {
Errors block import
} @if (preview.Validation.Warnings.Any()) {
Warnings require confirmation
} @if (preview.Package is not null) { @if (preview.UnmatchedPovNames.Any() || preview.UnmatchedLocationNames.Any() || preview.UnmatchedSceneCharacterNames.Any() || preview.UnresolvedThreadEventReferences.Any()) {
@if (preview.UnmatchedPovNames.Any()) { Unmatched POV: @string.Join(", ", preview.UnmatchedPovNames) } @if (preview.UnmatchedLocationNames.Any()) { Unmatched locations: @string.Join(", ", preview.UnmatchedLocationNames) } @if (preview.UnmatchedSceneCharacterNames.Any()) { Unmatched scene characters: @string.Join(", ", preview.UnmatchedSceneCharacterNames) } @if (preview.UnresolvedThreadEventReferences.Any()) { Unresolved thread events: @string.Join(", ", preview.UnresolvedThreadEventReferences) }
}
@foreach (var bookPreview in preview.BookPreviews) { var book = bookPreview.Book;
Book @book.SeriesOrder: @(string.IsNullOrWhiteSpace(book.Subtitle) ? book.Title : $"{book.Title}: {book.Subtitle}") @bookPreview.ChapterCount chapters / @bookPreview.SceneCount scenes @foreach (var chapterPreview in bookPreview.ChapterPreviews) { var chapter = chapterPreview.Chapter;
Chapter @chapter.ChapterNumber: @chapter.Title @chapterPreview.SceneCount scenes
    @foreach (var scene in chapter.Scenes.OrderBy(x => x.Order)) {
  • Scene @scene.SceneNumber: @scene.Title POV: @(scene.PovCharacterName ?? "Not set") / Location: @(scene.LocationName ?? "Not set") / Characters: @scene.SceneCharacters.Count
  • }
}
}
@if (preview.Package.PlotLines.Any()) {
@foreach (var plotLine in preview.Package.PlotLines) {
Plot line: @(string.IsNullOrWhiteSpace(plotLine.DisplayName) ? plotLine.Name : plotLine.DisplayName) @plotLine.Threads.Count threads / @plotLine.Threads.Sum(x => x.Events.Count) events @foreach (var thread in plotLine.Threads) {
@(string.IsNullOrWhiteSpace(thread.DisplayName) ? thread.Name : thread.DisplayName) @thread.Events.Count events
}
}
}

Confirm import

This will create @preview.Package.Project.Title with @preview.BookCount books, @preview.ChapterCount chapters, @preview.SceneCount scenes, @preview.CharacterCount characters, @preview.LocationCount locations, @preview.SceneCharacterAppearanceCount scene character appearances, @preview.PlotLineCount plot lines, @preview.PlotThreadCount plot threads, and @preview.ResolvedThreadEventCount resolved thread events.

@if (preview.HasDuplicateProjectTitle) {

Because this title already exists, the created project will be named @preview.DuplicateImportProjectTitle if you continue.

}
@if (preview.Validation.Warnings.Any()) {
} @if (preview.HasDuplicateProjectTitle) {
}
}
} @section Scripts { }