@uniweb/projections 0.3.7 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +1 -1
- package/src/markdown.js +66 -1
- package/src/search/generate.js +1 -1
- package/src/search/index.js +1 -1
- package/src/search/{collections.js → records.js} +23 -1
package/package.json
CHANGED
package/src/index.js
CHANGED
package/src/markdown.js
CHANGED
|
@@ -100,6 +100,71 @@ function collectSection(section, blocks, includeChildren, ancestorAnchor) {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Replace `inset_placeholder` nodes with the author's own words.
|
|
105
|
+
*
|
|
106
|
+
* ## Why this is not "restore the inset"
|
|
107
|
+
*
|
|
108
|
+
* An inset is `` — an author's caption plus a
|
|
109
|
+
* FOUNDATION COMPONENT to render it with. The build splits them: the caption and
|
|
110
|
+
* params go to the section's `insets[]`, and the body keeps an
|
|
111
|
+
* `inset_placeholder` carrying only `{ refId, embedKind }`.
|
|
112
|
+
*
|
|
113
|
+
* ⛔ **`@Diagram` must never reach this output.** A component name is a rendering
|
|
114
|
+
* assignment, and this package's whole property is that a projection is of the
|
|
115
|
+
* SITE — identical under a swapped foundation. Emitting it here would break the
|
|
116
|
+
* same rule that keeps `type:` and params out (see the package's README/notes on
|
|
117
|
+
* why the exclusions are load-bearing rather than tidy-up).
|
|
118
|
+
*
|
|
119
|
+
* ⭐ **But the caption IS site content** — the author wrote it, and an agent
|
|
120
|
+
* retrieving this page should read it. So the placeholder becomes its title, as
|
|
121
|
+
* plain text, and nothing else.
|
|
122
|
+
*
|
|
123
|
+
* ⚠️ Before this, `proseMirrorToMarkdown` had no serializer for the node and
|
|
124
|
+
* dropped it with a warning per build — *"this is a tracked capability gap"*. It
|
|
125
|
+
* was: every inset caption was missing from every agent-facing page.
|
|
126
|
+
*
|
|
127
|
+
* @param {Object} content - the section's ProseMirror document
|
|
128
|
+
* @param {Array} insets - the section's `insets[]` (`{ refId, title }`)
|
|
129
|
+
* @returns {Object} content with placeholders resolved to text
|
|
130
|
+
*/
|
|
131
|
+
function resolveInsetCaptions(content, insets) {
|
|
132
|
+
if (!content?.content?.length) return content
|
|
133
|
+
// No insets array → nothing to resolve against. Dropping is then still the only
|
|
134
|
+
// option, but it is silent: the caption is genuinely not reachable from here.
|
|
135
|
+
const titleByRef = new Map(
|
|
136
|
+
(Array.isArray(insets) ? insets : [])
|
|
137
|
+
.filter((i) => i && typeof i.refId === 'string' && i.title)
|
|
138
|
+
.map((i) => [i.refId, String(i.title)])
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
// ⛔ THE REPLACEMENT'S SHAPE DEPENDS ON WHERE IT SITS, and getting this wrong
|
|
142
|
+
// fails SILENTLY IN THE WORSE DIRECTION: a bare text node at block level is not
|
|
143
|
+
// serializable, so the caption vanishes exactly as before — but the warning that
|
|
144
|
+
// used to announce it is gone. Measured while writing this: the first version
|
|
145
|
+
// emitted text unconditionally, removed the warning, and restored nothing.
|
|
146
|
+
const TEXTBLOCKS = new Set(['paragraph', 'heading'])
|
|
147
|
+
|
|
148
|
+
const visit = (nodes, inline) =>
|
|
149
|
+
nodes.flatMap((node) => {
|
|
150
|
+
if (!node) return []
|
|
151
|
+
if (node.type === 'inset_placeholder') {
|
|
152
|
+
const title = titleByRef.get(node.attrs?.refId)
|
|
153
|
+
// An inset with no caption contributes no author text — drop it, and do
|
|
154
|
+
// so quietly: there is nothing a reader is missing.
|
|
155
|
+
if (!title) return []
|
|
156
|
+
const text = { type: 'text', text: title }
|
|
157
|
+
return inline ? [text] : [{ type: 'paragraph', content: [text] }]
|
|
158
|
+
}
|
|
159
|
+
if (Array.isArray(node.content)) {
|
|
160
|
+
return [{ ...node, content: visit(node.content, TEXTBLOCKS.has(node.type)) }]
|
|
161
|
+
}
|
|
162
|
+
return [node]
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
return { ...content, content: visit(content.content, false) }
|
|
166
|
+
}
|
|
167
|
+
|
|
103
168
|
/**
|
|
104
169
|
* @param {Object} section
|
|
105
170
|
* @returns {string}
|
|
@@ -107,5 +172,5 @@ function collectSection(section, blocks, includeChildren, ancestorAnchor) {
|
|
|
107
172
|
function serializeSectionContent(section) {
|
|
108
173
|
const content = section?.content
|
|
109
174
|
if (!content?.content?.length) return ''
|
|
110
|
-
return proseMirrorToMarkdown(content).trim()
|
|
175
|
+
return proseMirrorToMarkdown(resolveInsetCaptions(content, section?.insets)).trim()
|
|
111
176
|
}
|
package/src/search/generate.js
CHANGED
|
@@ -85,7 +85,7 @@ export function generateSearchIndex(siteContent, options = {}) {
|
|
|
85
85
|
* describe *where the index lives* — only whether it serves queries.
|
|
86
86
|
*
|
|
87
87
|
* @param {Object} pagesIndex - Result of {@link generateSearchIndex}
|
|
88
|
-
* @param {Object[]} [collectionIndexes] - Results of `
|
|
88
|
+
* @param {Object[]} [collectionIndexes] - Results of `generateRecordSearchIndex`
|
|
89
89
|
* @returns {Object} One index carrying every entry
|
|
90
90
|
*/
|
|
91
91
|
export function mergeSearchIndexes(pagesIndex, collectionIndexes = []) {
|
package/src/search/index.js
CHANGED
|
@@ -63,7 +63,29 @@ const isPrimitive = (v) =>
|
|
|
63
63
|
*/
|
|
64
64
|
const DISPLAY_VALUE_MAX = 200
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Search-index entries for one named group of records.
|
|
68
|
+
*
|
|
69
|
+
* ⛔ RENAMED from `generateCollectionIndex` — 2026-08-27, and the old name was
|
|
70
|
+
* wrong in two ways that mattered.
|
|
71
|
+
*
|
|
72
|
+
* 1. **"Collection" is FRAMEWORK'S build concept** — a named set our build
|
|
73
|
+
* compiles to one file. The live lane has no such thing: a host calls this
|
|
74
|
+
* with records it fetched from a folder, so the name put our build
|
|
75
|
+
* vocabulary into a function whose main caller is a host. `record` is true on
|
|
76
|
+
* both lanes and is what the argument actually holds.
|
|
77
|
+
* 2. **It never said `search`** — every sibling here does (`generateSearchIndex`,
|
|
78
|
+
* `mergeSearchIndexes`, `extractSearchContent`), and "collection index" reads
|
|
79
|
+
* as an index OF collections or a collection's own listing. It is neither: it
|
|
80
|
+
* is a search index derived FROM records.
|
|
81
|
+
*
|
|
82
|
+
* ⚠️ The RESULT still carries the old vocabulary — `type: 'collection'`,
|
|
83
|
+
* `collection: name`, and `id: "collection:<name>:<slug>"`. That is a data shape
|
|
84
|
+
* with live consumers (`kit`'s endpoint search provider and hosting's search both
|
|
85
|
+
* read `entry.collection`), so it is a separate, larger decision than this rename
|
|
86
|
+
* and is deliberately NOT bundled into it.
|
|
87
|
+
*/
|
|
88
|
+
export function generateRecordSearchIndex(name, config, collectionData, locale) {
|
|
67
89
|
// ⛔ NO DEFAULT FIELD LIST. This was `|| ['title']` — a claim about someone
|
|
68
90
|
// else's schema, and wrong for any collection without a `title` (a `people`
|
|
69
91
|
// collection has `name`, a `products` one has `label`). The failure was
|