Skip to content

Commit 3b9e014

Browse files
authored
Merge pull request #112 from aspiers/lexicon-sync
2 parents 8fd8896 + 060516a commit 3b9e014

9 files changed

Lines changed: 979 additions & 20 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@hypercerts-org/sdk-core": minor
3+
---
4+
5+
Add comprehensive documentation and tests for collection avatar and banner images
6+
7+
Collections and projects now support avatar (thumbnail/icon) and banner (header/cover) images. Images can be provided as
8+
Blobs for upload or as URI strings for external references.
9+
10+
**What's Included:**
11+
12+
- Comprehensive JSDoc documentation for `HypercertCollection` type explaining avatar and banner usage
13+
- Tests for creating collections with avatar/banner using both Blobs and URI strings
14+
- Tests for updating collection images (add, update, remove, preserve)
15+
- Examples showing avatar/banner in collections and projects
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"@hypercerts-org/sdk-core": patch
3+
---
4+
5+
Add documentation for collection item weights (lexicon v0.10.0-beta.7)
6+
7+
Collections now support optional weights on items for proportional attribution. Each item in a collection's `items`
8+
array can have an `itemWeight` field (positive number as string) to indicate relative weighting.
9+
10+
- Enhanced documentation for `HypercertCollectionItem` type with usage examples
11+
- Added examples showing weighted items, nested collections, and basic items
12+
- Documented `CollectionItemInput` helper type for SDK operations

.changeset/add-rich-text-facets.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@hypercerts-org/sdk-core": minor
3+
---
4+
5+
Add rich text facet support for activity descriptions (lexicon v0.10.0-beta.7)
6+
7+
Activities now support rich text annotations (facets) in their descriptions, enabling mentions (@user), URLs, hashtags
8+
(#tag), and other inline markup.
9+
10+
- Added `shortDescriptionFacets` and `descriptionFacets` fields to `CreateHypercertParams`
11+
- Updated `create()` method to include facet fields in hypercert records
12+
- Enhanced `HypercertClaim` documentation with comprehensive facet examples
13+
- Added examples showing mentions, links, and tag facets with proper byte indexing

.claude/skills/sync-lexicons/SKILL.md

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,10 @@ Example plan structure:
153153

154154
**SDK Tasks**:
155155

156-
- [ ] Add/update documentation for X
157-
- [ ] Add type exports for Y
158-
- [ ] Add usage examples
156+
- [ ] Add/update JSDoc documentation to methods (e.g., createX(), updateX())
157+
- [ ] Add type exports for Y if needed
158+
- [ ] Add usage examples in method documentation
159+
- [ ] Add/update tests
159160
- [ ] Build and test
160161
- [ ] Create changeset (minor/major - reason)
161162

@@ -217,15 +218,18 @@ Present this plan to the user and ask:
217218

218219
For the current change (e.g., "Change 1: Collection Item Weights"):
219220

220-
1. **Update documentation** in `packages/sdk-core/src/services/hypercerts/types.ts`:
221-
- Add JSDoc comments for new/changed types
222-
- Add usage examples
221+
1. **Update method documentation** - Focus on documenting the methods users will call:
222+
- Add/update JSDoc comments on SDK methods (e.g., `createCollection()`, `updateCollection()`)
223+
- Add usage examples in method documentation showing new features
224+
- Document parameters, return values, and behavior
223225
- Document breaking changes if any
226+
- Location: `packages/sdk-core/src/repository/HypercertOperationsImpl.ts` and interfaces
224227

225228
2. **Add type exports** if needed:
226-
- Add type aliases for new lexicon types
227-
- Add helper types for SDK operations
229+
- Add type aliases for new lexicon types in `packages/sdk-core/src/services/hypercerts/types.ts`
230+
- Add helper types for SDK operations (CreateParams, UpdateParams, etc.)
228231
- Export from appropriate modules
232+
- Keep type documentation minimal - let method docs do the heavy lifting
229233

230234
3. **Update SDK code** if needed:
231235
- Modify operations to support new fields
@@ -248,7 +252,7 @@ For the current change (e.g., "Change 1: Collection Item Weights"):
248252
pnpm changeset
249253
```
250254

251-
- Follow guidance from `writing-changesets` skill
255+
- Use the process in the `writing-changesets` skill
252256
- Reference the specific feature being added
253257
- Use appropriate bump type
254258

@@ -305,20 +309,15 @@ After ALL changes are implemented:
305309

306310
### Don't Hardcode Lexicon Names
307311

308-
-`if (lexicon === "HYPERCERTS_LEXICONS") ...`
312+
-`if (lexicon === "HYPERCERTS_LEXICON") ...`
309313
- ✅ Read dynamically from the lexicons package exports
310314

311-
### Don't Skip the CHANGELOG
315+
### Don't Skip Review of the CHANGELOG.md review or diffs
312316

313317
- ❌ Immediately update without reviewing changes
314318
- ✅ Always check `CHANGELOG.md` first to see exactly what changed between versions
315319
- ✅ Then run `git diff` for additional context
316320

317-
### Don't Skip the Diff Step
318-
319-
- ❌ Immediately update without reviewing changes
320-
- ✅ Always run `git diff` to see what actually changed
321-
322321
### Don't Forget to Rebuild
323322

324323
- ❌ Update imports and ship
@@ -330,9 +329,15 @@ The sync process focuses on making SDK changes visible and usable to developers.
330329

331330
### Documentation Updates
332331

333-
1. **Type documentation** - JSDoc comments explaining new features
334-
2. **Usage examples** - Code snippets showing how to use new fields/types
335-
3. **Breaking change notes** - Clear warnings about incompatible changes
332+
**IMPORTANT**: Document methods, not types. Users call methods, not types.
333+
334+
1. **Method documentation** - JSDoc comments on SDK methods explaining new features
335+
- Focus on `createX()`, `updateX()`, `getX()` methods in `HypercertOperationsImpl.ts`
336+
- Document parameters, return values, and behavior
337+
- Add `@example` tags showing how to use new fields
338+
2. **Usage examples** - Code snippets in method docs showing how to use new fields/types
339+
3. **Breaking change notes** - Clear warnings in method docs about incompatible changes
340+
4. **Type documentation** - Keep minimal; types should be self-explanatory from method docs
336341

337342
### Type System Updates
338343

packages/sdk-core/src/repository/HypercertOperationsImpl.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,14 @@ export class HypercertOperationsImpl extends EventEmitter<HypercertEvents> imple
283283
hypercertRecord.image = imageBlobRef;
284284
}
285285

286+
if (params.shortDescriptionFacets) {
287+
hypercertRecord.shortDescriptionFacets = params.shortDescriptionFacets;
288+
}
289+
290+
if (params.descriptionFacets) {
291+
hypercertRecord.descriptionFacets = params.descriptionFacets;
292+
}
293+
286294
const hypercertValidation = validate(hypercertRecord, HYPERCERT_COLLECTIONS.CLAIM, "main", false);
287295
if (!hypercertValidation.success) {
288296
throw new ValidationError(`Invalid hypercert record: ${hypercertValidation.error?.message}`);

packages/sdk-core/src/repository/interfaces.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* @packageDocumentation
99
*/
1010

11+
import type { AppBskyRichtextFacet } from "@atproto/api";
1112
import type { EventEmitter } from "eventemitter3";
1213
import type {
1314
LocationParams,
@@ -180,6 +181,42 @@ export interface CreateHypercertParams {
180181
*/
181182
shortDescription: string;
182183

184+
/**
185+
* Optional rich text facets for the short description (v0.10.0-beta.7+).
186+
*
187+
* Enables mentions (@user), URLs, hashtags (#tag), and other inline annotations
188+
* in the short description text. Each facet specifies a byte range and feature type.
189+
*
190+
* @example
191+
* ```typescript
192+
* shortDescriptionFacets: [
193+
* {
194+
* index: { byteStart: 13, byteEnd: 19 }, // "@alice"
195+
* features: [{ $type: "app.bsky.richtext.facet#mention", did: "did:plc:alice123" }]
196+
* }
197+
* ]
198+
* ```
199+
*/
200+
shortDescriptionFacets?: AppBskyRichtextFacet.Main[];
201+
202+
/**
203+
* Optional rich text facets for the full description (v0.10.0-beta.7+).
204+
*
205+
* Enables mentions (@user), URLs, hashtags (#tag), and other inline annotations
206+
* in the description text. Each facet specifies a byte range and feature type.
207+
*
208+
* @example
209+
* ```typescript
210+
* descriptionFacets: [
211+
* {
212+
* index: { byteStart: 6, byteEnd: 33 }, // URL
213+
* features: [{ $type: "app.bsky.richtext.facet#link", uri: "https://example.com" }]
214+
* }
215+
* ]
216+
* ```
217+
*/
218+
descriptionFacets?: AppBskyRichtextFacet.Main[];
219+
183220
/**
184221
* Optional cover image for the hypercert.
185222
*

packages/sdk-core/src/services/hypercerts/types.ts

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,69 @@ import type {
7878
// ============================================================================
7979

8080
export type StrongRef = ComAtprotoRepoStrongRef.Main;
81+
82+
/**
83+
* Hypercert claim (activity) record.
84+
*
85+
* Represents a single hypercert activity with metadata including descriptions,
86+
* time periods, work scope, contributors, and optional rich text annotations.
87+
*
88+
* @remarks
89+
* **Rich Text Facets (beta.7+):**
90+
* - `shortDescriptionFacets` - Annotations for the short description text
91+
* - `descriptionFacets` - Annotations for the full description text
92+
*
93+
* Facets enable rich text features like mentions (@user), URLs, hashtags (#tag),
94+
* and other inline annotations. Each facet specifies:
95+
* - `index`: Byte range in the UTF-8 encoded text (byteStart, byteEnd)
96+
* - `features`: Array of feature objects (mention, link, tag, etc.)
97+
*
98+
* @example Basic claim without facets
99+
* ```typescript
100+
* const claim: HypercertClaim = {
101+
* $type: "org.hypercerts.claim.activity",
102+
* createdAt: new Date().toISOString(),
103+
* shortDescription: "Community cleanup project",
104+
* description: "Monthly beach cleanup initiative",
105+
* startDate: "2024-01-01T00:00:00Z",
106+
* endDate: "2024-12-31T23:59:59Z",
107+
* // ... other fields
108+
* };
109+
* ```
110+
*
111+
* @example Claim with rich text facets
112+
* ```typescript
113+
* const claimWithFacets: HypercertClaim = {
114+
* $type: "org.hypercerts.claim.activity",
115+
* createdAt: new Date().toISOString(),
116+
* shortDescription: "Organized by @alice for #sustainability",
117+
* shortDescriptionFacets: [
118+
* {
119+
* index: { byteStart: 13, byteEnd: 19 }, // "@alice"
120+
* features: [{ $type: "app.bsky.richtext.facet#mention", did: "did:plc:alice123" }]
121+
* },
122+
* {
123+
* index: { byteStart: 24, byteEnd: 39 }, // "#sustainability"
124+
* features: [{ $type: "app.bsky.richtext.facet#tag", tag: "sustainability" }]
125+
* }
126+
* ],
127+
* description: "Visit https://example.com/cleanup for more info",
128+
* descriptionFacets: [
129+
* {
130+
* index: { byteStart: 6, byteEnd: 33 }, // URL
131+
* features: [{ $type: "app.bsky.richtext.facet#link", uri: "https://example.com/cleanup" }]
132+
* }
133+
* ],
134+
* startDate: "2024-01-01T00:00:00Z",
135+
* endDate: "2024-12-31T23:59:59Z",
136+
* // ... other fields
137+
* };
138+
* ```
139+
*
140+
* @see {@link https://atproto.com/specs/richtext#facets|AT Protocol Rich Text Facets}
141+
*/
81142
export type HypercertClaim = OrgHypercertsClaimActivity.Main;
143+
82144
export type HypercertRights = OrgHypercertsClaimRights.Main;
83145
export type HypercertContributionDetails = OrgHypercertsClaimContributionDetails.Main;
84146
export type HypercertContributorInformation = OrgHypercertsClaimContributorInformation.Main;
@@ -88,7 +150,51 @@ export type HypercertMeasurement = OrgHypercertsClaimMeasurement.Main;
88150
export type HypercertEvaluation = OrgHypercertsClaimEvaluation.Main;
89151
export type HypercertEvidence = OrgHypercertsClaimEvidence.Main;
90152
export type HypercertCollection = OrgHypercertsClaimCollection.Main;
91-
/** Collection item with optional weight */
153+
154+
/**
155+
* Collection item with optional weight.
156+
*
157+
* Represents a single item in a collection's `items` array. Each item can reference
158+
* either an activity or another collection (nested collections), with an optional
159+
* weight for proportional attribution.
160+
*
161+
* @remarks
162+
* Structure (beta.7+):
163+
* - `itemIdentifier` (required): StrongRef to the item (activity or collection)
164+
* - `itemWeight` (optional): Positive numeric value as string for proportional weighting
165+
*
166+
* @example Basic item without weight
167+
* ```typescript
168+
* const item: HypercertCollectionItem = {
169+
* itemIdentifier: {
170+
* uri: "at://did:plc:abc123/org.hypercerts.claim.activity/xyz789",
171+
* cid: "bafyreiabc123..."
172+
* }
173+
* };
174+
* ```
175+
*
176+
* @example Item with weight for proportional attribution
177+
* ```typescript
178+
* const weightedItem: HypercertCollectionItem = {
179+
* itemIdentifier: {
180+
* uri: "at://did:plc:abc123/org.hypercerts.claim.activity/xyz789",
181+
* cid: "bafyreiabc123..."
182+
* },
183+
* itemWeight: "2.5" // This activity has 2.5x weight compared to items with weight "1"
184+
* };
185+
* ```
186+
*
187+
* @example Nested collection
188+
* ```typescript
189+
* const nestedCollection: HypercertCollectionItem = {
190+
* itemIdentifier: {
191+
* uri: "at://did:plc:abc123/org.hypercerts.claim.collection/sub789",
192+
* cid: "bafyreiabc456..."
193+
* },
194+
* itemWeight: "1.0"
195+
* };
196+
* ```
197+
*/
92198
export type HypercertCollectionItem = OrgHypercertsClaimCollection.Item;
93199
/** Work scope tag for creating reusable scope atoms */
94200
export type HypercertWorkScopeTag = OrgHypercertsHelperWorkScopeTag.Main;
@@ -144,6 +250,26 @@ export type { OrgHypercertsClaimCollection as CollectionLexicon } from "@hyperce
144250
// SDK Input Helper Types (Derived from Lexicon)
145251
// ============================================================================
146252

253+
/**
254+
* Input type for collection items.
255+
*
256+
* Same as {@link HypercertCollectionItem} but with `$type` field optional since
257+
* the SDK will automatically populate it when creating records.
258+
*
259+
* @example
260+
* ```typescript
261+
* const items: CollectionItemInput[] = [
262+
* {
263+
* itemIdentifier: { uri: "at://did:plc:abc/org.hypercerts.claim.activity/123", cid: "bafyrei..." },
264+
* itemWeight: "1.0"
265+
* },
266+
* {
267+
* itemIdentifier: { uri: "at://did:plc:abc/org.hypercerts.claim.activity/456", cid: "bafyrei..." },
268+
* itemWeight: "2.5" // This activity weighted 2.5x more
269+
* }
270+
* ];
271+
* ```
272+
*/
147273
export type CollectionItemInput = SetOptional<OrgHypercertsClaimCollection.Item, "$type">;
148274

149275
/**

0 commit comments

Comments
 (0)