Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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