Skip to content

Builder recipes #21

@stephenwf

Description

@stephenwf

During work on the Manifest Editor, some new patterns for create IIIF were created. Usually focussed on creating IIIF from users input. At the moment it's tied to that UI layer, but I think it could be useful as a standalone piece of iiif builder.

Examples

Here is a minimal definition:

interface CreatePlaintextPayload {
  url: string;
  label?: InternationalString;
}

async function createPlaintext(data: CreatePlaintextPayload, ctx: CreatorFunctionContext) {
  return ctx.embed({
    id: data.url,
    type: "Text",
    label: data.label,
    format: "text/plain",
  });
}

export const plaintextCreator: CreatorDefinition = {
  id: "@manifest-editor/plaintext-creator",
  create: createPlaintext,
  label: "Plaintext",
  summary: "Add link to an plaintext",
  resourceType: "ContentResource",
  resourceFields: ["label", "format"],
  supports: {
    parentFields: ["seeAlso", "rendering", "homepage"],
  },
  staticFields: {
    format: "text/plain",
  },
};

It fully describes taking user input:

{
  "id": "https://example.org/some-link",
  "label": { "en": ["Some link"] }
}

And producing:

{
  "id": "https://example.org/some-link",
  "type": "Text",
  "label": { "en": ["Some link"] },
  "format": "text/plain"
}

This example is not a groundbreaking abstraction. But it does allow for more information to be attached. For example, human readable label/summary for what will be created, where this resource is valid from a IIIF point of view, which fields are created - and which are static.

Nesting definitions

Having a "Library" of these definitions allows the Manifest Editor to compose them together.

interface Payload {
  label?: InternationalString;
  body: InternationalString;
  motivation?: string;
  height?: number;
  width?: number;
}

async function createHtmlAnnotation(data: Payload, ctx: CreatorFunctionContext) {
  const annotation = {
    id: ctx.generateId("annotation"),
    type: "Annotation",
  };

  const targetType = ctx.options.targetType as "Annotation" | "Canvas";

  const languages = Object.keys(data.body);
  const bodies = [];
  for (const language of languages) {
    const body = (data.body as any)[language].join("\n");
    if (body) {
      bodies.push(
        await ctx.create(
          "@manifest-editor/html-body-creator",
          {
            language,
            body,
          },
          { parent: { resource: annotation, property: "items" } }
        )
      );
    }
  }

  if (targetType === "Annotation") {
    return ctx.embed({
      ...annotation,
      motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
      body: bodies,
      target: ctx.getTarget(),
    });
  }

  if (targetType === "Canvas") {
    const canvasId = ctx.generateId("canvas");
    const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });

    const annotationResource = ctx.embed({
      ...annotation,
      motivation: "painting",
      body: bodies,
      target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } },
    });

    const page = ctx.embed({
      id: pageId,
      type: "AnnotationPage",
      items: [annotationResource],
    });

    return ctx.embed({
      id: canvasId,
      type: "Canvas",
      label: data.label || { en: ["Untitled HTML canvas"] },
      height: data.height || 1000,
      width: data.width || 1000,
      items: [page],
    });
  }
}

There is a lot going on in this one, but if you look at the definition you can see what it supports:

export const htmlAnnotation: CreatorDefinition = {
  id: "@manifest-editor/html-annotation",
  create: createHtmlAnnotation,
  label: "HTML Annotation",
  summary: "Add HTML annotation",
  resourceType: "Annotation",
  resourceFields: ["id", "type", "motivation", "body", "target"],
  additionalTypes: ["Canvas"],
  supports: {
    initialData: true,
    parentTypes: ["AnnotationPage", "Manifest"],
    parentFields: ["items"],
  },
  staticFields: {
    type: "Annotation",
  },
};

So this produces an Annotation and can be created on:

  • AnnotationPage - Annotation added directly to the page
  • Manifest - A new canvas is created, and the annotation added

In the creator example, there is a "nested" example, which is calling out to another definition:

await ctx.create(
  "@manifest-editor/html-body-creator",
  {
    language,
    body,
  },
  { parent: { resource: annotation, property: "items" } }
);

So you can compose them together.

With IIIF Builder, you could install or create "plugins" and use them to build IIIF either statically, or incrementally in a UI, similar to the Manifest Editor.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions