From 506f426fc1e1e0666ac7577ebdf332c7778bc1c0 Mon Sep 17 00:00:00 2001 From: tomcrane Date: Tue, 28 Oct 2025 11:38:19 +0000 Subject: [PATCH 01/12] rewrite of content state approach --- source/presentation/4.0/index.md | 694 ++++++++++++++----------------- source/presentation/4.0/model.md | 2 +- 2 files changed, 309 insertions(+), 387 deletions(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index 65bd92298..ca01ce798 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -883,7 +883,7 @@ This example is a Manifest with one Canvas that represents the temporal extent o > **Key Points** * The decision about which item in the `Choice` to play by default is client dependent. In the absence of any other decision process the client should play the first item. In this specific example, the user might make the decision after reading the `label`, or the client might make the decision based on the `fileSize` property and an assessment of the user's available bandwidth. However, the client may have no way of determining why the publisher has offered the choices, and should not prevent the user from making the choice. The cookbook demonstrates several uses of `Choice` for common use cases. -* Slop +* Slop - impl note - don't interpret **very** minor discrepancies between `duration` on the different Choices and the Container `duration` as an instruction to stretch or compress the audio/video stream to match the Container duration. No real way to quantify this, just _be sensible_. {: .note} @@ -902,6 +902,7 @@ Scenes have infinite height (y axis), width (x axis) and depth (z axis), where 0 The positive y axis points upwards, the positive x axis points to the right, and the positive z axis points forwards (a [right-handed cartesian coordinate system](https://en.wikipedia.org/wiki/Right-hand_rule)). +(image of coordinate system here) ## Use Case 5: Simple 3D Model @@ -1088,31 +1089,29 @@ Properties: [backgroundColor](#model/backgroundColor), [lookAt](#model/lookAt), ## Use Case 6: Complex Scene -Chessboard is a Canvas with image -more than one model -transforms for scale and rotation -Scene in Scene -Exclude -interactionMode +**Chessboard is a Canvas with image (not a 3D chessboard)** +A Scene or a Canvas may be treated as a content resource, referenced or described within the `body` of an Annotation. As with models and other resources, the Annotation is associated with a Scene into which the Scene or Canvas is to be nested through an Annotation `target`. The content resource Scene will be placed within the `target` Scene by aligning the coordinate origins of the two scenes. Alternately, Scene Annotations may use `PointSelector` to place the origin of the resource Scene at a specified coordinate within the `target` Scene. +**more than one model** +**transforms for scale and rotation** +This (no units for scale) allows arbitrarily scaled models to be used, including very small or very large, without needing to deal with very small or very large values. If there is a correspondence to a physical scale, then this can be asserted using the physical dimensions pattern(fwd-ref-to-phys-dims). +**Scene in Scene** +**Exclude** +**interactionMode** -## Merge the below into the examples or into model -This (no units for scale) allows arbitrarily scaled models to be used, including very small or very large, without needing to deal with very small or very large values. If there is a correspondence to a physical scale, then this can be asserted using the physical dimensions pattern(fwd-ref-to-phys-dims). -``` -``` -A Scene or a Canvas may be treated as a content resource, referenced or described within the `body` of an Annotation. As with models and other resources, the Annotation is associated with a Scene into which the Scene or Canvas is to be nested through an Annotation `target`. The content resource Scene will be placed within the `target` Scene by aligning the coordinate origins of the two scenes. Alternately, Scene Annotations may use `PointSelector` to place the origin of the resource Scene at a specified coordinate within the `target` Scene. +## Merge the below into the examples or into model As with other containers in IIIF, Annotations are used to target the Scene to place content such as 3d models into the scene. Annotations are also used to add lights and cameras. A Scene can have multiple models, lights, cameras and other resources, allowing them to be grouped together. Scenes and other IIIF containers, such as Canvases, may also be embedded within Scenes, as described below in the nesting section [fwd-ref-to-nesting]. @@ -1341,26 +1340,49 @@ When a Scene is nested into another Scene, the `backgroundColor` of the Scene to # Annotations +In the examples so far, Annotations have been used to associate the images, audio and other Content Resources with their Containers for presentation. IIIF uses the same W3C standard for the perhaps more familiar _annotation_ concepts of commenting, tagging, describing and so on. Annotations can carry textual transcriptions or translations of the content, discussion about the content and any other linking between resources. +Whereas annotations that associate content resources with Containers are included in the `items` property of the Container, all other types of Annotation are referenced from the `annotations` property. Containers, Manifests, Collections and Ranges can all have this property, linking to relevant annotations. As with the `items` property, annotations are grouped into one or more AnnotationPage resources. These are usually external references. -## Comment Annotations +``` +Manifest + items + Canvas + annotations + AnnotationPage + items + Annotation +``` +## Comment Annotations +### A comment about a segment of music -## Choice of Alternative Resources +(targets Timeline) +"Here begins the development of the second theme" -## Use Case : Multi-spectral Images with Comments +### A comment about a face in a painting +(targets Canvas) +"This might be so-and-so" -## Embedded Content +### A comment about something in a Model -e.g., painting TextualBody on Canvas +(targets Scene) +Look at this scratch in the helmet Todo: This is mostly copy-pasted from properties, is it needed here? It is important to be able to position the textual body of an annotation within the Container's space that the annotation also targets. For example, a description of part of an image in a Canvas should be positioned such that it does not obscure the image region itself and labels to be displayed as part of a Scene should not be rendered such that the text is hidden by the three dimensional geometry of the model. The positioning of the textual body in a container is accomplished through the `position` property, which has as a value a Specific Resource identifying the targeted container as the source and a selector defining how the textual body should be positioned in the targeted container. If this property is not supplied, then the client should do its best to ensure the content is visible to the user. +## Choice of Alternative Resources + +## Use Case 7: Multi-spectral Images with Comments +(same as cookbook example?) +## Embedded Content + +e.g., painting TextualBody on Canvas ## Non Rectangular Segments @@ -1381,15 +1403,12 @@ Move to SpecificResource Move to SpecificResource - - ## Annotation Page "Overlapping elements with a larger z-index cover those with a smaller one." link to https://developer.mozilla.org/en-US/docs/Web/CSS/z-index - ## Annotation Collection deal with this: @@ -1398,12 +1417,6 @@ https://github.com/IIIF/api/pull/2304/files#diff-cc70f02818f6bed2b14dfbf8bf3206e use totalItems? https://iiif.io/api/discovery/1.0/#totalitems - - - - - - # Navigation ## Collection @@ -1608,7 +1621,9 @@ partOf - -# Content State and toggles +# Content State + +(this + model doc should relieve Content State spec of modelling concerns and leave it entirely about protocol) A Content State is simply any valid IIIF Presentation Resource, or part of a Presentation resource. The following are all Content States that describe a "fragment" of IIIF: @@ -1731,6 +1746,8 @@ The mechanisms for passing Content State into a client, and exporting a Content ## Load a particular view of some resource and modify it +⚠ what are we doing with this? Do we still allow it? It's a good use case... + In the previous usage, the fragment of IIIF carried by the annotation with the motivation `contentState` provides enough information for a Client to load a resource and show it. This fragment can also carry additional IIIF Presentation API resources not shown in the referred-to resource. For example, in the following example the Content State carries additional annotations not present in the original published Manifest. A client initializing from this Content State would show these additional annotations to the user: ```json @@ -1757,6 +1774,10 @@ In the previous usage, the fragment of IIIF carried by the annotation with the m As well as adding resources not present in the referred-to resource, the Content State can also remove parts of the referred-to resource from the user's view by applying the behavior `hidden` to them: +⚠⚠⚠⚠ + +now we are entering the danger zone + ```jsonc { // What does this actually look like? I want to load bnf_chateauroux example but HIDE the illumination @@ -1770,15 +1791,16 @@ As well as adding resources not present in the referred-to resource, the Content TODO: what is the processing algorithm for applying incoming `hidden` ? -When a Content State annotation carries a Scene, a view might be initialized from a Content State that introduces an additional Camera that shows the user the point of interest. +~When a Content State annotation carries a Scene, a view might be initialized from a Content State that introduces an additional Camera that shows the user the point of interest.~ -## Modify the Container in a particular context +# Interactivity and Storytelling -The techniques in the previous example are also used within a published IIIF Manifest to modify the contents of a Container in the contexts of different annotations on that Container. This technique allows IIIF to be used for _storytelling_ and other narrative applications beyond simply conveying a static Digital Object into a viewer and leaving subsequent interactions entirely in the control of the user. The `scope` property indicates to the client that the Content State provides valuable context for displaying some aspect of a Scene or other Container. In the case of a commenting annotation, this means that the Content State should be loaded when the commenting annotation is selected or otherwise highlighted. +Sometimes it is necessary to modify the contents of a Container in the contexts of different annotations on that Container. This technique allows IIIF to be used for _storytelling_ and other narrative applications beyond simply conveying a static Digital Object into a viewer and leaving subsequent interactions entirely in the control of the user. +A narrative might comprise a set (an AnnotationPage) of `commenting` annotations that target different parts of the Container, for example a guided tour of a painting or a map. For a Canvas or Timeline it is usually sufficient to leave the interactivity to the client; the fact that comments target different extents implies the client must offer some affordance for those comments (typically the user can click each one), and in response the client will move the current play point of the Timeline to the commenting annotation target, or pan and zoom the viewport to show the relevant part of an image. For 3D this may not be enough; a particular comment only make sense from a certain viewpoint (i.e., Camera), or different steps of the story require different Lights to be active. -Consider a Scene with two models, and two `commenting` annotations: +Consider a Scene with two models, two `commenting` annotations, and a camera. We really only want the camera to be used when the user is looking at the Mandibular tooth, by default and at other times we don't need a specific camera, we can let them explore freely. ```jsonc { @@ -1807,6 +1829,40 @@ Consider a Scene with two models, and two `commenting` annotations: // SpecificResource with PointSelector } }, + { + "id": "https://example.org/iiif/3d/anno-that-paints-desired-camera", + "type": "Annotation", + "motivation": ["painting"], + "behavior": ["hidden"], + "body": { + "type": "SpecificResource", + "source": [ + { + "id": "https://example.org/iiif/3d/cameras/1", + "type": "PerspectiveCamera", + "label": {"en": ["Perspective Camera Pointed At Front of Cranium and Mandible"]}, + "fieldOfView": 50.0, + "near": 0.10, + "far": 2000.0 + } + ] + }, + "target": { + "type": "SpecificResource", + "source": [ + { + "id": "https://example.org/iiif/scene1", + "type": "Scene" + } + ], + "selector": [ + { + "type": "PointSelector", + "x": 0.0, "y": 0.15, "z": 0.75 + } + ] + } + }, { "id": "https://example.org/iiif/3d/anno2", "type": "Annotation", @@ -1830,7 +1886,7 @@ Consider a Scene with two models, and two `commenting` annotations: "type": "AnnotationPage", "items": [ { - "id": "https://example.org/iiif/3d/anno7", + "id": "https://example.org/iiif/3d/commenting-anno-for-mandibular-tooth", "type": "Annotation", "motivation": ["commenting"], "bodyValue": "Mandibular tooth", @@ -1839,7 +1895,7 @@ Consider a Scene with two models, and two `commenting` annotations: } }, { - "id": "https://example.org/iiif/3d/anno5", + "id": "https://example.org/iiif/3d/commenting-anno-for-right-pterygoid-hamulus", "type": "Annotation", "motivation": ["commenting"], "bodyValue": "Right pterygoid hamulus", @@ -1855,315 +1911,198 @@ Consider a Scene with two models, and two `commenting` annotations: In that form, the user is left to interpret the commenting annotations and explore the Scene. The client will render a UI that presents the two commenting annotation in some form and allow the user to navigate between them. The commenting annotations are ordered; while the user might explore them freely in the Scene they might also go "forward" from the first to the second commenting annotation and "back" to the first from the second. -In many complex 3D Scenes, it may not be clear what or how to look at a particular point of interest even when the commenting annotation targets a particular point. The view may be occluded by parts of the model, or other models in the Scene. It may be useful to light the Scene differently in different contexts. - -In the same way an incoming Content State can modify a Scene as it initializes the client, so can a Content State attached to each (non-`painting`) annotation target modify the Scene as the user moves between different annotations. - -The `scope` property of an annotation `target` provides _contextual_ Content State - the viewer should modify the Scene by applying the Content State carried by the `scope` property _only when the user is in the context of that annotation_. - -Taking the first commenting annotation from the above example and adding a `scope` property, whose value is an annotation with the motivation `contentState`, we can introduce a new Camera specifically for this particular annotation, so that when the user selects this comment, the client will switch the view to this camera. This example also changes the background color of the Scene: - -```jsonc -{ - "id": "https://example.org/iiif/3d/anno7", - "type": "Annotation", - "motivation": ["commenting"], - "bodyValue": "Mandibular tooth", - "target": { - - // SpecificResource with PointSelector - // "type": "SpecificResource", - // "source": ... the Scene... - // "selector": ... a point ... - - "scope": { // a modification to the Scene, only in the context of this annotation - - "id": "https://example.org/iiif/3d/anno4", - "type": "Annotation", - "motivation": ["contentState"], - "target": { - "id": "https://example.org/iiif/scene1/page/p1/1", - "type": "Scene", - "backgroundColor": "yellow", - "items": [ - { - "id": "https://example.org/iiif/3d/anno8", - "type": "Annotation", - "motivation": ["painting"], - "body": { - "type": "SpecificResource", - "source": [ - { - "id": "https://example.org/iiif/3d/cameras/1", - "type": "PerspectiveCamera", - "label": {"en": ["Perspective Camera Pointed At Front of Cranium and Mandible"]}, - "fieldOfView": 50.0, - "near": 0.10, - "far": 2000.0 - } - ] - }, - "target": { - "type": "SpecificResource", - "source": [ - { - "id": "https://example.org/iiif/scene1", - "type": "Scene" - } - ], - "selector": [ - { - "type": "PointSelector", - "x": 0.0, "y": 0.15, "z": 0.75 - } - ] - } - } - ] - } - } - } -} -``` - -In a storytelling or exhibition scenario, the non-painting `annotations` might be carrying informative text, or even rich HTML bodies. They can be considered to be _steps_ in the story. The use of `scope` allows a precise storytelling experience to be specified, including: - - - providing a specific viewpoint for each step of the narrative (or even a choice of viewpoints) - - modifying the lighting of the Scene for each step, for example shining a spotlight on a point of interest - - hiding parts of the Scene for a step - - introducing additional models at a particular step - - (and many more!) - -Use of `scope` is permitted in annotations on any Container type, not just Scenes. For example, a 2D narrative around a Canvas might show or hide different `painting` annotations at each step. - - +In many complex 3D Scenes, it may not be clear what or how to look at a particular point of interest even when the commenting annotation targets a particular point. The view may be occluded by parts of the model, or other models in the Scene. In this case we only want that camera to be used for looking at the Mandibular tooth. For now, it has been given the behavior `hidden`. +## Activating Annotations +Annotations with the motivation `activating` are referred to as _activating_ annotations, and are used to link a resource that triggers an action with the resource(s) to change, enable or disable. In the above case the `target` of the activating annotation could be one of the commenting annotations, for which a user might click a corresponding UI element. In other scenarios the `target` could be the painting annotation of a 3D model, or an annotation that targets part of a model, or a region of a Canvas, or a point or segment of a Timeline, or any other annotation that a user could interact with (in whatever manner) to trigger an event. The `body` of the annotation is the resource that is then activated - for example, a Camera which then becomes the active viewport. +`target` variations -## The `sequence` behavior - -// Is this right? Language... +- user "walks into a room" +- AV scrub bar reaches time t1 +- user interacts with a model +- user touches a face in a painting -While all AnnotationPage `items` are inherently ordered, an Annotation Page with the behavior `sequence` is explicitly a narrative, and clients should prevent (dissuade) users from jumping about. The presence of `sequence` affects the way a client should interpret the `reset` property described below. +We can add an additional `activating` annotation to the existing annotations, to connect them to the resources activated. -## Content States on Manifests +Activating annotations are provided in a Container's `annotations` property. They can be mixed in with the commenting (or other interactive annotations) they target, or they can be in a separate AnnotationPage. The client should evaluate all the activating annotations it can find. -When an annotation with the motivation `contentState` is provided via the `annotations` property of a Manifest, rather than contextually via `scope`, it is assumed to be generally available for selection by the user at any time. A client may present such as annotations as a menu of views, allowing arbitrary jumping into any Scene (or Canvas or Timeline) from any other point. +> recommend they are inline in the manifest? -// Is there some overlap here with Range? +> use cases for loading in new pages of annos later - activate the French translations -## Processing Content States in Scopes: reset -When a Content State is applied to a Container such as a Scene, it is assumed to be a "diff" - for example if 3 cameras and 4 lights are already present in the Scene, and a Content State asserts a single new Camera, the default behavior is to add this fourth Camera to the Scene and leave the existing resources as they are. - -The client should reset the Container to its original state before applying the diff operation. However, for narratives that cumulatively build a Scene this may lead to excessively verbose Manifests. When moving through the items of an Annotation page with the behavior `linear-nav`, the Container is not reset and the diff is cumulative; modifications from one `scope` persist into the next. This can be overridden for an individual annotation with the behavior `reset`: - -`linear-nav` does not inherit, but `reset` does. `reset` is the default behavior for AnnotationPage and Annotation. - -```json -{ - // a story, but we reset the Scene for each content state - "type": "AnnotationPage", - "behavior": ["linear-nav"], - "items": [ - - ] -} -``` - -```json +```jsonc { - // a story, but the application of content-state is cumulative - // what if you go backwards? - // What if other content states have been applied before you start the story? - // **** Is linear-nav implicitly reset on the first anno? - // Should all anno pages reset the Scene - no because I might send you a view with a content state - "type": "AnnotationPage", - "behavior": ["linear-nav", "no-reset"], // no-reset is a behavior for the annos not the page - "items": [ - + "id": "https://example.org/iiif/3d/anno9", + "type": "Annotation", + "motivation": ["activating"], + "target": [ + { + "id": "https://example.org/iiif/3d/commenting-anno-for-mandibular-tooth", + "type": "Annotation" + } + ], + "body": [ + { + "id": "https://example.org/iiif/3d/anno-that-paints-desired-camera", + "type": "Annotation" + } ] } ``` -```jsonc -[ - { - "id": "https://.../step-1", - "type": "Annotation", - "motivation": ["contentState"] - // if you really want to ensure that any ad-hoc applied content states are wiped out, - // then put an explicit reset here. But usually, we can start the nav by applying - // the content state in the scope to the Scene without worrying that someone has - // modified the Scene already. - }, - // .... - +The pattern is similar to that for hotspot linking (ref) - { - "id": "https://.../step-20", - "type": "Annotation", - "motivation": ["contentState"] - // inherit no-reset - }, - - - { - // However, this particular step (step 37) needs to reset the Scene to the initial state. - "id": "https://.../step-37", - "type": "Annotation", - "motivation": ["contentState"], - "behavior": ["reset"] - } -] -``` +In a storytelling or exhibition scenario, the non-painting `annotations` might be carrying informative text, or even rich HTML bodies. They can be considered to be _steps_ in the story. The use of activating annotations allows a precise storytelling experience to be specified, including: -behavior: `linear-nav` -- cannot jump around only one step forward/back - -client _MUST_ support forward nav and _MAY_ support backward nav - -If you go backwards from step n in a linear-nav, **and** you have applied one or more content states during the linear-nav, the client _MAY_ reset the whole Scene to default condition and then replay the linear-nav up to step n-1. (client doesn't have to maintain infinite undo history) + - providing a specific viewpoint for each step of the narrative (or even a choice of viewpoints) + - modifying the lighting of the Scene for each step, for example shining a spotlight on a point of interest + - hiding models in the Scene at a particular step + - showing additional models at a particular step +As in the above example, all the other annotations referred to by the activating annotations `target` and `body` properties are already present in the Scene from the beginning. Initially, many of them may have the behavior `hidden`, invisible until activated. -Before applying the content state to the Scene, the client should reset the Scene to its original state as provided by the Manifest. +The `body` is anything that is can be activated: -// I am assuming reset is always true except in `linear-nav` - otherwise it's completely unpredictable!! or is it... arbitrary navigation, state provided by initialization content states, etc... +- Camera: if "hidden" the behavior is removed, and (crucially) this Camera becomes the viewport. +- AnimationSelector: A named animation within a model is played. +- (anything else yet?) -## Contribute additional information permanently -Rerum inbox scenario - should be covered in CS2 protocol +## Showing and hiding resources -## activating - animation and interactions +An activating annotation has two additional optional properties: -Annotations with the motivation `activating` are referred to as _activating_ annotations. +* `enables`: For each annotation in the value, remove the 'hidden' behavior if it has it. +* `disables`: For each annotation in the value, add the 'hidden' behavior if it does not have it. -There are two uses of `activating` annotations: +Hidden resources cannot be active or activated. If the values are the `id` properties of painting resources that paint models, they are hidden or made visible. If Lights, they are turned on. -### Triggering a content state +### Example: a light switch -An activating annotation links a Painting Annotation to a content state. When a user interacts with the Painting Annotation - whether through clicking it, tapping it, or other client-specific behaviors - the linked content state should be processed to modify the Scene or other Container, as in the previous examples. The Painting Annotation is the target of the activating annotation, and the content state is the body value. Only one content state may be specified in the body array, but the body array may include a `TextualBody` to provide a label for the interaction. The pattern is the same as for the `linking` motivation, but rather than the client opening a new browser window on the resource specified in the `body`, it applies the modification provided by the Content State. +* Initially, a model of a light switch is painted into the Scene. A PointLight is also painted, but with the `behavior` "hidden", which means it is inactive (i.e., off). A commenting annotation with the text "Click the switch to turn the light on or off" targets the light switch. An activating annotation targets the commenting annotation, so that user interaction with the commenting annotation will trigger the activating annotation. This activating annotation has no `body`, but it does have `enables` with values that are the `id` properties of the painting annotation for the light switch model, and the activating annotation that turns the light off. It also has a `disables` with the value of its own `id` - i.e., it disables _itself_. A further activating annotation has the opposite effect. Initially this has the `behavior` "hidden" - which means it is inactive. It also targets the commenting annotation, but has no effect while hidden. +* When the user interacts with the light switch model, the client processes any activating annotations that target it and are not hidden. In this case, the first activating annotation is triggered because while both target the switch, only the first is not hidden. This activation `enables` the light (i.e., removing its "hidden" `behavior` and therefore turning it on) and the other activating annotation, and `disables` itself. +* If the user clicks the light again, the client again processes any activating annotations that target it and are not hidden. This time the second annotation is the active one - and it `disables` the light (turning it off) and itself, and enables the first activating annotation again. +* Subsequent clicks simply alternate between these two states, indefinitely. -The activating annotation is provided in a Container's `annotations` property. In this (contrived for brevity) example, if the user clicks the mandible model, the Scene background changes color: ```jsonc { - "id": "https://example.org/iiif/3d/activating.json", - "type": "Manifest", - "label": { "en": ["Whale Cranium and Mandible with Dynamic Commenting Annotations and Custom Per-Anno Views"] }, - "items": [ - { - "id": "https://example.org/iiif/scene1/scene-with-activation", - "type": "Scene", - "label": { "en": ["A Scene Containing a Whale Cranium and Mandible"] }, - "items": [ + "@context": "http://iiif.io/api/presentation/4/context.json", + "id": "https://example.org/iiif/manifest/switch", + "type": "Manifest", + "label": { "en": [ "Light switch" ] }, + "items": [ { - "id": "https://example.org/iiif/scene1/page/p1/1", - "type": "AnnotationPage", - "items": [ - { - "id": "https://example.org/iiif/3d/painting-anno-for-mandible", - "type": "Annotation", - "motivation": ["painting"], - "body": { - "id": "https://raw.githubusercontent.com/IIIF/3d/main/assets/whale/whale_mandible.glb", - "type": "Model" - }, - "target": { - // SpecificResource with PointSelector - } - } - ], - "annotations": [ - { - "id": "https://example.org/iiif/scene1/page/activators", - "type": "AnnotationPage", - "items": [ + "id": "https://example.org/iiif/scene/switch/scene-1", + "type": "Scene", + "items": [ { - "id": "https://example.org/iiif/3d/anno2", - "type": "Annotation", - "motivation": ["activating"], - "body": [ - { - "type": "TextualBody", - "value": "A label for the activation may be provided as a TextualBody" - }, - { - // A body where the type is a IIIF Resource (eg Scene) is the Content State to apply - "id": "https://example.org/iiif/scene1/scene-with-activation", - "type": "Scene", - "backgroundColor": "#FF99AA" - } - ], - "target": { - "id": "https://example.org/iiif/3d/painting-anno-for-mandible", - "type": "Annotation" - } + "id": "https://example.org/iiif/scene/switch/scene-1/painting-annotation-pages/1", + "type": "AnnotationPage", + "items": [ + { + "id": "https://example.org/iiif/painting-annotation/lightswitch-1", + "type": "Annotation", + "motivation": ["painting"], + "label": { + "en": ["A light switch"] + }, + "body": { + "id": "https://example.org/iiif/model/models/lightswitch.gltf", + "type": "Model" + }, + "target": "https://example.org/iiif/scene/switch/scene-1" + }, + { + "id": "https://example.org/iiif/scene/switch/scene-1/lights/point-light-4", + "type": "Annotation", + "motivation": ["painting"], + "body": { + "id": "https://example.org/iiif/scene/switch/scene-1/lights/4/body", + "type": "PointLight" + }, + "target": { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1", + "selector": [ + { + "type": "PointSelector", + "x": 5, "y": 5, "z": 5 + } + ] + }, + "behavior": ["hidden"] + } + ] } - ] - } - ] - } - ] - } - ] -} -``` - - - -```json -{ - "items": [ - { - "id": "https://example.org/iiif/3d/anno2", - "type": "Annotation", - "motivation": ["activating"], - "body": [ - { - "type": "TextualBody", - "value": "A label for the activation may be provided as a TextualBody" - } - ], - "target": { - "source": { - "id": "https://example.org/iiif/3d/painting-anno-for-mandible", - "type": "Annotation" - }, - "scope": { - "type": "Annotation", - "motivation": ["contentState"], - "target": { - // A body where the type is a IIIF Resource (eg Scene) is the Content State to apply - "id": "https://example.org/iiif/scene1/scene-with-activation", - "type": "Scene", - "backgroundColor": "#FF99AA" - } + ], + "annotations": [ + { + "id": "https://example.org/iiif/scene/switch/scene-1/annos/1", + "type": "AnnotationPage", + "items": [ + { + "id": "https://example.org/iiif/scene/switch/scene-1/annos/1/switch-comment-0", + "type": "Annotation", + "motivation": [ + "commenting" + ], + "body": { + "type": "TextualBody", + "value": "Click the switch to turn the light on or off" + }, + "target": "https://example.org/iiif/painting-annotation/lightswitch-1" + }, + { + "id": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2", + "type": "Annotation", + "motivation": [ + "activating" + ], + "target": "https://example.org/iiif/painting-annotation/lightswitch-1", + "disables": [ + "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2" + ], + "enables": [ + "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-off-3", + "https://example.org/iiif/scene/switch/scene-1/lights/point-light-4" + ] + }, + { + "id": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-off-3", + "type": "Annotation", + "motivation": [ + "activating" + ], + "target": "https://example.org/iiif/painting-annotation/lightswitch-1", + "disables": [ + "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-off-3", + "https://example.org/iiif/scene/switch/scene-1/lights/point-light-4" + ], + "enables": [ + "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2" + ], + "behavior": ["hidden"] + } + ] + } + ] } - } - } - ] + ] } ``` -// Can you put activating annotations in `manifest.annotations`? They would work there too, you have all the information. - - - ### Triggering a named animation in a model -> toggles: anno supplies the label. This anno's `toggles` property lists id of an activating anno that activates the animation. - Sometimes a model file has inbuilt animations. While a description of these is outside the scope of IIIF, because it is 3D-implementation-specific, as long as there is a way to refer to a model's animation(s) by name, we can connect the animation to IIIF resources. -This pattern is similar to the above, except that: - - - There is no Content State in the `body`, but there _MUST_ be a TextualBody to label the interaction. (?? must?) - - The `target` selects a _named animation_ in the model. The `target` MUST be a SpecificResource, where the `source` is the Painting Annotation that paints the model, and the `selector` is of type `AnimationSelector` with the `value` being a string that corresponds to the animation in the model. - - The format of the `value` string is implementation-specific, and will depend on how different 3D formats support addressing of animations within models. The same model can be painted multiple times into the scene, and you might want to activate only one model's animation, thus we need to refer to the annotation that paints the model, not the model directly. +This pattern is also achieved with activating annotations, except that the body of the activating annotation references a _named animation_ in the model. The `body` MUST be a SpecificResource, where the `source` is the Painting Annotation that paints the model, and the `selector` is of type `AnimationSelector` with the `value` being a string that corresponds to the animation in the model. +The format of the `value` string is implementation-specific, and will depend on how different 3D formats support addressing of animations within models. The same model can be painted multiple times into the scene, and you might want to activate only one model's animation, thus we need to refer to the annotation that paints the model, not the model directly. ```jsonc @@ -2200,9 +2139,9 @@ This pattern is similar to the above, except that: "type": "AnnotationPage", "items": [ { - "id": "https://example.org/iiif/3d/box-opening-activating-anno", + "id": "https://example.org/iiif/3d/box-opening-commenting-anno", "type": "Annotation", - "motivation": ["activating"], + "motivation": ["commenting"], "body": [ { "type": "TextualBody", @@ -2210,13 +2149,30 @@ This pattern is similar to the above, except that: } ], "target": [ + { + "id": "https://example.org/iiif/3d/painting-anno-for-music-box", + "type": "Annotation" + } + ] + } + { + "id": "https://example.org/iiif/3d/box-opening-activating-anno", + "type": "Annotation", + "motivation": ["activating"], + "target": [ + { + "id": "https://example.org/iiif/3d/box-opening-commenting-anno", + "type": "Annotation" + } + ], + "body": [ { "type": "SpecificResource", "source": "https://example.org/iiif/3d/painting-anno-for-music-box", "selector": [ { - "type": "AnimationSelector", - "value": "open-the-lid" + "type": "AnimationSelector", + "value": "open-the-lid" } ] } @@ -2232,38 +2188,46 @@ This pattern is similar to the above, except that: } ``` -> Toggles example -```json +### Modifying resource properties + +Many Scene interaction use cases can be accomplished using the `enables` and `disables` properties to toggle the `"behavior": ["hidden"]`, and/or using activating annotations with bodies that can be _activated_: the examples above show a Camera and then an Animation being activated. Models in the Scene can also be shown and hidden via these properties. + +> when to use enables and when to use the `body` of the activating anno - are they equivalent for, say, a hidden model: enable it, activate it - interchangeable? + +For some interactions it is necessary to do more than show or hide or "activate" resources, by changing just `"behavior": ["hidden"]`. Other properties can also be changed via the JSON Patch mechanism. + +> **This is a clear distinction like level0, level1 - a client can simply choose not to support arbitrary patching.** + +> Be clear that you still need to have all the patchable resources present from the start, you can't pull them in later. + +In the following simple example, the background color of the Scene is changed: + + +```jsonc { - "id": "https://example.org/iiif/3d/activating-animation.json", + "id": "https://example.org/iiif/3d/property-change.json", "type": "Manifest", - "label": { "en": ["Music Box with lid that opens as an internal animation"] }, + "label": { "en": ["Whale Mandible"] }, "items": [ { - "id": "https://example.org/iiif/scene1/scene-with-activation-animation", + "id": "https://example.org/iiif/scene1/scene-with-color-change", "type": "Scene", - "label": { "en": ["A Scene Containing a Music Box"] }, + "label": { "en": ["A Scene Containing a Whale Mandible"] }, "items": [ { - "id": "https://example.org/iiif/scene-with-activation-animation/page/p1/1", + "id": "https://example.org/iiif/scene1/page/p1/1", "type": "AnnotationPage", "items": [ { - "id": "https://example.org/iiif/3d/painting-anno-for-music-box", + "id": "https://example.org/iiif/3d/painting-anno-for-mandible", "type": "Annotation", "motivation": ["painting"], "body": { - "id": "https://raw.githubusercontent.com/IIIF/3d/main/assets/music-box.glb", + "id": "https://raw.githubusercontent.com/IIIF/3d/main/assets/whale/whale_mandible.glb", "type": "Model" }, - "target": { - // SpecificResource with PointSelector - }, - "toggles": [ - // Clicking the box opens the lid - "https://example.org/iiif/3d/activating-anno-for-music-box" - ] + "target": "https://example.org/iiif/scene1/scene-with-color-change" } ], "annotations": [ @@ -2272,77 +2236,41 @@ This pattern is similar to the above, except that: "type": "AnnotationPage", "items": [ { - "id": "https://example.org/iiif/3d/activation-labelling-anno", + "id": "https://example.org/iiif/3d/color-change-commenting-anno", "type": "Annotation", - "motivation": ["commenting-maybe"], + "motivation": ["commenting"], "body": [ { "type": "TextualBody", - "value": "Click me to open the lid of the box" + "value": "Change the background color" } ], - "toggles": [ - // clicking the 'Click me' opens the lid - "https://example.org/iiif/3d/activating-anno-for-music-box" - ], - "target": [ - "https://example.org/iiif/3d/painting-anno-for-music-box" - ] - }, - { - "id": "https://example.org/iiif/3d/activating-anno-for-music-box", - "type": "Annotation", - "motivation": ["activating"], - "body": [ + "target": [ { - "type": "TextualBody", - "value": "Click me to open the lid of the box" + "id": "https://example.org/iiif/3d/painting-anno-for-mandible", // or the Scene? + "type": "Annotation" } - ], - "target": { - "type": "SpecificResource", - "source": "https://example.org/iiif/3d/painting-anno-for-music-box", - "selector": [ - { - "type": "AnimationSelector", - "value": "open-the-lid" - } - ], - "refinedBy": [ - // fragment time selector - ] - } + ] } - ] - } - ] - } - - - - - { - "id": "https://example.org/iiif/scene1/page/activators", - "type": "AnnotationPage", - "items": [ { - "id": "https://example.org/iiif/3d/box-opening-activating-anno", + "id": "https://example.org/iiif/3d/color-change-activating-anno", "type": "Annotation", "motivation": ["activating"], - "body": [ + "target": [ { - "type": "TextualBody", - "value": "Click the box to open the lid" + "id": "https://example.org/iiif/3d/color-change-commenting-anno", + "type": "Annotation" } ], - "target": [ + "body": [ { - "type": "SpecificResource", - "source": "https://example.org/iiif/3d/painting-anno-for-music-box", - "selector": [ + "type": "JSONPatch", + "patchTarget": "https://example.org/iiif/scene1/scene-with-color-change", + "value": [ { - "type": "AnimationSelector", - "value": "open-the-lid" + "op": "replace", + "path": "/backgroundColor", + "value": "#FF99AA" } ] } @@ -2356,28 +2284,22 @@ This pattern is similar to the above, except that: } ] } - - ``` -// TODO - -activating to apply a content state and activating to trigger a named animation - use of body and target... what if we want to click a painting anno to trigger the animation? -Can we ADD that to the target, alongside the SpecificResource with the AnimationSelector? - -if the `target` is an AnimationSelector, then the `body` can ONLY be TextualBody (or list of TextualBody)? - -There is a more general rule here! - -## reset - -See above... +## The `sequence` behavior +While all AnnotationPage `items` are inherently ordered, an Annotation Page with the `behavior` "sequence" is explicitly a narrative, and clients should prevent (dissuade) users from jumping about - the annotations, and the effects of them _activating_ other contents of the Container, are intended to be experienced in order and individually. Normally, a client might display all the comments in an AnnotationPage in a sidebar so they are all visible in the UI, but for an AnnotationPage with `behavior` "sequence" only show the currently active annotation text, and next and previous UI. +## Chains of activation +Chaining together activating annotations can then allow the implementation of, at least: +* Specific camera position to look at an Annotation +* Multi-step linear stories +* Animations, including as part of stories without disrupting the flow, and looping animations (they activate themselves) +* Interactive components such as light switches (enable/disable a light), jukeboxes (enable/disable Audio Emitter) diff --git a/source/presentation/4.0/model.md b/source/presentation/4.0/model.md index 79858e833..a3e56086a 100644 --- a/source/presentation/4.0/model.md +++ b/source/presentation/4.0/model.md @@ -1155,7 +1155,7 @@ TODO: Address https://github.com/IIIF/api/issues/2318 | `sequence` | Valid only on Ranges, where the Range is [referenced][prezi30-terminology] in the `structures` property of a Manifest. Ranges that have this behavior represent different orderings of the Containers listed in the `items` property of the Manifest, and user interfaces that interact with this order _SHOULD_ use the order within the selected Range, rather than the default order of `items`. Disjoint with `thumbnail-nav` and `no-nav`.| | `thumbnail-nav`{: style="white-space:nowrap;"} | Valid only on Ranges. Ranges that have this behavior _MAY_ be used by the client to present an alternative navigation or overview based on thumbnails, such as regular keyframes along a timeline for a video, or sections of a long scroll. Clients _SHOULD NOT_ use them to generate a conventional table of contents. Child Ranges of a Range with this behavior _MUST_ have a suitable `thumbnail` property. Disjoint with `sequence` and `no-nav`.| | `no-nav` | Valid only on Ranges. Ranges that have this behavior _MUST NOT_ be displayed to the user in a navigation hierarchy. This allows for Ranges to be present that capture unnamed regions with no interesting content, such as the set of blank pages at the beginning of a book, or dead air between parts of a performance, that are still part of the Manifest but do not need to be navigated to directly. Disjoint with `sequence` and `thumbnail-nav`.| -| `linear-nav` | FIXME: ... | +| `linear-nav` | FIXME: Obsolete? use `sequence`, now valid for AnnotationPage as well as Range | | | **Miscellaneous Behaviors** | | `hidden` | Valid on Annotation Collections, Annotation Pages, Annotations, Specific Resources, Lights, Cameras and Choices. If this behavior is provided, then the client _SHOULD NOT_ render the resource by default, but allow the user to turn it on and off. This behavior does not inherit, as it is not valid on Collections, Manifests, Ranges or Canvases. | | `reset` | Valid on Annotations with a scope property. FIXME: ... | From ea0df9c1b2909267156224e195b0c2c96a97f96b Mon Sep 17 00:00:00 2001 From: tomcrane Date: Tue, 28 Oct 2025 12:11:49 +0000 Subject: [PATCH 02/12] more reorganisation of p4 intro --- source/presentation/4.0/index.md | 116 ++++++++++++++++--------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index ca01ce798..0de2002a2 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -1088,32 +1088,60 @@ Properties: [backgroundColor](#model/backgroundColor), [lookAt](#model/lookAt), ## Use Case 6: Complex Scene +Annotations may use a type of Selector called a `PointSelector` to align the Annotation to a point within the Scene that is not the Scene's origin. PointSelectors have three spatial properties, `x`, `y` and `z` which give the value on that axis. They also have a temporal property `instant` which can be used if the Scene has a duration, which gives the temporal point in seconds from the start of the duration, the use of which is defined in the [section on Scenes with Durations](). -**Chessboard is a Canvas with image (not a 3D chessboard)** - -A Scene or a Canvas may be treated as a content resource, referenced or described within the `body` of an Annotation. As with models and other resources, the Annotation is associated with a Scene into which the Scene or Canvas is to be nested through an Annotation `target`. The content resource Scene will be placed within the `target` Scene by aligning the coordinate origins of the two scenes. Alternately, Scene Annotations may use `PointSelector` to place the origin of the resource Scene at a specified coordinate within the `target` Scene. - -**more than one model** - -**transforms for scale and rotation** -This (no units for scale) allows arbitrarily scaled models to be used, including very small or very large, without needing to deal with very small or very large values. If there is a correspondence to a physical scale, then this can be asserted using the physical dimensions pattern(fwd-ref-to-phys-dims). - -**Scene in Scene** +Example Annotation that positions a model at a point within a Scene: -**Exclude** +```json +{ + "id": "https://example.org/iiif/3d/anno1", + "type": "Annotation", + "motivation": ["painting"], + "body": { + "id": "https://example.org/iiif/assets/model1.glb", + "type": "Model" + }, + "target": { + "type": "SpecificResource", + "source": [ + { + "id": "https://example.org/iiif/scene1", + "type": "Scene" + } + ], + "selector": [ + { + "type": "PointSelector", + "x": -1.0, + "y": 0.0, + "z": 1.0 + } + ] + } +} +``` -**interactionMode** +Annotations may alternately use a type of Selector called a `WktSelector` to align the Annotation to a region with the Scene that is not the Scene's origin. WktSelectors have a single property, `value`, which is a string conforming to a WKT Linestring, LineStringZ, Polygon, or PolygonZ list of 2D or 3D coordinate points. Whether and how a region defined by a WktSelector may be translated to a single 2D or 3D coordinate point, for targeting or other purposes, is client-dependent. +Example Annotation that comments on a 3D polygon within a Scene: +``` +Todo add example +``` +### Chessboard is a Canvas with image (not a 3D chessboard) +A Scene or a Canvas may be treated as a content resource, referenced or described within the `body` of an Annotation. As with models and other resources, the Annotation is associated with a Scene into which the Scene or Canvas is to be nested through an Annotation `target`. The content resource Scene will be placed within the `target` Scene by aligning the coordinate origins of the two scenes. Alternately, Scene Annotations may use `PointSelector` to place the origin of the resource Scene at a specified coordinate within the `target` Scene. +### More than one model +**transforms for scale and rotation** +This (no units for scale) allows arbitrarily scaled models to be used, including very small or very large, without needing to deal with very small or very large values. If there is a correspondence to a physical scale, then this can be asserted using the physical dimensions pattern(fwd-ref-to-phys-dims). -## Merge the below into the examples or into model +### Scene in Scene -As with other containers in IIIF, Annotations are used to target the Scene to place content such as 3d models into the scene. Annotations are also used to add lights and cameras. A Scene can have multiple models, lights, cameras and other resources, allowing them to be grouped together. Scenes and other IIIF containers, such as Canvases, may also be embedded within Scenes, as described below in the nesting section [fwd-ref-to-nesting]. +Scenes and other IIIF containers, such as Canvases, may also be embedded within Scenes, as described below in the nesting section [fwd-ref-to-nesting]. ```json { @@ -1143,7 +1171,7 @@ A simple example painting one Scene into another: } ``` - +### More on Point and Fragment Selectors A content resource may be annotated into a Scene for a period of time by use of a PointSelector that is temporally scoped by a [FragmentSelector](https://www.w3.org/TR/annotation-model/#fragment-selector). The FragmentSelector has a `value` property, the value of which follows the [media fragment syntax](https://www.w3.org/TR/media-frags/#naming-time) of `t=`. This annotation pattern uses the `refinedBy` property [defined by the W3C Web Annotation Data Model](https://www.w3.org/TR/annotation-model/#refinement-of-selection). @@ -1230,6 +1258,13 @@ An Annotation may target a specific point in time using a PointSelector's `insta } ``` +**Give example of refinedBy ? e.g. WktSelector + Instant** + + +### Exclude + +### Time mode + The Annotation's [`timeMode` property](https://iiif.io/api/presentation/3.0/#timemode) can be used to indicate the desired behavior when the duration of the content resource that is not equal to the temporal region targeted by the annotation. It is an error to select a temporal region of a Scene that does not have a `duration`, or to select a temporal region that is not within the Scene's temporal extent. A Canvas or Scene with a `duration` may not be annotated as a content resource into a Scene that does not itself have a `duration`. @@ -1238,7 +1273,15 @@ It is an error to select a temporal region of a Scene that does not have a `dura An annotation that targets a Scene using a PointSelector without any temporal refinement implicitly targets the Scene's entire duration. -Audio and 3D +### interactionMode + + + + + + + +## Audio and 3D AmbientAudio (everywhere) @@ -1256,50 +1299,9 @@ hidden on audio = inaudible All resources that can be added to a Scene have an implicit (e.g. Lights, Cameras) or explicit (e.g. Models, Scenes), local coordinate space. If a resource does not have an explicit coordinate space, then it is positioned at the origin of its coordinate space. In order to add a resource with its local coordinate space into a Scene with its own coordinate space, these spaces must be aligned. This done by aligning the origins of the two coordinate spaces. -Annotations may use a type of Selector called a `PointSelector` to align the Annotation to a point within the Scene that is not the Scene's origin. PointSelectors have three spatial properties, `x`, `y` and `z` which give the value on that axis. They also have a temporal property `instant` which can be used if the Scene has a duration, which gives the temporal point in seconds from the start of the duration, the use of which is defined in the [section on Scenes with Durations](). - -Example Annotation that positions a model at a point within a Scene: - -```json -{ - "id": "https://example.org/iiif/3d/anno1", - "type": "Annotation", - "motivation": ["painting"], - "body": { - "id": "https://example.org/iiif/assets/model1.glb", - "type": "Model" - }, - "target": { - "type": "SpecificResource", - "source": [ - { - "id": "https://example.org/iiif/scene1", - "type": "Scene" - } - ], - "selector": [ - { - "type": "PointSelector", - "x": -1.0, - "y": 0.0, - "z": 1.0 - } - ] - } -} -``` - -Annotations may alternately use a type of Selector called a `WktSelector` to align the Annotation to a region with the Scene that is not the Scene's origin. WktSelectors have a single property, `value`, which is a string conforming to a WKT Linestring, LineStringZ, Polygon, or PolygonZ list of 2D or 3D coordinate points. Whether and how a region defined by a WktSelector may be translated to a single 2D or 3D coordinate point, for targeting or other purposes, is client-dependent. - - -Example Annotation that comments on a 3D polygon within a Scene: -``` -Todo add example -``` -## Give example of refinedBy ? e.g. WktSelector + Instant From 195ff1ed24911507d2300137fb7d533c54a29c18 Mon Sep 17 00:00:00 2001 From: tomcrane Date: Tue, 28 Oct 2025 15:29:19 +0000 Subject: [PATCH 03/12] operationd in JSONPatch --- source/presentation/4.0/index.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index 0de2002a2..94f99089a 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -2199,6 +2199,20 @@ Many Scene interaction use cases can be accomplished using the `enables` and `di For some interactions it is necessary to do more than show or hide or "activate" resources, by changing just `"behavior": ["hidden"]`. Other properties can also be changed via the JSON Patch mechanism. +```jsonc +{ + "type": "JSONPatch", + "patchTarget": "https://example.org/iiif/scene1/scene-with-color-change", // the Scene + "operations": [ + { + "op": "replace", + "path": "/backgroundColor", // path to the property being changed. + "value": "#FF99AA" + } + ] +} +``` + > **This is a clear distinction like level0, level1 - a client can simply choose not to support arbitrary patching.** > Be clear that you still need to have all the patchable resources present from the start, you can't pull them in later. @@ -2268,7 +2282,7 @@ In the following simple example, the background color of the Scene is changed: { "type": "JSONPatch", "patchTarget": "https://example.org/iiif/scene1/scene-with-color-change", - "value": [ + "operations": [ { "op": "replace", "path": "/backgroundColor", From 1a9326129d0bdece4d22c5bea964cfbc9cea2662 Mon Sep 17 00:00:00 2001 From: tomcrane Date: Tue, 28 Oct 2025 17:14:04 +0000 Subject: [PATCH 04/12] style expt --- source/presentation/4.0/index.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index 94f99089a..e080ffac2 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -35,6 +35,12 @@ hero: image: '' --- + + # Status of this Document {:.no_toc} __This Version:__ {{ page.major }}.{{ page.minor }}.{{ page.patch }}{% if page.pre != 'final' %}-{{ page.pre }}{% endif %} @@ -2197,7 +2203,7 @@ Many Scene interaction use cases can be accomplished using the `enables` and `di > when to use enables and when to use the `body` of the activating anno - are they equivalent for, say, a hidden model: enable it, activate it - interchangeable? -For some interactions it is necessary to do more than show or hide or "activate" resources, by changing just `"behavior": ["hidden"]`. Other properties can also be changed via the JSON Patch mechanism. +For some interactions it is necessary to do more than show or hide or "activate" resources, by changing just `"behavior": ["hidden"]`. Other properties can also be changed via the [JSON Patch](link) mechanism. ```jsonc { From 50988fe04ff2c7e1330d11905282ae1d29f01cdf Mon Sep 17 00:00:00 2001 From: tomcrane Date: Tue, 28 Oct 2025 17:14:25 +0000 Subject: [PATCH 05/12] style expt --- source/presentation/4.0/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index e080ffac2..a6ddcff88 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -36,7 +36,7 @@ hero: --- From 0ccc805514b2c6c0d9864fbad967a210e86e4b6a Mon Sep 17 00:00:00 2001 From: tomcrane Date: Tue, 28 Oct 2025 17:20:35 +0000 Subject: [PATCH 06/12] style expt --- source/presentation/4.0/index.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index a6ddcff88..196dd13b1 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -36,9 +36,17 @@ hero: --- # Status of this Document From a182c219a773a5fab54b2df858e21af320172ff4 Mon Sep 17 00:00:00 2001 From: tomcrane Date: Tue, 28 Oct 2025 17:27:09 +0000 Subject: [PATCH 07/12] style expt --- source/presentation/4.0/index.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index 196dd13b1..376d6f88b 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -36,10 +36,14 @@ hero: ---