Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Novident Document Format

Official document format for the Novident ecosystem. Defines the tree model that structures all information within the application: documents, folders, and resources. This is the canonical implementation used by the official app and the foundation on which the rest of the suite is built.

What is novident_document_format?

This package provides the three core entities of the Novident data model:

Entity Description
Document A leaf node representing an editable document. It has a name, can belong to a section, and can be moved to the trash.
Folder A container node that organizes the hierarchy. It can hold documents, other folders, and resources. Supports special types: normal, templates, research, manuscript, and trash.
DocumentResource A leaf node representing an external resource (image, PDF, video, etc.) linked via a file path.

All three types share common behavior through mixins: UniversalName (they have a name), Trashable (they can be trashed), AttachableSection (they belong to a section), SpecialFolderBehavior (movement rules between folders), and DragAndDropMixin (drag-and-drop support).

Features

  • Tree-structured document model with folders, documents, and resources
  • Trash system with automatic expiration (30 days by default)
  • Sections for organizing content in compilations
  • Deep and shallow cloning for any node
  • Full JSON serialization (toJson / fromJson) with runtime type discriminators
  • Special folder types: normal, templates, research, manuscript, and trash

Installation

Add the dependency to your pubspec.yaml:

dependencies:
  novident_document_format: <latest> 

Usage

Creating a document

// Document with a name
final document = Document(
  details: NodeDetails.zero(),
  name: 'My first chapter',
);

// Empty document (no name)
final empty = Document.empty(
  details: NodeDetails.zero(),
);

// Cloning a document
final copy = document.clone();
final shallowCopy = document.clone(deep: false);

// Immutable copy with changes
final modified = document.copyWith(
  name: 'Revised chapter',
);

Creating a folder

// Normal folder with children
final folder = Folder(
  details: NodeDetails.zero(),
  name: 'My Documents',
  children: [document],
);

// Research-type folder
final research = Folder(
  details: NodeDetails.zero(),
  name: 'Research',
  folderType: FolderType.research,
  children: <Node>[],
);

// Adding children after creation
research.add(anotherDocument);
research.insert(0, aResource);

// Expanding / collapsing
folder.toggleExpand();
folder.expand(true); // Force expanded

Creating a resource

final resource = DocumentResource(
  details: NodeDetails.zero(),
  name: 'reference-image',
  extension: 'png',
  path: '/assets/images/diagram.png',
);

// Check if a node is a resource
if (resource.isResource) {
  print(resource.value); // -> '/assets/images/diagram.png'
}

Building a full tree

final tree = Folder(
  details: NodeDetails.zero(),
  name: 'root',
  children: [
    Folder(
      details: NodeDetails.zero(),
      name: 'Manuscript',
      folderType: FolderType.manuscript,
      children: [
        Folder(
          details: NodeDetails.zero(),
          name: 'Chapter 1',
          folderType: FolderType.manuscript,
          children: [
            Document(details: NodeDetails.zero(), name: 'Section 1.1'),
            Document(details: NodeDetails.zero(), name: 'Section 1.2'),
          ],
        ),
      ],
    ),
    Folder(
      details: NodeDetails.zero(),
      name: 'Resources',
      folderType: FolderType.research,
      children: [
        DocumentResource(
          details: NodeDetails.zero(),
          name: 'Reference',
          extension: 'pdf',
          path: '/docs/reference.pdf',
        ),
      ],
    ),
    Folder(
      details: NodeDetails.zero(),
      name: 'Trash',
      folderType: FolderType.trash,
      children: <Node>[],
    ),
  ],
);

// Deep search for a node
final exists = tree.existNodeWhere((node) => node.objectName == 'Section 1.1');

JSON serialization

// Serialize to JSON
final doc = Document(
  details: NodeDetails.zero(),
  name: 'My Document',
);
final json = doc.toJson();
// {
//   "runtimeType": "Document",
//   "details": { ... },
//   "name": "My Document",
//   "attachedSection": "structured-based",
//   "trashOptions": { "trashed": false, "expire": null }
// }

// Deserialize from JSON
final reconstructed = Document.fromJson(json);

final folder = Folder(
  details: NodeDetails.zero(),
  name: 'root',
  children: [doc],
);
final folderJson = folder.toJson();
// Includes "children" with recursive serialization of each child

final folderReconstructed = Folder.fromJson(folderJson);

final resource = DocumentResource(
  details: NodeDetails.zero(),
  name: 'photo',
  extension: 'jpg',
  path: '/photos/vacation.jpg',
);
final resourceJson = resource.toJson();
final resourceReconstructed = DocumentResource.fromJson(resourceJson);

Working with the trash

// Send a document to the trash
final trashed = document.setTrashState(expire: DateTime.now(), end: 30);
print(trashed.isTrashed); // -> true

// Recover from the trash
final recovered = trashed.unsetTrashState();
print(recovered.isTrashed); // -> false

// When a folder is trashed, its children are trashed too
final trashedFolder = folder.setTrashState(end: 30);
// All children of trashedFolder now have isTrashed == true

// Moving a node into a trash-type folder marks it automatically
final trash = Folder(
  details: NodeDetails.zero(),
  name: 'Trash',
  folderType: FolderType.trash,
  children: <Node>[],
);
trash.add(document); // document is now trashed

Useful Node extensions

final node = someNode;

// Type checks
if (node.isDocument) { /* ... */ }
if (node.isFolder) { /* ... */ }
if (node.isDocumentResource) { /* ... */ }
if (node.isTrashFolder) { /* ... */ }

// State checks
if (node.isTrashed) { /* ... */ }
if (node.nodeHasName) { /* ... */ }
if (node.nodeHasResource) { /* ... */ }

// Deep comparison between folders
if (folderA.equals(folderB)) { /* ... */ }

License

This project is part of the Novident ecosystem. See the LICENSE file for details.

About

Core document tree model for the Novident writing ecosystem

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages