{"id":2653,"url":"https://github.com/rechsteiner/Parchment","last_synced_at":"2025-08-06T16:31:22.013Z","repository":{"id":8708473,"uuid":"53791028","full_name":"rechsteiner/Parchment","owner":"rechsteiner","description":"A paging view with a highly customizable menu ✨","archived":false,"fork":false,"pushed_at":"2024-09-29T15:40:59.000Z","size":5769,"stargazers_count":3394,"open_issues_count":24,"forks_count":419,"subscribers_count":54,"default_branch":"main","last_synced_at":"2024-12-03T03:05:15.518Z","etag":null,"topics":["ios","ios-libraries","ios-ui","pageviewcontroller","swift","swift-library","swiftui","uicollectionview"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rechsteiner.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-03-13T15:04:27.000Z","updated_at":"2024-12-02T06:32:54.000Z","dependencies_parsed_at":"2024-01-11T21:07:14.558Z","dependency_job_id":"e01631a7-029c-4502-8cf9-3646043a02a8","html_url":"https://github.com/rechsteiner/Parchment","commit_stats":{"total_commits":498,"total_committers":42,"mean_commits":"11.857142857142858","dds":0.142570281124498,"last_synced_commit":"189e900d1910d3c3a18cc6ad4b73fdbe54896e74"},"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rechsteiner%2FParchment","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rechsteiner%2FParchment/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rechsteiner%2FParchment/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rechsteiner%2FParchment/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rechsteiner","download_url":"https://codeload.github.com/rechsteiner/Parchment/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228041341,"owners_count":17860221,"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":["ios","ios-libraries","ios-ui","pageviewcontroller","swift","swift-library","swiftui","uicollectionview"],"created_at":"2024-01-05T20:16:19.328Z","updated_at":"2024-12-09T16:31:02.626Z","avatar_url":"https://github.com/rechsteiner.png","language":"Swift","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/rechsteiner/Parchment/main/.images/title-light-mode.png#gh-light-mode-only\" width=\"240\" height=\"70\" /\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/rechsteiner/Parchment/main/.images/title-dark-mode.png#gh-dark-mode-only\" width=\"240\" height=\"70\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cstrong\u003e\u003ca href=\"#getting-started\"\u003eGetting Started\u003c/a\u003e\u003c/strong\u003e |\n    \u003cstrong\u003e\u003ca href=\"#customization\"\u003eCustomization\u003c/a\u003e\u003c/strong\u003e |\n    \u003cstrong\u003e\u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/rechsteiner/Parchment/actions/workflows/parchment.yml\"\u003e\u003cimg src=\"https://github.com/rechsteiner/Parchment/actions/workflows/parchment.yml/badge.svg\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr/\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/rechsteiner/Parchment/main/.images/example-cities.gif\" alt=\"Cities Example\" /\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/rechsteiner/Parchment/main/.images/example-unsplash.gif\" alt=\"Unsplash Example\" /\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/rechsteiner/Parchment/main/.images/example-calendar.gif\" alt=\"Calendar Example\" /\u003e\n\u003c/p\u003e\n\n## Features\n\nParchment lets you page between view controllers while showing any type of generic indicator that scrolls along with the content. Here are some benefits of using Parchment:\n\n- **Highly customizable** \u003cbr/\u003e The menu items are built using\n  `UICollectionView`, which means you can display pretty much whatever you want. You can even subclass the layout to create completely custom behaviours.\n\n- **Memory-efficient**: \u003cbr/\u003e Parchment only allocates view controllers when they’re needed, meaning if you have a lot of view controllers you don’t have to initialize them all up-front.\n\n- **Infinite scrolling**: \u003cbr /\u003e Because view controllers are only allocated as you are scrolling, you can create data sources that are infinitely large. This is perfect for things like [calendars](/Documentation/infinite-data-source.md).\n\n## Table of contents\n\n- [Getting started](#getting-started)\n  - [SwiftUI](#basic-usage)\n    - [Basic usage](#basic-usage)\n    - [Dynamic pages](#dynamic-pages)\n    - [Update selection](#update-selection)\n    - [Modifiers](#modifiers)\n  - [UIKit](#basic-usage-with-uikit)\n    - [Basic usage with UIKit](#basic-usage-with-uikit)\n    - [Data source](#data-source)\n    - [Infinite data source](#infinite-data-source)\n    - [Selecting items](#selecting-items)\n    - [Reloading data](#reloading-data)\n    - [Delegate](#delegate)\n    - [Size delegate](#size-delegate)\n    - [Customization](#customization)\n- [Options](#options)\n- [Installation](#installation)\n- [Changelog](#changelog)\n- [Licence](#licence)\n\n## Getting started\n\nUsing UIKit? Go to [UIKit documentation](#basic-usage-with-uikit).\n\n\u003cdetails open\u003e\n\n\u003csummary\u003eSwiftUI\u003c/summary\u003e\n\n### Basic usage\n\nCreate a `PageView` instance with the pages you want to show. Each `Page` takes a title and a content view, which can be any SwiftUI view.\n\n```swift\nPageView {\n    Page(\"Title 0\") {\n        Text(\"Page 0\")\n    }\n    Page(\"Title 1\") {\n        Text(\"Page 1\")\n    }\n}\n```\n\nBy default, the menu items are displayed as titles, but you can also pass in any SwiftUI view as the menu item. The state parameter allows you to customize the menu item based on the selected state and scroll position of the view. For instance, you could show an icon that rotates based on its progress like this:\n\n```swift\nPageView {\n    Page { state in\n        Image(systemName: \"star.fill\")\n            .rotationEffect(Angle(degrees: 90 * state.progress))\n    } content: {\n        Text(\"Page 1\")\n    }\n}\n```\n\n### Dynamic pages\n\nTo create a `PageView` with a dynamic number of pages, you can pass in a collection of items where each item is mapped to a `Page`:\n\n```swift\nPageView(items, id: \\.self) { item in\n    Page(\"Title \\(item)\") {\n        Text(\"Page \\(item)\")\n    }\n}\n```\n\n### Update selection\n\nTo select specific items, you can pass a binding into `PageView` with the index of the currently selected item. When updating the binding, Parchment will scroll to the new index.\n\n```swift\n@State var selectedIndex: Int = 0\n...\nPageView(selectedIndex: $selectedIndex) {\n    Page(\"Title 1\") {\n        Button(\"Next\") {\n            selectedIndex = 1\n        }\n    }\n    Page(\"Title 2\") {\n        Text(\"Page 2\")\n    }\n}\n```\n\n### Modifiers\n\nYou can customize the `PageView` using the following modifiers. See [Options](#options) for more details on each option.\n\n```swift\nPageView {\n    Page(\"Title 1\") {\n        Text(\"Page 1\")\n    }\n}\n.menuItemSize(.fixed(width: 100, height: 60))\n.menuItemSpacing(20)\n.menuItemLabelSpacing(30)\n.menuBackgroundColor(.white)\n.menuInsets(.vertical, 20)\n.menuHorizontalAlignment(.center)\n.menuPosition(.bottom)\n.menuTransition(.scrollAlongside)\n.menuInteraction(.swipe)\n.contentInteraction(.scrolling)\n.contentNavigationOrientation(.vertical)\n.selectedScrollPosition(.preferCentered)\n.indicatorOptions(.visible(height: 4))\n.indicatorColor(.blue)\n.borderOptions(.visible(height: 4))\n.borderColor(.blue.opacity(0.2))\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eUIKit\u003c/summary\u003e\n\n### Basic usage with UIKit\n\nParchment is built around the `PagingViewController` class. You can initialize it with an array of view controllers and it will display menu items for each view controller using their `title` property.\n\n```Swift\nlet firstViewController = UIViewController()\nlet secondViewController = UIViewController()\n\nlet pagingViewController = PagingViewController(viewControllers: [\n  firstViewController,\n  secondViewController\n])\n```\n\nSee more: [Basic usage](/Documentation/basic-usage.md)\n\n### Data source\n\nInitializing `PagingViewController` with an array of view controllers is fine in most cases, but if you have more than a few view controllers you probably don't want to allocate them all up-front. If you're going to display a fixed number of view controllers, you can setup your own data source by implementing `PagingViewControllerDataSource`:\n\n```Swift\nextension ViewController: PagingViewControllerDataSource {\n    func numberOfViewControllers(in pagingViewController: PagingViewController) -\u003e Int {\n        return 10\n    }\n\n    func pagingViewController(_ pagingViewController: PagingViewController, viewControllerAt index: Int) -\u003e UIViewController {\n        return ChildViewController(index: index)\n    }\n\n    func pagingViewController(_: PagingViewController, pagingItemAt index: Int) -\u003e PagingItem {\n        return PagingIndexItem(title: \"View \\(index)\", index: index)\n    }\n}\n```\n\nThen you need to set the `dataSource` property and select the initial item:\n\n```Swift\nlet pagingViewController = PagingViewController()\npagingViewController.dataSource = self\npagingViewController.select(index: 0)\n```\n\nUsing the data source means Parchment will only allocate view controllers for the currently selected item and any of its siblings. This is a lot more memory efficient than using `PagingViewController(viewControllers:)` if you have many view controllers.\n\nRead more: [Using the data source](/Documentation/data-source.md)\n\n### Infinite data source\n\nUsing `PagingViewControllerDataSource` means you need to know how many view controllers to display. If you’re creating something like a calendar, the number of view controllers can be infinitely large. In that case you can use the `PagingViewControllerInfiniteDataSource` protocol:\n\n```Swift\nextension ViewController: PagingViewControllerInfiniteDataSource {\n    func pagingViewController(_: PagingViewController, viewControllerFor pagingItem: PagingItem) -\u003e UIViewController {\n        return ItemViewController(item: pagingItem)\n    }\n\n    func pagingViewController(_: PagingViewController, itemBefore pagingItem: PagingItem) -\u003e PagingItem? {\n        guard let item = pagingItem as? Item else { return nil }\n        return Item(index: item.index - 1)\n    }\n\n    func pagingViewController(_ : PagingViewController, itemAfter pagingItem: PagingItem) -\u003e PagingItem? {\n        guard let item = pagingItem as? Item else { return nil }\n        return Item(index: item.index + 1)\n    }\n}\n```\n\nThen set the `infiniteDataSource` property and select the initial item:\n\n```Swift\nlet pagingViewController = PagingViewController()\npagingViewController.infiniteDataSource = self\npagingViewController.select(pagingItem: Item(index: 0))\n```\n\nThis pattern is very similar to the\n[UIPageViewControllerDataSource](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIPageViewControllerClassReferenceClassRef/)\nprotocol. The main difference is that instead of returning view controllers directly, you have to return an instance conforming to the `PagingItem` protocol. Parchment will recursively call these methods for the selected `PagingItem` until the available space is filled up.\n\nRead more: [Using the infinite data source](/Documentation/infinite-data-source.md)\n\n### Selecting items\n\nYou can select items programmatically using:\n\n```Swift\nfunc select(pagingItem: PagingItem, animated: Bool = false)\n```\n\nLet’s say you want to select the first item:\n\n```Swift\noverride func viewDidLoad() {\n  super.viewDidLoad()\n  if let first = pagingViewController.children.first as? PagingItem {\n    pagingViewController.select(pagingItem: first)\n  }\n}\n```\n\nOr if you have set the `dateSource` property, you can select items based on their index:\n\n```Swift\nfunc select(index: Int, animated: Bool = false)\n```\n\n### Reloading data\n\nYou can reload data using this method:\n\n```Swift\nfunc reloadData()\n```\n\nThis will keep the previously selected item if it's still part of the\nupdated data. If not, it will select the first item in the list. It\nwill also reload the view controllers displayed in the page view\ncontroller. If you only want to reload the menu items, you can use\nthis method:\n\n```Swift\nfunc reloadMenu()\n```\n\nCalling `reloadData()` will not work when using\n`PagingViewControllerInfiniteDataSource`, as we then need to know what\nthe initial item should be. In that case you should use this method:\n\n```Swift\nfunc reloadData(around: PagingItem)\n```\n\nThis will mark the given paging item as selected and generate new items around it.\n\n### Delegate\n\nParchment provides delegate methods for every step of the transition process through the `PagingViewControllerDelegate` protocol.\n\n```Swift\nprotocol PagingViewControllerDelegate: class {\n\n    func pagingViewController(\n        _: PagingViewController,\n        isScrollingFromItem currentPagingItem: PagingItem,\n        toItem upcomingPagingItem: PagingItem?,\n        startingViewController: UIViewController,\n        destinationViewController: UIViewController?,\n        progress: CGFloat)\n\n    func pagingViewController(\n        _: PagingViewController,\n        willScrollToItem pagingItem: PagingItem,\n        startingViewController: UIViewController,\n        destinationViewController: UIViewController)\n\n    func pagingViewController(\n        _ pagingViewController: PagingViewController,\n        didScrollToItem pagingItem: PagingItem,\n        startingViewController: UIViewController?,\n        destinationViewController: UIViewController,\n        transitionSuccessful: Bool)\n\n    func pagingViewController(\n        _ pagingViewController: PagingViewController,\n        didSelectItem pagingItem: PagingItem)\n}\n```\n\n### Size delegate\n\nBy default, the size of the menu items is controlled by the `menuItemSize` property. If you need to control width of each menu item individually you can use the `PagingControllerSizeDelegate` protocol:\n\n```Swift\nprotocol PagingViewControllerSizeDelegate: class {\n    func pagingViewController(\n        _: PagingViewController,\n        widthForPagingItem pagingItem: PagingItem,\n        isSelected: Bool) -\u003e CGFloat\n}\n```\n\nThen set the `sizeDelegate` on the `PagingViewController`:\n\n```Swift\nlet pagingViewController = PagingViewController()\npagingViewController.sizeDelegate = self\n```\n\n### Customization\n\nParchment is built to be very flexible. The menu items are displayed using UICollectionView, so they can display pretty much whatever you want. If you need any further customization you can even subclass the collection view layout. All customization is handled by the properties listed below.\n\n### Custom cells\n\nTo use custom cells you need to subclass `PagingCell` and register the cell type for a given `PagingItem`:\n\n```Swift\nlet pagingViewController = PagingViewController()\npagingViewController.register(CalendarPagingCell.self, for: CalendarItem.self)\n```\n\nParchment will then dequeue your custom cell when you return the given `PagingItem` in your data source. You can register multiple cell types for different `PagingItem`s.\n\n### Properties\n\nAll customization properties are set on `PagingViewController`:\n\n```Swift\nlet pagingViewController = PagingViewController()\npagingViewController.menuItemSize = .fixed(width: 40, height: 40)\npagingViewController.menuItemSpacing = 10\n```\n\nSee [Options](#options) for all customization options.\n\n\u003c/details\u003e\n\n## Options\n\n#### `menuItemSize`\n\nThe size of the menu items. When using [`sizeDelegate`](#size-delegate) the width will be ignored.\n\n```Swift\nenum PagingMenuItemSize {\n  case fixed(width: CGFloat, height: CGFloat)\n\n  // Automatically calculate the size of the menu items based on the\n  // cells intrinsic content size. Try to come up with an estimated\n  // width that's similar to the expected width of the cells.\n  case selfSizing(estimatedWidth: CGFloat, height: CGFloat)\n\n  // Tries to fit all menu items inside the bounds of the screen.\n  // If the items can't fit, the items will scroll as normal and\n  // set the menu items width to `minWidth`.\n  case sizeToFit(minWidth: CGFloat, height: CGFloat)\n}\n```\n\n_Default: `.sizeToFit(minWidth: 150, height: 40)`_\n\n#### `menuItemSpacing`\n\nThe spacing between the menu items.\n\n_Default: `0`_\n\n#### `menuItemLabelSpacing`\n\nThe horizontal constraints of menu item label.\n\n_Default: `20`_\n\n#### `menuInsets`\n\nThe insets around all of the menu items.\n\n_Default: `UIEdgeInsets()`_\n\n#### `menuHorizontalAlignment`\n\n```Swift\nenum PagingMenuHorizontalAlignment {\n  case `default`\n\n  // Allows all paging items to be centered within the paging menu\n  // when PagingMenuItemSize is .fixed and the sum of the widths\n  // of all the paging items are less than the paging menu\n  case center\n}\n```\n\n_Default: `.default`_\n\n#### `menuTransition`\n\nDetermine the transition behaviour of menu items while scrolling the content.\n\n```Swift\nenum PagingMenuTransition {\n  // Update scroll offset based on how much the content has\n  // scrolled. Makes the menu items transition smoothly as you scroll.\n  case scrollAlongside\n\n  // Animate the menu item position after a transition has completed.\n  case animateAfter\n}\n```\n\n_Default: `.scrollAlongside`_\n\n#### `menuInteraction`\n\nDetermine how users can interact with the menu items.\n\n```Swift\nenum PagingMenuInteraction {\n  case scrolling\n  case swipe\n  case none\n}\n```\n\n_Default: `.scrolling`_\n\n#### `menuLayoutClass`\n\nThe class type for collection view layout. Override this if you want to use your own subclass of the layout. Setting this property will initialize the new layout type and update the collection view.\n\n_Default: `PagingCollectionViewLayout.Type`_\n\n#### `selectedScrollPosition`\n\nDetermine how the selected menu item should be aligned when it is selected. Effectively the same as the `UICollectionViewScrollPosition`.\n\n```Swift\nenum PagingSelectedScrollPosition {\n  case left\n  case right\n\n  // Centers the selected menu item where possible. If the item is\n  // to the far left or right, it will not update the scroll position.\n  // Effectivly the same as .centeredHorizontally on UIScrollView.\n  case preferCentered\n}\n```\n\n_Default: `.preferCentered`_\n\n#### `indicatorOptions`\n\nAdd an indicator view to the selected menu item. The indicator width will be equal to the selected menu items width. Insets only apply horizontally.\n\n```Swift\nenum PagingIndicatorOptions {\n  case hidden\n  case visible(\n    height: CGFloat,\n    zIndex: Int,\n    spacing: UIEdgeInsets,\n    insets: UIEdgeInsets)\n}\n```\n\n_Default:_\n\n```Swift\n.visible(\n  height: 4,\n  zIndex: Int.max,\n  spacing: UIEdgeInsets.zero,\n  insets: UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8))\n```\n\n#### `indicatorClass`\n\nThe class type for the indicator view. Override this if you want your use your own subclass of `PagingIndicatorView`.\n\n_Default: `PagingIndicatorView.self`_\n\n#### `indicatorColor`\n\nThe background color for the indicator view.\n\n_Default: `UIColor(red: 3/255, green: 125/255, blue: 233/255, alpha: 1)`_\n\n#### `borderOptions`\n\nAdd a border at the bottom of the menu items. The border will be as wide as all the menu items. Insets only apply horizontally.\n\n```Swift\nenum PagingBorderOptions {\n  case hidden\n  case visible(\n    height: CGFloat,\n    zIndex: Int,\n    insets: UIEdgeInsets)\n}\n```\n\n_Default:_\n\n```Swift\n.visible(\n  height: 1,\n  zIndex: Int.max - 1,\n  insets: UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8))\n```\n\n#### `borderClass`\n\nThe class type for the border view. Override this if you want your use your own subclass of `PagingBorderView`.\n\n_Default: `PagingBorderView.self`_\n\n#### `borderColor`\n\nThe background color for the border view.\n\n_Default: `UIColor(white: 0.9, alpha: 1)`_\n\n#### `includeSafeAreaInsets`\n\nUpdates the content inset for the menu items based on the `.safeAreaInsets` property.\n\n_Default: `true`_\n\n#### `font`\n\nThe font used for title label on the menu items.\n\n_Default: `UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)`_\n\n#### `selectedFont`\n\nThe font used for title label on the currently selected menu item.\n\n_Default: `UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)`_\n\n#### `textColor`\n\nThe color of the title label on the menu items.\n\n_Default: `UIColor.black`_\n\n#### `selectedTextColor`\n\nThe text color for the currently selected menu item.\n\n_Default: `UIColor(red: 3/255, green: 125/255, blue: 233/255, alpha: 1)`_\n\n#### `backgroundColor`\n\nThe background color for the menu items.\n\n_Default: `UIColor.white`_\n\n#### `selectedBackgroundColor`\n\nThe background color for the selected menu item.\n\n_Default: `UIColor.clear`_\n\n#### `menuBackgroundColor`\n\nThe background color for the view behind the menu items.\n\n_Default: `UIColor.white`_\n\n## Installation\n\nParchment will be compatible with the lastest public release of Swift.\n\n### Requirements\n\n- iOS 12.0+\n- Xcode 14.0+\n\n### CocoaPods\n\nParchment is available through [CocoaPods](https://cocoapods.org). To install it, add the following to your `Podfile`:\n\n```\npod 'Parchment', '~\u003e 4.0'\n```\n\n### Swift Package Manager\n\nParchment is available through [Swift Package Manager](https://swift.org/package-manager/). Add Parchment as a dependency to your `Package.swift`:\n\n```Swift\n.package(url: \"https://github.com/rechsteiner/Parchment\", from: \"4.0.0\")\n```\n\n### Carthage\n\nParchment also supports [Carthage](https://github.com/Carthage/Carthage). To install it, add the following to your `Cartfile`:\n\n```\ngithub \"rechsteiner/Parchment\" ~\u003e 4.0\n```\n\nSee [this guide](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) for more details on using Carthage.\n\n## Changelog\n\nThis can be found in the [CHANGELOG](/CHANGELOG.md) file.\n\n## Licence\n\nParchment is released under the MIT license. See [LICENSE](/LICENSE) for details.\n","funding_links":[],"categories":["UI","Libs","Swift","UI [🔝](#readme)","uicollectionview"],"sub_categories":["Menu","UI","Other free courses"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frechsteiner%2FParchment","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frechsteiner%2FParchment","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frechsteiner%2FParchment/lists"}