diff --git a/.gitignore b/.gitignore index b6d335aca..8c3f0b0e2 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ /http-tests/out /fuseki .claude/scheduled_tasks.lock +/cli/target diff --git a/README.md b/README.md index b1f153c76..8198c5915 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ We are building LinkedDataHub primarily for: What makes LinkedDataHub unique is its completely _data-driven architecture_: applications and documents are defined as data, managed using a single generic HTTP API and presented using declarative technologies. The default application structure and user interface are provided, but they can be completely overridden and customized. Unless a custom server-side processing is required, no imperative code such as Java or JavaScript needs to be involved at all. +XHTML documents can be edited in-place using a built-in RDFa-aware rich text editor — annotations link selected text directly to Knowledge Graph terms, embedding machine-readable RDF statements in the markup without leaving the page. + **Follow the [Get started](https://atomgraph.github.io/LinkedDataHub/linkeddatahub/docs/get-started/) guide to LinkedDataHub.** The setup and basic configuration sections are provided below and should get you running. **LinkedDataHub is also available as a free AWS Marketplace product!** AWS Marketplace diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/css/bootstrap.css b/src/main/webapp/static/com/atomgraph/linkeddatahub/css/bootstrap.css index 0e19e5ebd..a2a07d9e1 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/css/bootstrap.css +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/css/bootstrap.css @@ -44,6 +44,7 @@ ul.dropdown-menu ul { margin: 0; } .action-bar #doc-controls { text-align: right; padding-top: 5px; } .action-bar #doc-controls .btn-edit { margin-top: -5px; margin-left: 10px; } .action-bar p.alert { margin-bottom: 0; } +body:has(#edit-toolbar) .action-bar { border-bottom: 1px solid #aaa; } .nav-header.btn { color: inherit; } .btn.btn-primary.create-action { background-image: url('../icons/ic_note_add_white_24px.svg'); background-position: 12px center; background-repeat: no-repeat; padding-left: 40px; /* height: 30px; */ } .btn.create-action, a.create-action { background-image: url('../icons/ic_note_add_black_24px.svg'); background-position: 12px center; background-repeat: no-repeat; padding-left: 40px; /* height: 30px; */ } @@ -152,6 +153,8 @@ h2.item-logo { background-image: url('../icons/file.svg'); background-position: .modal-footer { position: sticky; bottom: 0; background-color: white; z-index: 1060; } .modal-first-time-message { margin-left: 0; width: 80%; left: 10%; } .modal-first-time-message .hero-unit { margin-bottom: 0; } +/* Annotation overlay */ +#rdfa-editor-overlay { position: absolute; min-width: 360px; max-width: 520px; z-index: 10001; } .control-group.endpoint-classes ul { max-height: 20em; overflow: auto; } .nav-tabs > li > a, .pager > li > a.active { cursor: pointer; } .pager > li.previous > a { text-align: right; font-size: 0; color: transparent; background-image: url('../icons/ic_navigate_before_black_24px.svg'); background-position: center center; background-repeat: no-repeat; width: 64px; height: 24px; } @@ -180,8 +183,11 @@ button.add-typeahead { width: 219px; } .description { display: none; } .content .dl-horizontal { max-height: 35em; overflow: auto; } div[typeof="https://w3id.org/atomgraph/linkeddatahub#XHTML"] .main { font-size: larger; line-height: 25px; } +div[typeof="https://w3id.org/atomgraph/linkeddatahub#XHTML"]:not(:has(form)) .main { cursor: pointer; } +div[typeof="https://w3id.org/atomgraph/linkeddatahub#XHTML"] .btn-edit { display: none; } div[typeof="https://w3id.org/atomgraph/linkeddatahub#XHTML"] .form-horizontal .control-label { display: none; } div[typeof="https://w3id.org/atomgraph/linkeddatahub#XHTML"] .form-horizontal .controls { margin-left: 0; } +div[typeof="https://w3id.org/atomgraph/linkeddatahub#XHTML"] form fieldset { border: none; } .span12.progress .row-block-controls { background-color: #e9ecef; } .span12.progress .bar { height: 20px; } /* .content .btn.add-typeahead { margin-top: -10px; } */ @@ -210,4 +216,18 @@ object { display: block; margin: auto; max-height: 400px; overflow: hidden; widt .ol-overlay-container { width: 60%; max-height: 75%; background-color: white; overflow: auto; } .ol-overlay-container img { max-width: 100%; } /* SVG graph specific */ -circle { cursor: move; } \ No newline at end of file +circle { cursor: move; } +/* RDFa editor specific */ +.editor-bar { position: sticky; top: calc(var(--action-bar-top, 51px) + 50px); z-index: 998; background: #dfdfdf; padding: 0; box-shadow: none; display: flex; align-items: center; } +#edit-toolbar { order: -1; margin-right: auto; display: flex; gap: 6px; align-items: center; } +#edit-toolbar button, #edit-toolbar select { padding: 4px 8px; border: 1px solid #ccc; border-radius: 4px; background: white; font-size: 13px; font-family: inherit; cursor: pointer; margin-top: auto; margin-bottom: auto; } +#edit-toolbar button:disabled { opacity: 0.4; cursor: default; } +#edit-toolbar button:hover { background: #f5f5f5; } +#edit-toolbar button[aria-pressed="true"] { background: #e3f0ff; border-color: #6aa3e0; color: #1a5fb4; } +#edit-toolbar .tb-group { display: flex; gap: 6px; align-items: center; } +#edit-toolbar .tb-group + .tb-group { border-left: 1px solid #e0e0e0; padding-left: 6px; } +#edit-toolbar .tb-group.table-ops.active { background: #e3f0ff; border-radius: 4px; } +.edit-dialog { background: white; border: 1px solid #ccc; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,.2); padding: 12px; min-width: 280px; z-index: 10000; } +.edit-dialog .rdfa-editor-ui .action-buttons { margin-top: 10px; } +.edit-dialog .rdfa-editor-ui .checkbox-label { display: inline-flex; gap: 6px; align-items: center; font-weight: 400; margin-top: 8px; } +.editor-bar:not(:has(#edit-toolbar)), body:not(:has(.rdfa-editor-content)) .editor-bar, body:not(:has(.rdfa-editor-content)) #rdfa-editor-breadcrumb { display: none; } diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/css/rdfa-editor.css b/src/main/webapp/static/com/atomgraph/linkeddatahub/css/rdfa-editor.css new file mode 100644 index 000000000..bbae2296f --- /dev/null +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/css/rdfa-editor.css @@ -0,0 +1,601 @@ +/* + * rdfa-editor.css - the RDFa editor's UI contract: annotation overlay, edit + * dialogs, toolbar, block chrome and drag-and-drop marks, ToC drawer, + * breadcrumb bar, lint markers, RDFa highlighting and the output modal. + * + * Host page requirements: include this stylesheet and give the editable + * container (.rdfa-editor-content) enough left padding (>= 2.5em) for the block drag + * handles, which render in the gutter outside each block's box. + */ + +/* RDFa Visual Styling - Subtle dashed borders */ + .rdfa-editor-content *[property] { + border: 1px dashed #4caf50; + padding: 1px 2px; + cursor: pointer; + transition: background-color 0.2s, border-color 0.2s; + } + +.rdfa-editor-content *[property]:hover { + background-color: rgba(76, 175, 80, 0.1); + border-color: #2e7d32; + } + +.rdfa-editor-content *[typeof] { + border: 1px dashed #9c27b0; + padding: 1px 2px; + cursor: pointer; + transition: background-color 0.2s, border-color 0.2s; + } + +.rdfa-editor-content *[typeof]:hover { + background-color: rgba(156, 39, 176, 0.1); + border-color: #7b1fa2; + } + +.rdfa-editor-content *[about] { + border: 1px dashed #2196f3; + padding: 1px 2px; + cursor: pointer; + transition: background-color 0.2s, border-color 0.2s; + } + +.rdfa-editor-content *[about]:hover { + background-color: rgba(33, 150, 243, 0.1); + border-color: #1976d2; + } + +.rdfa-editor-content *[resource] { + border: 1px dashed #ff9800; + padding: 1px 2px; + cursor: pointer; + transition: background-color 0.2s, border-color 0.2s; + } + +.rdfa-editor-content *[resource]:hover { + background-color: rgba(255, 152, 0, 0.1); + border-color: #f57c00; + } + +/* Invalid selection flash animation */ + @keyframes selection-flash { + 0% { background-color: transparent; } + 25% { background-color: rgba(244, 67, 54, 0.4); } + 50% { background-color: rgba(244, 67, 54, 0.6); } + 75% { background-color: rgba(244, 67, 54, 0.4); } + 100% { background-color: transparent; } + } + +.invalid-selection-flash { + animation: selection-flash 0.6s ease-in-out 2; + } + +.rdfa-editor-content > * { + position: relative; + } + +/* nested draggable blocks position their own handle (chrome is their first + child); handles stack per nesting level, each at its block's own indent */ + .rdfa-editor-content *:has(> [data-role="chrome"]) { + position: relative; + } + +/* invisible gutter strip: keeps the block hovered while the pointer + travels to the drag handle, which sits outside the block box */ + .rdfa-editor-content > *::before { + content: ''; + position: absolute; + left: -2.25em; + top: 0; + bottom: 0; + width: 2.25em; + } + +/* nested blocks get a narrower gutter, inside the parent's indentation */ + .rdfa-editor-content > * *:has(> [data-role="chrome"])::before { + content: ''; + position: absolute; + left: -1.5em; + top: 0; + bottom: 0; + width: 1.5em; + } + +/* a blockquote has no intrinsic child indentation, so its handle and its first + block's handle would coincide; the editing view indents quote content to give + each nesting level its own gutter column (lists and cells indent natively). + The quote bar (Markdown/Bootstrap convention) makes the container visible - + a normalized blockquote > p would otherwise read as a plain paragraph */ + .rdfa-editor-content blockquote { + padding-left: 2em; + border-left: 4px solid #e0e0e0; + color: #616161; + } + +/* the editing-DOM run wrapper (mixed flow content, e.g.
  • text