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

Tools

JSON Import

Paste or upload a Phase 1F 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, top-level storyAssets, top-level sceneDependencies, plot line threads, thread events, asset states, asset events, initial asset owner/location mapping, POV/location mapping, date/time text, purpose/outcome text, and sceneCharacters.

Scene links

sceneDependencies use fromScene and toScene objects. Link scenes with bookOrder, chapterOrder, and sceneOrder; sceneRef is optional future-friendly text. dependencyType, strength, description, and notes are optional. Warnings can be acknowledged; errors block import. Unknown fields are ignored safely.

Not imported yet

No AI extraction, Word/text parsing, manuscript parsing, metrics import, continuity warning import, relationship graphs, merge/update, or export logic.

@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 @preview.StoryAssetCount assets @preview.ResolvedAssetStateCount asset states @preview.ResolvedAssetEventCount asset events @preview.ResolvedAssetCustodyCount custody links @preview.ResolvedSceneDependencyCount dependencies
@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() || preview.UnresolvedAssetReferences.Any() || preview.UnmatchedAssetOwnerNames.Any() || preview.UnmatchedAssetLocationNames.Any() || preview.UnresolvedSceneDependencyReferences.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) } @if (preview.UnresolvedAssetReferences.Any()) { Unresolved asset references: @string.Join(", ", preview.UnresolvedAssetReferences) } @if (preview.UnmatchedAssetOwnerNames.Any()) { Unmatched asset owners/events: @string.Join(", ", preview.UnmatchedAssetOwnerNames) } @if (preview.UnmatchedAssetLocationNames.Any()) { Unmatched asset locations: @string.Join(", ", preview.UnmatchedAssetLocationNames) } @if (preview.UnresolvedSceneDependencyReferences.Any()) { Unresolved dependencies: @string.Join(", ", preview.UnresolvedSceneDependencyReferences) }
}
@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
}
}
} @if (preview.Package.SceneDependencies.Any()) {
@foreach (var group in preview.Package.SceneDependencies.Select((dependency, index) => new { Dependency = dependency, Index = index + 1 }).GroupBy(x => $"{x.Dependency.FromScene?.BookOrder?.ToString() ?? "?"}/{x.Dependency.FromScene?.ChapterOrder?.ToString() ?? "?"}").OrderBy(x => x.Key)) {
Dependencies from @group.Key @group.Count() links
    @foreach (var item in group.Take(12)) { var dependency = item.Dependency; var fromScene = $"{dependency.FromScene?.BookOrder?.ToString() ?? "?"}/{dependency.FromScene?.ChapterOrder?.ToString() ?? "?"}/{dependency.FromScene?.SceneOrder?.ToString() ?? "?"}"; var toScene = $"{dependency.ToScene?.BookOrder?.ToString() ?? "?"}/{dependency.ToScene?.ChapterOrder?.ToString() ?? "?"}/{dependency.ToScene?.SceneOrder?.ToString() ?? "?"}";
  • @fromScene -> @toScene @((dependency.DependencyType ?? "Dependency")) / @(dependency.Strength ?? "soft")
  • } @if (group.Count() > 12) {
  • Showing first 12 links in this group.
  • }
}
} @if (preview.Package.StoryAssets.Any()) {
@foreach (var asset in preview.Package.StoryAssets) {
Asset: @(string.IsNullOrWhiteSpace(asset.DisplayName) ? asset.Name : asset.DisplayName) @asset.States.Count states / @asset.Events.Count events
  • Type: @(asset.Type ?? "Not set") / Initial owner: @(asset.InitialOwnerCharacterName ?? "Not set") / Initial location: @(asset.InitialLocationName ?? "Not set")
}
}

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, @preview.ResolvedThreadEventCount resolved thread events, @preview.StoryAssetCount story assets, @preview.ResolvedAssetStateCount resolved asset states, @preview.ResolvedAssetEventCount resolved asset events, @preview.ResolvedAssetCustodyCount custody links, and @preview.ResolvedSceneDependencyCount scene dependencies.

@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 { }