{"id":18409030,"url":"https://github.com/objcio/attributed-string-builder","last_synced_at":"2025-04-07T09:33:30.296Z","repository":{"id":65315570,"uuid":"588592521","full_name":"objcio/attributed-string-builder","owner":"objcio","description":"Attributed String Builders","archived":false,"fork":false,"pushed_at":"2023-09-14T08:38:38.000Z","size":128,"stargazers_count":109,"open_issues_count":1,"forks_count":6,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-02T22:19:22.999Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/objcio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2023-01-13T13:59:53.000Z","updated_at":"2024-12-13T07:50:47.000Z","dependencies_parsed_at":"2023-02-14T15:31:01.711Z","dependency_job_id":null,"html_url":"https://github.com/objcio/attributed-string-builder","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/objcio%2Fattributed-string-builder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/objcio%2Fattributed-string-builder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/objcio%2Fattributed-string-builder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/objcio%2Fattributed-string-builder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/objcio","download_url":"https://codeload.github.com/objcio/attributed-string-builder/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247626714,"owners_count":20969357,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T03:23:08.733Z","updated_at":"2025-04-07T09:33:30.054Z","avatar_url":"https://github.com/objcio.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AttributedString Builder\n\nA simple way to build up attributed strings using result builders from a variety of sources. Based on the episodes from [Swift Talk](https://talk.objc.io/episodes/S01E337-attributed-string-builder-part-1). Here are the things you can embed:\n\n- Plain strings\n- Markdown\n- Images\n- SwiftUI Views\n- Table support\n- Multi-page PDF export\n- Footnotes\n\nHere's an example showing plain strings, Markdown and SwiftUI views:\n\n```swift\n@AttributedStringBuilder\nvar example: some AttributedStringConvertible {\n    \"Hello, World!\"\n        .bold()\n        .modify { $0.backgroundColor = .yellow }\n    \"\"\"\n    This is some markdown with **strong** `code` text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tempus, tortor eu maximus gravida, ante diam fermentum magna, in gravida ex tellus ac purus.\n\n    - One\n    - Two\n    - Three\n\n    \u003e A blockquote.\n    \"\"\".markdown()\n    Embed {\n        HStack {\n            Image(systemName: \"hand.wave\")\n                .font(.largeTitle)\n            Text(\"Hello from SwiftUI\")\n            Color.red.frame(width: 100, height: 50)\n        }\n    }\n```\n\nYou can then turn this example into a multi-page PDF like this:\n\n```swift\nlet data = await example\n    .joined(separator: \"\\n\") // join the parts using newlines\n    .run(environment: .init(attributes: sampleAttributes)) // turn into a single `NSAttributedString`\n    .pdf() // render as PDF\ntry! data.write(to: .desktopDirectory.appending(component: \"out.pdf\"))\n```\n\nHere's [a larger sample](Sources/Tests/Tests.swift).\n\n## Features\n\n### Attributes\n\nThe [Attributes](Sources/AttributedStringBuilder/Attributes.swift) struct is a value type representing the attributes in an `NSAttributedString`. During the building of the attributed string, this is passed on through the environment. For example, this is how you can build a simple attributed string using plain strings and `.modify`:\n\n```swift\n@AttributedStringBuilder var sample1: some AttributedStringConvertible {\n    \"Hello\"\n    \"World\".modify { $0.textColor = .red }\n}\n```\n\n### Strings\n\nYou can turn any string directly into an attributed string. The attributes from the environment are used to do this. You can also modify the environment in a way very similar to what SwiftUI does. For example, you can write `\"Hello\".bold()\" to take the current attributes, make them bold, and then render the string `\"Hello\"` using these modified attributes.\n\n### Markdown\n\nYou can take any Markdown string and render it into an attributed string as well. For most customization, you can pass in a custom [stylesheet](Sources/AttributedStringBuilder/MarkdownStylesheet.swift). In the Markdown string literal, you can embed other values that convert to `AttributedStringConvertible`:\n\n```swift\n@AttributedStringBuilder var sample2: some AttributedStringConvertible {\n    Markdown(\"\"\"\n    This is *Markdown* syntax.\n\n    With \\(\"inline\".modify { $0.underlineStyle = .single }) nesting.\n    \"\"\")\n}\n```\n\n### Images\n\nYou can embed any `NSImage` into the attributed string, they're rendered as-is.\n\n### SwiftUI Views\n\nSwiftUI views can be embedded using the [Embed](Sources/AttributedStringBuilder/SwiftUI.swift) modifier. By default, it proposes `nil⨉nil` to the view, but this can be customized. SwiftUI views are rendered into a PDF context and are embedded as vector graphics.\n\n### Tables\n\nYou can construct tables in attributed strings using the [Table](Sources/AttributedStringBuilder/Table.swift) support. This interface might still change (ideally, we'd use result builders for this as well).\n\n### Environment\n\nYou can use the environment in a way similar to SwiftUI's Environment to pass values down the tree.\n\n### State\n\nSimilar to the environment, you can also thread state through. This is useful (for example) to number footnotes. While the modified environment is always passed to *children* of the current node, modified state is passed to the next nodes that are rendered.\n\n## Swift Talk Episodes\n\n- [Writing the Builder](https://talk.objc.io/episodes/S01E337-attributed-string-builder-part-1)\n- [Joining Elements](https://talk.objc.io/episodes/S01E338-attributed-string-builder-part-2)\n- [Syntax Highlighting](https://talk.objc.io/episodes/S01E339-attributed-string-builder-part-3)\n- [Rendering SwiftUI Views](https://talk.objc.io/episodes/S01E340-attributed-string-builder-part-4)\n- [Rendering Markdown](https://talk.objc.io/episodes/S01E341-attributed-string-builder-part-5)\n- [Creating a PDF](https://talk.objc.io/episodes/S01E342-attributed-string-builder-part-6)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobjcio%2Fattributed-string-builder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobjcio%2Fattributed-string-builder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobjcio%2Fattributed-string-builder/lists"}