Compare commits
1 Commits
event-rela
...
tyiu/photo
| Author | SHA1 | Date | |
|---|---|---|---|
|
a72f964111
|
52
.github/ISSUE_TEMPLATE/app_release.md
vendored
52
.github/ISSUE_TEMPLATE/app_release.md
vendored
@@ -1,52 +0,0 @@
|
||||
---
|
||||
name: App release process
|
||||
about: Begin preparing for a new app release
|
||||
title: 'Release: '
|
||||
labels: release-tasks
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
A new version release. Please attempt to follow the release process steps below in the order they are shown.
|
||||
|
||||
## TestFlight release candidates
|
||||
|
||||
### Release candidate 1
|
||||
|
||||
**Version:** _[Enter full build information for the release candidate, including major and minor version number, build number, and commit hash]_
|
||||
|
||||
1. [ ] Merge in all needed changes to `master`
|
||||
2. [ ] Check CI, make sure it is passing
|
||||
3. [ ] Prepare preliminary changelog as a draft PR: _[Enter PR link to changelog here]_
|
||||
4. [ ] Make a _release_ build and submit to the internal TestFlight group via our new Release candidate workflow in Xcode Cloud.
|
||||
5. [ ] Prepare short screencast style video with main changes for the announcement
|
||||
6. [ ] Publish release build to these TestFlight groups:
|
||||
- [ ] Alpha testers group
|
||||
- [ ] Translators group
|
||||
- [ ] Purple group
|
||||
7. [ ] Publish announcement on Nostr
|
||||
|
||||
|
||||
_[Duplicate this release candidate section if there is more than one release candidate]_
|
||||
|
||||
|
||||
## App Store release
|
||||
|
||||
1. [ ] Release candidate checks:
|
||||
- [ ] Release candidate has been on Purple TestFlight for at least one week
|
||||
- [ ] No blocker issues came from feedback from Purple users (double-check)
|
||||
- [ ] Check with stakeholders
|
||||
- [ ] Check with developers & product for any release showstoppers (e.g., critical newfound bugs)
|
||||
2. [ ] Thorough check on release notes
|
||||
3. [ ] Submit to App Store review (with manual publishing setting enabled)
|
||||
4. [ ] Get App Store approval from Apple
|
||||
5. [ ] Prepare announcement
|
||||
7. [ ] Publish on the App Store and make announcement
|
||||
8. [ ] Publish changelog and tag commit hash corresponding to the release
|
||||
9. [ ] Perform a version bump on the repository, in preparation for the next release
|
||||
|
||||
|
||||
## Notes/others
|
||||
|
||||
_Enter any relevant notes here_
|
||||
|
||||
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,35 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: 'Bug: '
|
||||
labels: bug, Needs recreation
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**What happens**
|
||||
When I perform action ___, _____ happens.
|
||||
|
||||
**What I expect to happen**
|
||||
I expect _______ to happen.
|
||||
|
||||
**Link to noteID, npub**
|
||||
Provide link to relevant noteID, npub etc.
|
||||
|
||||
**Screenshots/video recording**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
|
||||
** Versions **
|
||||
Damus version: [e.g. 1.7.2 (1()]
|
||||
Operating system version: [e.g. iOS 17.2.1]
|
||||
Device: e.g. iPhone 13 Pro
|
||||
|
||||
**Steps To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Open Damus
|
||||
2. Tap on ___
|
||||
3. Action ____
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
27
.github/ISSUE_TEMPLATE/feature_request.md
vendored
27
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: 'Feature Request:'
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Have a go at filling out the User Story template below
|
||||
|
||||
As a Damus user who is _____________, I would like to _________________, so that I achieve ___________.
|
||||
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
** When does this problem happen? **
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
37
.github/pull_request_template.md
vendored
37
.github/pull_request_template.md
vendored
@@ -1,37 +0,0 @@
|
||||
## Summary
|
||||
|
||||
_[Please provide a summary of the changes in this PR.]_
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have read (or I am familiar with) the [Contribution Guidelines](../docs/CONTRIBUTING.md)
|
||||
- [ ] I have tested the changes in this PR
|
||||
- [ ] I have opened or referred to an existing github issue related to this change.
|
||||
- [ ] My PR is either small, or I have split it into smaller logical commits that are easier to review
|
||||
- [ ] I have added the signoff line to all my commits. See [Signing off your work](../docs/CONTRIBUTING.md#sign-your-work---the-developers-certificate-of-origin)
|
||||
- [ ] I have added appropriate changelog entries for the changes in this PR. See [Adding changelog entries](../docs/CONTRIBUTING.md#add-changelog-changed-changelog-fixed-etc)
|
||||
- [ ] I do not need to add a changelog entry. Reason: _[Please provide a reason]_
|
||||
- [ ] I have added appropriate `Closes:` or `Fixes:` tags in the commit messages wherever applicable, or made sure those are not needed. See [Submitting patches](https://github.com/damus-io/damus/blob/master/docs/CONTRIBUTING.md#submitting-patches)
|
||||
|
||||
## Test report
|
||||
|
||||
_Please provide a test report for the changes in this PR. You can use the template below, but feel free to modify it as needed._
|
||||
|
||||
**Device:** _[Please specify the device you used for testing]_
|
||||
|
||||
**iOS:** _[Please specify the iOS version you used for testing]_
|
||||
|
||||
**Damus:** _[Please specify the Damus version or commit hash you used for testing]_
|
||||
|
||||
**Setup:** _[Please provide a brief description of the setup you used for testing, if applicable]_
|
||||
|
||||
**Steps:** _[Please provide a list of steps you took to test the changes in this PR]_
|
||||
|
||||
**Results:**
|
||||
- [ ] PASS
|
||||
- [ ] Partial PASS
|
||||
- Details: _[Please provide details of the partial pass]_
|
||||
|
||||
## Other notes
|
||||
|
||||
_[Please provide any other information that you think is relevant to this PR.]_
|
||||
17
.github/workflows/run-tests.yaml
vendored
Normal file
17
.github/workflows/run-tests.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
jobs:
|
||||
test:
|
||||
name: Run Tests
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v1
|
||||
- name: Running Tests
|
||||
run: xcodebuild test -scheme damus -project damus.xcodeproj -destination 'platform=iOS Simulator,name=iPhone 14,OS=16.0' | xcpretty && exit ${PIPESTATUS[0]}
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,9 +1,5 @@
|
||||
xcuserdata
|
||||
/.direnv
|
||||
damus/TestingPrivate.swift
|
||||
damus.xcodeproj/xcshareddata/xcbaselines
|
||||
.DS_Store
|
||||
TODO.bak
|
||||
tags
|
||||
build-git-hash.txt
|
||||
.build
|
||||
|
||||
7
.mailmap
7
.mailmap
@@ -1,7 +0,0 @@
|
||||
Terry Yiu <git@tyiu.xyz> <963907+tyiu@users.noreply.github.com>
|
||||
Ben Weeks <ben.weeks@knowall.ai> <ben.weeks@outlook.com>
|
||||
Suhail Saqan <suhail.saqan@gmail.com> <43693074+suhailsaqan@users.noreply.github.com>
|
||||
cr0bar <cr0bar@cr0.bar> <cr0bar@users.noreply.github.com>
|
||||
Swift <scoder1747@gmail.com> <120697811+scoder1747@users.noreply.github.com>
|
||||
Daniel D'Aquino <daniel@daquino.me> <patches@damus.io>
|
||||
Transifex <transifex@transifex.com> <43880903+transifex-integration[bot]@users.noreply.github.com>
|
||||
@@ -1,5 +0,0 @@
|
||||
### Acknowledgements and licenses
|
||||
|
||||
1. This product contains code derived from [Nostr SDK iOS](https://github.com/nostr-sdk/nostr-sdk-ios). [License](https://github.com/nostr-sdk/nostr-sdk-ios/blob/40df800c6749d7ce0b6fd7328e76cbc0dc71c87b/LICENSE)
|
||||
2. This product includes software developed by the "Marcin Krzyzanowski" (http://krzyzanowskim.com/). [License](https://github.com/krzyzanowskim/CryptoSwift/blob/e74bbbfbef939224b242ae7c342a90e60b88b5ce/LICENSE)
|
||||
|
||||
1671
CHANGELOG.md
1671
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.usernotifications.communication</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.kernel.extended-virtual-addressing</key>
|
||||
<true/>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.damus</string>
|
||||
</array>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.jb55.damus2</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.usernotifications.service</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,45 +0,0 @@
|
||||
//
|
||||
// NotificationExtensionState.swift
|
||||
// DamusNotificationService
|
||||
//
|
||||
// Created by Daniel D’Aquino on 2023-11-27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct NotificationExtensionState: HeadlessDamusState {
|
||||
let ndb: Ndb
|
||||
let settings: UserSettingsStore
|
||||
let contacts: Contacts
|
||||
let mutelist_manager: MutelistManager
|
||||
let keypair: Keypair
|
||||
let profiles: Profiles
|
||||
let zaps: Zaps
|
||||
let lnurls: LNUrls
|
||||
|
||||
init?() {
|
||||
guard let ndb = Ndb(owns_db_file: false) else { return nil }
|
||||
self.ndb = ndb
|
||||
|
||||
guard let keypair = get_saved_keypair() else { return nil }
|
||||
|
||||
// dumb stuff needed for property wrappers
|
||||
UserSettingsStore.pubkey = keypair.pubkey
|
||||
self.settings = UserSettingsStore()
|
||||
|
||||
self.contacts = Contacts(our_pubkey: keypair.pubkey)
|
||||
self.mutelist_manager = MutelistManager(user_keypair: keypair)
|
||||
self.keypair = keypair
|
||||
self.profiles = Profiles(ndb: ndb)
|
||||
self.zaps = Zaps(our_pubkey: keypair.pubkey)
|
||||
self.lnurls = LNUrls()
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func add_zap(zap: Zapping) -> Bool {
|
||||
// store generic zap mapping
|
||||
self.zaps.add_zap(zap: zap)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
//
|
||||
// NotificationFormatter.swift
|
||||
// DamusNotificationService
|
||||
//
|
||||
// Created by Daniel D’Aquino on 2023-11-13.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UserNotifications
|
||||
|
||||
struct NotificationFormatter {
|
||||
static var shared = NotificationFormatter()
|
||||
|
||||
// MARK: - Formatting with NdbNote
|
||||
|
||||
func format_message(event: NdbNote) -> UNMutableNotificationContent? {
|
||||
let content = UNMutableNotificationContent()
|
||||
if let event_json_data = try? JSONEncoder().encode(event), // Must be encoded, as the notification completion handler requires this object to conform to `NSSecureCoding`
|
||||
let event_json_string = String(data: event_json_data, encoding: .utf8) {
|
||||
content.userInfo = [
|
||||
NDB_NOTE_JSON_USER_INFO_KEY: event_json_string
|
||||
]
|
||||
}
|
||||
switch event.known_kind {
|
||||
case .text:
|
||||
content.title = NSLocalizedString("Someone posted a note", comment: "Title label for push notification where someone posted a note")
|
||||
content.body = event.content
|
||||
break
|
||||
case .dm:
|
||||
content.title = NSLocalizedString("New message", comment: "Title label for push notifications where a direct message was sent to the user")
|
||||
content.body = NSLocalizedString("(Contents are encrypted)", comment: "Label on push notification indicating that the contents of the message are encrypted")
|
||||
break
|
||||
case .like:
|
||||
guard let reactionEmoji = to_reaction_emoji(ev: event) else {
|
||||
content.title = NSLocalizedString("Someone reacted to your note", comment: "Generic title label for push notifications where someone reacted to the user's post")
|
||||
break
|
||||
}
|
||||
content.title = NSLocalizedString("New note reaction", comment: "Title label for push notifications where someone reacted to the user's post with a specific emoji")
|
||||
content.body = String(format: NSLocalizedString("Someone reacted to your note with %@", comment: "Body label for push notifications where someone reacted to the user's post with a specific emoji"), reactionEmoji)
|
||||
break
|
||||
case .zap:
|
||||
content.title = NSLocalizedString("Someone zapped you ⚡️", comment: "Title label for a push notification where someone zapped the user")
|
||||
break
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
// MARK: - Formatting with LocalNotification
|
||||
|
||||
func format_message(displayName: String, notify: LocalNotification) -> (content: UNMutableNotificationContent, identifier: String)? {
|
||||
let content = UNMutableNotificationContent()
|
||||
var title = ""
|
||||
var identifier = ""
|
||||
|
||||
switch notify.type {
|
||||
case .tagged:
|
||||
title = String(format: NSLocalizedString("Tagged by %@", comment: "Tagged by heading in local notification"), displayName)
|
||||
identifier = "myMentionNotification"
|
||||
case .mention:
|
||||
title = String(format: NSLocalizedString("Mentioned by %@", comment: "Mentioned by heading in local notification"), displayName)
|
||||
identifier = "myMentionNotification"
|
||||
case .repost:
|
||||
title = String(format: NSLocalizedString("Reposted by %@", comment: "Reposted by heading in local notification"), displayName)
|
||||
identifier = "myBoostNotification"
|
||||
case .like:
|
||||
title = String(format: NSLocalizedString("%@ reacted with %@", comment: "Reacted by heading in local notification"), displayName, to_reaction_emoji(ev: notify.event) ?? "")
|
||||
identifier = "myLikeNotification"
|
||||
case .dm:
|
||||
title = displayName
|
||||
identifier = "myDMNotification"
|
||||
case .zap, .profile_zap:
|
||||
// not handled here. Try `format_message(displayName: String, notify: LocalNotification, state: HeadlessDamusState) async -> (content: UNMutableNotificationContent, identifier: String)?`
|
||||
return nil
|
||||
case .reply:
|
||||
title = String(format: NSLocalizedString("%@ replied to your note", comment: "Heading for local notification indicating a new reply"), displayName)
|
||||
identifier = "myReplyNotification"
|
||||
}
|
||||
content.title = title
|
||||
content.body = notify.content
|
||||
content.sound = UNNotificationSound.default
|
||||
content.userInfo = notify.to_lossy().to_user_info()
|
||||
|
||||
return (content, identifier)
|
||||
}
|
||||
|
||||
func format_message(displayName: String, notify: LocalNotification, state: HeadlessDamusState) async -> (content: UNMutableNotificationContent, identifier: String)? {
|
||||
// Try sync method first and return if it works
|
||||
if let sync_formatted_message = self.format_message(displayName: displayName, notify: notify) {
|
||||
return sync_formatted_message
|
||||
}
|
||||
|
||||
// If it does not work, try async formatting methods
|
||||
let content = UNMutableNotificationContent()
|
||||
|
||||
switch notify.type {
|
||||
case .zap, .profile_zap:
|
||||
guard let zap = await get_zap(from: notify.event, state: state) else {
|
||||
Log.debug("format_message: async get_zap failed", for: .push_notifications)
|
||||
return nil
|
||||
}
|
||||
content.title = Self.zap_notification_title(zap)
|
||||
content.body = Self.zap_notification_body(profiles: state.profiles, zap: zap)
|
||||
content.sound = UNNotificationSound.default
|
||||
content.userInfo = LossyLocalNotification(type: .zap, mention: .note(notify.event.id)).to_user_info()
|
||||
return (content, "myZapNotification")
|
||||
default:
|
||||
// The sync method should have taken care of this.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Formatting zap utility notifications
|
||||
|
||||
static func zap_notification_title(_ zap: Zap) -> String {
|
||||
if zap.private_request != nil {
|
||||
return NSLocalizedString("Private Zap", comment: "Title of notification when a private zap is received.")
|
||||
} else {
|
||||
return NSLocalizedString("Zap", comment: "Title of notification when a non-private zap is received.")
|
||||
}
|
||||
}
|
||||
|
||||
static func zap_notification_body(profiles: Profiles, zap: Zap, locale: Locale = Locale.current) -> String {
|
||||
let src = zap.request.ev
|
||||
let pk = zap.is_anon ? ANON_PUBKEY : src.pubkey
|
||||
|
||||
let profile_txn = profiles.lookup(id: pk)
|
||||
let profile = profile_txn?.unsafeUnownedValue
|
||||
let name = Profile.displayName(profile: profile, pubkey: pk).displayName.truncate(maxLength: 50)
|
||||
|
||||
let sats = NSNumber(value: (Double(zap.invoice.amount) / 1000.0))
|
||||
let formattedSats = format_msats_abbrev(zap.invoice.amount)
|
||||
|
||||
if src.content.isEmpty {
|
||||
let format = localizedStringFormat(key: "zap_notification_no_message", locale: locale)
|
||||
return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats, name)
|
||||
} else {
|
||||
let format = localizedStringFormat(key: "zap_notification_with_message", locale: locale)
|
||||
return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats, name, src.content)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
//
|
||||
// NotificationService.swift
|
||||
// DamusNotificationService
|
||||
//
|
||||
// Created by Daniel D’Aquino on 2023-11-10.
|
||||
//
|
||||
|
||||
import Kingfisher
|
||||
import ImageIO
|
||||
import UserNotifications
|
||||
import Foundation
|
||||
import UniformTypeIdentifiers
|
||||
import Intents
|
||||
|
||||
class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
var bestAttemptContent: UNMutableNotificationContent?
|
||||
|
||||
private func configureKingfisherCache() {
|
||||
guard let groupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.DAMUS_APP_GROUP_IDENTIFIER) else {
|
||||
return
|
||||
}
|
||||
|
||||
let cachePath = groupURL.appendingPathComponent(Constants.IMAGE_CACHE_DIRNAME)
|
||||
if let cache = try? ImageCache(name: "sharedCache", cacheDirectoryURL: cachePath) {
|
||||
KingfisherManager.shared.cache = cache
|
||||
}
|
||||
}
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
configureKingfisherCache()
|
||||
|
||||
self.contentHandler = contentHandler
|
||||
|
||||
guard let nostr_event_json = request.content.userInfo["nostr_event"] as? String,
|
||||
let nostr_event = NdbNote.owned_from_json(json: nostr_event_json)
|
||||
else {
|
||||
// No nostr event detected. Just display the original notification
|
||||
contentHandler(request.content)
|
||||
return;
|
||||
}
|
||||
|
||||
// Log that we got a push notification
|
||||
Log.debug("Got nostr event push notification from pubkey %s", for: .push_notifications, nostr_event.pubkey.hex())
|
||||
|
||||
guard let state = NotificationExtensionState() else {
|
||||
Log.debug("Failed to open nostrdb", for: .push_notifications)
|
||||
|
||||
// Something failed to initialize so let's go for the next best thing
|
||||
guard let improved_content = NotificationFormatter.shared.format_message(event: nostr_event) else {
|
||||
// We cannot format this nostr event. Suppress notification.
|
||||
contentHandler(UNNotificationContent())
|
||||
return
|
||||
}
|
||||
contentHandler(improved_content)
|
||||
return
|
||||
}
|
||||
|
||||
let sender_profile = {
|
||||
let txn = state.ndb.lookup_profile(nostr_event.pubkey)
|
||||
let profile = txn?.unsafeUnownedValue?.profile
|
||||
let picture = ((profile?.picture.map { URL(string: $0) }) ?? URL(string: robohash(nostr_event.pubkey)))!
|
||||
return ProfileBuf(picture: picture,
|
||||
name: profile?.name,
|
||||
display_name: profile?.display_name,
|
||||
nip05: profile?.nip05)
|
||||
}()
|
||||
let sender_pubkey = nostr_event.pubkey
|
||||
|
||||
// Don't show notification details that match mute list.
|
||||
// TODO: Remove this code block once we get notification suppression entitlement from Apple. It will be covered by the `guard should_display_notification` block
|
||||
if state.mutelist_manager.is_event_muted(nostr_event) {
|
||||
// We cannot really suppress muted notifications until we have the notification supression entitlement.
|
||||
// The best we can do if we ever get those muted notifications (which we generally won't due to server-side processing) is to obscure the details
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = NSLocalizedString("Muted event", comment: "Title for a push notification which has been muted")
|
||||
content.body = NSLocalizedString("This is an event that has been muted according to your mute list rules. We cannot suppress this notification, but we obscured the details to respect your preferences", comment: "Description for a push notification which has been muted, and explanation that we cannot suppress it")
|
||||
content.sound = UNNotificationSound.default
|
||||
contentHandler(content)
|
||||
return
|
||||
}
|
||||
|
||||
guard should_display_notification(state: state, event: nostr_event, mode: .push) else {
|
||||
Log.debug("should_display_notification failed", for: .push_notifications)
|
||||
// We should not display notification for this event. Suppress notification.
|
||||
// contentHandler(UNNotificationContent())
|
||||
// TODO: We cannot really suppress until we have the notification supression entitlement. Show the raw notification
|
||||
contentHandler(request.content)
|
||||
return
|
||||
}
|
||||
|
||||
guard let notification_object = generate_local_notification_object(from: nostr_event, state: state) else {
|
||||
Log.debug("generate_local_notification_object failed", for: .push_notifications)
|
||||
// We could not process this notification. Probably an unsupported nostr event kind. Suppress.
|
||||
// contentHandler(UNNotificationContent())
|
||||
// TODO: We cannot really suppress until we have the notification supression entitlement. Show the raw notification
|
||||
contentHandler(request.content)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Task {
|
||||
let sender_dn = DisplayName(name: sender_profile.name, display_name: sender_profile.display_name, pubkey: sender_pubkey)
|
||||
guard let (improvedContent, _) = await NotificationFormatter.shared.format_message(displayName: sender_dn.displayName, notify: notification_object, state: state) else {
|
||||
|
||||
Log.debug("NotificationFormatter.format_message failed", for: .push_notifications)
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
var options: [AnyHashable: Any] = [:]
|
||||
if let imageSource = CGImageSourceCreateWithURL(sender_profile.picture as CFURL, nil),
|
||||
let uti = CGImageSourceGetType(imageSource) {
|
||||
options[UNNotificationAttachmentOptionsTypeHintKey] = uti
|
||||
}
|
||||
|
||||
let attachment = try UNNotificationAttachment(identifier: sender_profile.picture.absoluteString, url: sender_profile.picture, options: options)
|
||||
improvedContent.attachments = [attachment]
|
||||
} catch {
|
||||
Log.error("failed to get notification attachment: %s", for: .push_notifications, error.localizedDescription)
|
||||
}
|
||||
|
||||
let kind = nostr_event.known_kind
|
||||
|
||||
// these aren't supported yet
|
||||
if !(kind == .text || kind == .dm) {
|
||||
contentHandler(improvedContent)
|
||||
return
|
||||
}
|
||||
|
||||
// rich communication notifications for kind1, dms, etc
|
||||
|
||||
let message_intent = await message_intent_from_note(ndb: state.ndb,
|
||||
sender_profile: sender_profile,
|
||||
content: improvedContent.body,
|
||||
note: nostr_event,
|
||||
our_pubkey: state.keypair.pubkey)
|
||||
|
||||
improvedContent.threadIdentifier = nostr_event.thread_id().hex()
|
||||
improvedContent.categoryIdentifier = "COMMUNICATION"
|
||||
|
||||
let interaction = INInteraction(intent: message_intent, response: nil)
|
||||
interaction.direction = .incoming
|
||||
do {
|
||||
try await interaction.donate()
|
||||
let updated = try improvedContent.updating(from: message_intent)
|
||||
contentHandler(updated)
|
||||
} catch {
|
||||
Log.error("failed to donate interaction: %s", for: .push_notifications, error.localizedDescription)
|
||||
contentHandler(improvedContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func serviceExtensionTimeWillExpire() {
|
||||
// Called just before the extension will be terminated by the system.
|
||||
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
|
||||
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
|
||||
contentHandler(bestAttemptContent)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct ProfileBuf {
|
||||
let picture: URL
|
||||
let name: String?
|
||||
let display_name: String?
|
||||
let nip05: String?
|
||||
}
|
||||
|
||||
func message_intent_from_note(ndb: Ndb, sender_profile: ProfileBuf, content: String, note: NdbNote, our_pubkey: Pubkey) async -> INSendMessageIntent {
|
||||
let sender_pk = note.pubkey
|
||||
let sender = await profile_to_inperson(name: sender_profile.name,
|
||||
display_name: sender_profile.display_name,
|
||||
picture: sender_profile.picture.absoluteString,
|
||||
nip05: sender_profile.nip05,
|
||||
pubkey: sender_pk,
|
||||
our_pubkey: our_pubkey)
|
||||
|
||||
let conversationIdentifier = note.thread_id().hex()
|
||||
var recipients: [INPerson] = []
|
||||
var pks: [Pubkey] = []
|
||||
let meta = INSendMessageIntentDonationMetadata()
|
||||
|
||||
// gather recipients
|
||||
if let recipient_note_id = note.direct_replies() {
|
||||
let replying_to = ndb.lookup_note(recipient_note_id)
|
||||
if let replying_to_pk = replying_to?.unsafeUnownedValue?.pubkey {
|
||||
meta.isReplyToCurrentUser = replying_to_pk == our_pubkey
|
||||
|
||||
if replying_to_pk != sender_pk {
|
||||
// we push the actual person being replied to first
|
||||
pks.append(replying_to_pk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pubkeys = Array(note.referenced_pubkeys)
|
||||
meta.recipientCount = pubkeys.count
|
||||
if pubkeys.contains(sender_pk) {
|
||||
meta.recipientCount -= 1
|
||||
}
|
||||
|
||||
for pk in pubkeys.prefix(3) {
|
||||
if pk == sender_pk || pks.contains(pk) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !meta.isReplyToCurrentUser && pk == our_pubkey {
|
||||
meta.mentionsCurrentUser = true
|
||||
}
|
||||
|
||||
pks.append(pk)
|
||||
}
|
||||
|
||||
for pk in pks {
|
||||
let recipient = await pubkey_to_inperson(ndb: ndb, pubkey: pk, our_pubkey: our_pubkey)
|
||||
recipients.append(recipient)
|
||||
}
|
||||
|
||||
// we enable default formatting this way
|
||||
var groupName = INSpeakableString(spokenPhrase: "")
|
||||
|
||||
// otherwise we just say its a DM
|
||||
if note.known_kind == .dm {
|
||||
groupName = INSpeakableString(spokenPhrase: "DM")
|
||||
}
|
||||
|
||||
let intent = INSendMessageIntent(recipients: recipients,
|
||||
outgoingMessageType: .outgoingMessageText,
|
||||
content: content,
|
||||
speakableGroupName: groupName,
|
||||
conversationIdentifier: conversationIdentifier,
|
||||
serviceName: "kind\(note.kind)",
|
||||
sender: sender,
|
||||
attachments: nil)
|
||||
intent.donationMetadata = meta
|
||||
|
||||
// this is needed for recipients > 0
|
||||
if let img = sender.image {
|
||||
intent.setImage(img, forParameterNamed: \.speakableGroupName)
|
||||
}
|
||||
|
||||
return intent
|
||||
}
|
||||
|
||||
func pubkey_to_inperson(ndb: Ndb, pubkey: Pubkey, our_pubkey: Pubkey) async -> INPerson {
|
||||
let profile_txn = ndb.lookup_profile(pubkey)
|
||||
let profile = profile_txn?.unsafeUnownedValue?.profile
|
||||
let name = profile?.name
|
||||
let display_name = profile?.display_name
|
||||
let nip05 = profile?.nip05
|
||||
let picture = profile?.picture
|
||||
|
||||
return await profile_to_inperson(name: name,
|
||||
display_name: display_name,
|
||||
picture: picture,
|
||||
nip05: nip05,
|
||||
pubkey: pubkey,
|
||||
our_pubkey: our_pubkey)
|
||||
}
|
||||
|
||||
func fetch_pfp(picture: URL) async throws -> RetrieveImageResult {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
KingfisherManager.shared.retrieveImage(with: Kingfisher.ImageResource(downloadURL: picture)) { result in
|
||||
switch result {
|
||||
case .success(let img):
|
||||
continuation.resume(returning: img)
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func profile_to_inperson(name: String?, display_name: String?, picture: String?, nip05: String?, pubkey: Pubkey, our_pubkey: Pubkey) async -> INPerson {
|
||||
let npub = pubkey.npub
|
||||
let handle = INPersonHandle(value: npub, type: .unknown)
|
||||
var aliases: [INPersonHandle] = []
|
||||
|
||||
if let nip05 {
|
||||
aliases.append(INPersonHandle(value: nip05, type: .emailAddress))
|
||||
}
|
||||
|
||||
let nostrName = DisplayName(name: name, display_name: display_name, pubkey: pubkey)
|
||||
let nameComponents = nostrName.nameComponents()
|
||||
let displayName = nostrName.displayName
|
||||
let contactIdentifier = npub
|
||||
let customIdentifier = npub
|
||||
let suggestionType = INPersonSuggestionType.socialProfile
|
||||
|
||||
var image: INImage? = nil
|
||||
|
||||
if let picture,
|
||||
let url = URL(string: picture),
|
||||
let img = try? await fetch_pfp(picture: url),
|
||||
let imgdata = img.data()
|
||||
{
|
||||
image = INImage(imageData: imgdata)
|
||||
} else {
|
||||
Log.error("Failed to fetch pfp (%s) for %s", for: .push_notifications, picture ?? "nil", displayName)
|
||||
}
|
||||
|
||||
let person = INPerson(personHandle: handle,
|
||||
nameComponents: nameComponents,
|
||||
displayName: displayName,
|
||||
image: image,
|
||||
contactIdentifier: contactIdentifier,
|
||||
customIdentifier: customIdentifier,
|
||||
isMe: pubkey == our_pubkey,
|
||||
suggestionType: suggestionType
|
||||
)
|
||||
|
||||
return person
|
||||
}
|
||||
|
||||
func robohash(_ pk: Pubkey) -> String {
|
||||
return "https://robohash.org/" + pk.hex()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>1C8F.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
13
Makefile
13
Makefile
@@ -1,13 +0,0 @@
|
||||
|
||||
all: nostrscript/primal.wasm
|
||||
|
||||
nostrscript/%.wasm: nostrscript/%.ts nostrscript/nostr.ts Makefile
|
||||
asc $< --runtime stub --outFile $@ --optimize
|
||||
|
||||
tags:
|
||||
find damus-c -name '*.c' -or -name '*.h' | xargs ctags
|
||||
|
||||
clean:
|
||||
rm nostrscript/*.wasm
|
||||
|
||||
.PHONY: tags
|
||||
@@ -1,32 +1,4 @@
|
||||
// swift-tools-version: 6.0
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "damus",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.macOS(.v12)
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
name: "damus",
|
||||
targets: ["damus"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/jb55/secp256k1.swift.git", branch: "main")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "damus",
|
||||
dependencies: [
|
||||
.product(name: "secp256k1", package: "secp256k1.swift")
|
||||
],
|
||||
path: "damus"),
|
||||
.testTarget(
|
||||
name: "damusTests",
|
||||
dependencies: ["damus"],
|
||||
path: "damusTests"),
|
||||
]
|
||||
)
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/daltoniam/Starscream.git", majorVersion: 4),
|
||||
.Package(url: "https://github.com/jb55/secp256k1.swift.git", branch: "main")
|
||||
]
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>1C8F.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
125
Purple.storekit
125
Purple.storekit
@@ -1,125 +0,0 @@
|
||||
{
|
||||
"identifier" : "64C21A2D",
|
||||
"nonRenewingSubscriptions" : [
|
||||
|
||||
],
|
||||
"products" : [
|
||||
|
||||
],
|
||||
"settings" : {
|
||||
"_applicationInternalID" : "1628663131",
|
||||
"_developerTeamID" : "XK7H4JAB3D",
|
||||
"_failTransactionsEnabled" : false,
|
||||
"_lastSynchronizedDate" : 704848066.26849198,
|
||||
"_locale" : "en_US",
|
||||
"_storefront" : "USA",
|
||||
"_storeKitErrors" : [
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Load Products"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Purchase"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Verification"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "App Store Sync"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Subscription Status"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "App Transaction"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Manage Subscriptions Sheet"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Refund Request Sheet"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Offer Code Redeem Sheet"
|
||||
}
|
||||
]
|
||||
},
|
||||
"subscriptionGroups" : [
|
||||
{
|
||||
"id" : "21283177",
|
||||
"localizations" : [
|
||||
|
||||
],
|
||||
"name" : "Purple",
|
||||
"subscriptions" : [
|
||||
{
|
||||
"adHocOffers" : [
|
||||
|
||||
],
|
||||
"codeOffers" : [
|
||||
|
||||
],
|
||||
"displayPrice" : "6.99",
|
||||
"familyShareable" : false,
|
||||
"groupNumber" : 1,
|
||||
"internalID" : "6446591615",
|
||||
"introductoryOffer" : null,
|
||||
"localizations" : [
|
||||
{
|
||||
"description" : "Support damus development with Damus Purple!",
|
||||
"displayName" : "Damus Purple",
|
||||
"locale" : "en_CA"
|
||||
}
|
||||
],
|
||||
"productID" : "purple",
|
||||
"recurringSubscriptionPeriod" : "P1M",
|
||||
"referenceName" : "Purple",
|
||||
"subscriptionGroupID" : "21283177",
|
||||
"type" : "RecurringSubscription"
|
||||
},
|
||||
{
|
||||
"adHocOffers" : [
|
||||
|
||||
],
|
||||
"codeOffers" : [
|
||||
|
||||
],
|
||||
"displayPrice" : "69.99",
|
||||
"familyShareable" : false,
|
||||
"groupNumber" : 2,
|
||||
"internalID" : "6448764101",
|
||||
"introductoryOffer" : null,
|
||||
"localizations" : [
|
||||
|
||||
],
|
||||
"productID" : "purpleyearly",
|
||||
"recurringSubscriptionPeriod" : "P1Y",
|
||||
"referenceName" : "Purple Yearly",
|
||||
"subscriptionGroupID" : "21283177",
|
||||
"type" : "RecurringSubscription"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"version" : {
|
||||
"major" : 3,
|
||||
"minor" : 0
|
||||
}
|
||||
}
|
||||
114
README.md
114
README.md
@@ -1,84 +1,37 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="./damus/Assets.xcassets/damus-home.imageset/damus-home@2x.png" alt="Damus Logo" title="Damus logo" width=""/>
|
||||
# damus
|
||||
|
||||
# Damus
|
||||
A twitter-like [nostr][nostr] client for iPhone, iPad and MacOS.
|
||||
|
||||
The social network you control
|
||||
|
||||
A twitter-like [nostr][nostr] client for iPhone, iPad and MacOS.
|
||||
|
||||
[](/LICENSE)
|
||||
|
||||
## Download and Install
|
||||
|
||||
[](https://apps.apple.com/us/app/damus/id1628663131)
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
iOS 16.0+ • macOS 13.0+
|
||||
|
||||
<img src="./demo1.png" width="70%" height="50%" />
|
||||
|
||||
</div>
|
||||
<img src="./ss.png" width="50%" height="50%" />
|
||||
|
||||
[nostr]: https://github.com/fiatjaf/nostr
|
||||
|
||||
## How is Damus better than X/Twitter?
|
||||
There are no toxic algorithms.\
|
||||
You can send or receive zaps (satoshis) without asking for permission.\
|
||||
[There is no central database](https://fiatjaf.com/nostr.html). Therefore, Damus is censorship resistant.\
|
||||
There are no ads.\
|
||||
You don't have to reveal sensitive personal information to sign up.\
|
||||
No email is required. \
|
||||
No phone number is required. \
|
||||
Damus is free and open source software. \
|
||||
There is no Big Tech moat. Therefore, seamless interoperability with thousands or millions of other nostr apps is possible, and is how [Damus and nostr win](https://www.youtube.com/watch?v=qTixqS-W1yo).
|
||||
|
||||
## If there are no ads, how is Damus funded?
|
||||
Damus offers a paid subscription 🟣 purple 🟣 https://damus.io/purple/. \
|
||||
Initial benefits include a unique subscriber number, subscriber badge, and auto-translate powered by DeepL.
|
||||
|
||||
Damus has also graciously received donations or grants from hundreds of Damus users, [Opensats](https://opensats.org/), and the [Human Rights Foundation](https://hrf.org/).
|
||||
|
||||
## Spec Compliance
|
||||
|
||||
damus implements the following [Nostr Implementation Possibilities][nips]
|
||||
|
||||
- [NIP-01: Basic protocol flow][nip01]
|
||||
- [NIP-04: Encrypted direct message][nip04]
|
||||
- [NIP-08: Mentions][nip08]
|
||||
- [NIP-10: Reply conventions][nip10]
|
||||
- [NIP-12: Generic tag queries (hashtags)][nip12]
|
||||
- [NIP-19: bech32-encoded entities][NIP19]
|
||||
- [NIP-21: nostr: URI scheme][NIP21]
|
||||
- [NIP-25: Reactions][NIP25]
|
||||
- [NIP-42: Authentication of clients to relays][nip42]
|
||||
- [NIP-56: Reporting][nip56]
|
||||
|
||||
[nips]: https://github.com/nostr-protocol/nips
|
||||
[nip01]: https://github.com/nostr-protocol/nips/blob/master/01.md
|
||||
[nip04]: https://github.com/nostr-protocol/nips/blob/master/04.md
|
||||
[nip08]: https://github.com/nostr-protocol/nips/blob/master/08.md
|
||||
[nip10]: https://github.com/nostr-protocol/nips/blob/master/10.md
|
||||
[nip12]: https://github.com/nostr-protocol/nips/blob/master/12.md
|
||||
[nip19]: https://github.com/nostr-protocol/nips/blob/master/19.md
|
||||
[nip21]: https://github.com/nostr-protocol/nips/blob/master/21.md
|
||||
[nip25]: https://github.com/nostr-protocol/nips/blob/master/25.md
|
||||
[nip42]: https://github.com/nostr-protocol/nips/blob/master/42.md
|
||||
[nip56]: https://github.com/nostr-protocol/nips/blob/master/56.md
|
||||
|
||||
|
||||
## Getting Started on Damus
|
||||
|
||||
### Damus iOS
|
||||
1) Get the Damus app on the iOS App Store: https://apps.apple.com/ca/app/damus/id1628663131
|
||||
1) Get the Damus app on TestFlight: https://testflight.apple.com/join/CLwjLxWl
|
||||
|
||||
#### ⚙️ Settings (gear icon, top right)
|
||||
- Relays: You can add more relays to send your notes to by tapping the "+".
|
||||
- Find more relays to add: https://nostr.info/relays/
|
||||
- Public Key (pubkey): Your public, personal address and how people can find and tag you
|
||||
- Secret Key: Your *private* key unique to you. Never share your private key publicly and share with other clients at your own risk!
|
||||
- Secret Key: Your *private* key unique to you. Never share your private key publically and share with other clients at your own risk!
|
||||
- Save your keys somewhere safe
|
||||
- Log out
|
||||
|
||||
@@ -92,15 +45,19 @@ damus implements the following [Nostr Implementation Possibilities][nips]
|
||||
1. Search their username in the search bar at the top of the 🔍 Global Feed and click their profile
|
||||
2. Tap the 🔑 icon which will copy their pubkey to your clipboard
|
||||
3. Go back to your 🏠 Personal Feed and tap the blue + button to compose your Note
|
||||
4. Add @ directly followed by the pubkey (e.g., `@npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s`)
|
||||
- You can also tap the ellipsis menu of a Note (three dots in top right of note) to grab their User ID aka pubkey or Note ID to link directly to a Note.
|
||||
4. Add @ direcly followed by the pubkey (e.g., `@npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s`)
|
||||
- You can also long-press a Note to grab their User ID aka pubkey or Note ID to link directly to a Note.
|
||||
- Currently you can't delete your Notes in the iOS app
|
||||
- Share images by pasting the image url which you can grab from nostr.build, imgbb, imgur, etc. (i.e., `https://i.ibb.co/2SHZbwm/alpha60.jpg`). Currently images only load for people you follow in the 🏠 Personal Feed. Images are not automatically loaded in 🔍 Global Feed
|
||||
- Share images by pasting the image url which you can grab from imgbb, imgur, etc. (i.e., `(https://i.ibb.co/2SHZbwm/alpha60.jpg)`). Currently images only load for people you follow in the 🏠 Personal Feed. Images are not automatically loaded in 🔍 Global Feed
|
||||
- Engaging with Notes
|
||||
- 💬 Replying to a Note: Tap the chat icon underneath the note. This will show up in the users’ notifications and in your 🏠 Personal and 🔍 Global Feeds
|
||||
- ♺ Reposts: Tap the repost icon which will show up in your 🏠 Personal and 🔍 Global Feeds
|
||||
- ♡ Likes: Tap the heart icon. Users will not get a notification, and cannot see who liked their note (currently, web clients can see your pfp only)
|
||||
|
||||
- Formatting Notes (may not format as intended in other web clients)
|
||||
- Italics: 1 asterisk `*italic*`
|
||||
- Bold: 2 asterisk `**bold**`
|
||||
- Strikethrough: 1 tildes `~strikethrough~`
|
||||
- Code: 1 back-tick `` `code` ``
|
||||
|
||||
#### 💬 Encrypted DMs (chat app, bottom navigation)
|
||||
- Tap the chat icon and you'll notice there's nothing to see at first. Go to a user profile and tap the 💬 chat icon next to the follow button to begin a DM
|
||||
@@ -118,9 +75,7 @@ damus implements the following [Nostr Implementation Possibilities][nips]
|
||||
4. For PFP, insert a URL containing your image (support video: https://cdn.jb55.com/vid/pfp-editor.mp4)
|
||||
5. Save
|
||||
|
||||
|
||||
#### ⚡️ Request Sats
|
||||
Paste an invoice from your favorite LN wallet.
|
||||
(Sats or Satoshis are the smallest denomination of bitcoin)
|
||||
|
||||
**Alby (browser extension)**
|
||||
@@ -136,52 +91,19 @@ Paste an invoice from your favorite LN wallet.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributors welcome! Start by examining known issues: https://github.com/damus-io/damus/issues.
|
||||
Contributors welcome! [Email patches][git-send-email] to jb55@jb55.com are preferred, but I accept PRs on github as well.
|
||||
|
||||
### Mailing lists
|
||||
[git-send-email]: http://git-send-email.io
|
||||
|
||||
We have a few mailing lists that anyone can join to get involved in damus development:
|
||||
## git log bot
|
||||
|
||||
- [dev][dev-list] - development discussions
|
||||
- [patches][patches-list] - code submission and review
|
||||
- [product][product-list] - product discussions
|
||||
- [design][design-list] - design discussions
|
||||
npub1fjtdwclt9lspjy8huu3qklr7eklp5uq90u6yh8mec290pqxraccqlufnas
|
||||
|
||||
[dev-list]: https://damus.io/list/dev
|
||||
[patches-list]: https://damus.io/list/patches
|
||||
[product-list]: https://damus.io/list/product
|
||||
[design-list]: https://damus.io/list/design
|
||||
### Awards
|
||||
|
||||
### Contributing
|
||||
|
||||
See [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md)
|
||||
|
||||
### Privacy
|
||||
Your internet protocol (IP) address is exposed to the relays you connect to, and third party media hosters (e.g. nostr.build, imgur.com, giphy.com, youtube.com etc.) that render on Damus. If you want to improve your privacy, consider utilizing a service that masks your IP address (e.g. a VPN) from trackers online.
|
||||
|
||||
The relay also learns which public keys you are requesting, meaning your public key will be tied to your IP address.
|
||||
|
||||
It is public information which other profiles (npubs) you are exchanging DMs with. The content of the DMs is encrypted.
|
||||
|
||||
### Translations
|
||||
|
||||
Translators welcome! Join the [Transifex][transifex] project.
|
||||
|
||||
All user-facing strings must have a comment in order to provide context to translators. If a SwiftUI component has a `comment` parameter, use that. Otherwise, wrap your string with `NSLocalizedString` with the `comment` field populated.
|
||||
|
||||
[transifex]: https://explore.transifex.com/damus/damus-ios/
|
||||
|
||||
### Awards
|
||||
|
||||
Damus lead dev and founder Will awards developers with satoshis!
|
||||
There may be nostr badges awarded for contributors in the future... :)
|
||||
|
||||
|
||||
First contributors:
|
||||
|
||||
1. @randymcmillan
|
||||
2. @jcarucci27
|
||||
|
||||
### git log bot
|
||||
|
||||
npub1fjtdwclt9lspjy8huu3qklr7eklp5uq90u6yh8mec290pqxraccqlufnas
|
||||
|
||||
1050
damus Localizations/en-US.xcloc/Localized Contents/en-US.xliff
Normal file
1050
damus Localizations/en-US.xcloc/Localized Contents/en-US.xliff
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "Damus";
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "damus";
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "Granting Damus access to your photos allows you to save images.";
|
||||
Binary file not shown.
@@ -2,6 +2,22 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>collapsed_event_view_other_notes</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>··· %#@NOTES@ ···</string>
|
||||
<key>NOTES</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>%d other note</string>
|
||||
<key>other</key>
|
||||
<string>%d other notes</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>followers_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
@@ -13,11 +29,9 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Seguace</string>
|
||||
<key>many</key>
|
||||
<string>Seguaci</string>
|
||||
<string>Follower</string>
|
||||
<key>other</key>
|
||||
<string>Seguaci</string>
|
||||
<string>Followers</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>reactions_count</key>
|
||||
@@ -31,11 +45,9 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Reazione</string>
|
||||
<key>many</key>
|
||||
<string>Reazioni</string>
|
||||
<string>Reaction</string>
|
||||
<key>other</key>
|
||||
<string>Reazioni</string>
|
||||
<string>Reactions</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>relays_count</key>
|
||||
@@ -49,47 +61,45 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Relè</string>
|
||||
<key>many</key>
|
||||
<string>Relè</string>
|
||||
<string>Relay</string>
|
||||
<key>other</key>
|
||||
<string>Relè</string>
|
||||
<string>Relays</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>replying_to_one_and_others</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@OTHERS@</string>
|
||||
<string>Replying to %@%#@OTHERS@</string>
|
||||
<key>OTHERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>zero</key>
|
||||
<string></string>
|
||||
<key>one</key>
|
||||
<string>Rispondendo a %2$@ & %1$d altro</string>
|
||||
<key>many</key>
|
||||
<string>Rispondendo a %2$@ & %1$d altri</string>
|
||||
<string> & %d other</string>
|
||||
<key>other</key>
|
||||
<string>Rispondendo a %2$@ & %1$d altri</string>
|
||||
<string> & %d others</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>replying_to_two_and_others</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@OTHERS@</string>
|
||||
<string>Replying to %@, %@%#@OTHERS@</string>
|
||||
<key>OTHERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>zero</key>
|
||||
<string></string>
|
||||
<key>one</key>
|
||||
<string>Rispondendo a %2$@, %3$@ & %1$d altro</string>
|
||||
<key>many</key>
|
||||
<string>Rispondendo a %2$@, %3$@ & %1$d altri</string>
|
||||
<string> & %d other</string>
|
||||
<key>other</key>
|
||||
<string>Rispondendo a %2$@, %3$@ & %1$d altri</string>
|
||||
<string> & %d others</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>reposts_count</key>
|
||||
@@ -104,10 +114,8 @@
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Repost</string>
|
||||
<key>many</key>
|
||||
<string>I Repost</string>
|
||||
<key>other</key>
|
||||
<string>I Repost</string>
|
||||
<string>Reposts</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>sats_count</key>
|
||||
@@ -122,28 +130,24 @@
|
||||
<string>@</string>
|
||||
<key>one</key>
|
||||
<string>%2$@ sat</string>
|
||||
<key>many</key>
|
||||
<string>%2$@ sats</string>
|
||||
<key>other</key>
|
||||
<string>%2$@ sats</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>zaps_count</key>
|
||||
<key>tips_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@ZAPS@</string>
|
||||
<key>ZAPS</key>
|
||||
<string>%#@TIPS@</string>
|
||||
<key>TIPS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Zap</string>
|
||||
<key>many</key>
|
||||
<string>Zaps</string>
|
||||
<string>Tip</string>
|
||||
<key>other</key>
|
||||
<string>Zaps</string>
|
||||
<string>Tips</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
@@ -3,10 +3,10 @@
|
||||
"project" : "damus.xcodeproj",
|
||||
"targetLocale" : "en-US",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "16F6",
|
||||
"toolBuildNumber" : "14C18",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
"toolName" : "Xcode",
|
||||
"toolVersion" : "16.4"
|
||||
"toolVersion" : "14.2"
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
1234
damus Localizations/es_419.xcloc/Localized Contents/es_419.xliff
Normal file
1234
damus Localizations/es_419.xcloc/Localized Contents/es_419.xliff
Normal file
File diff suppressed because it is too large
Load Diff
@@ -91,12 +91,13 @@ int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t dat
|
||||
return 1;
|
||||
}
|
||||
|
||||
bech32_encoding bech32_decode_len(char* hrp, uint8_t *data, size_t *data_len, const char *input, size_t input_len) {
|
||||
bech32_encoding bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input, size_t max_input_len) {
|
||||
uint32_t chk = 1;
|
||||
size_t i;
|
||||
size_t input_len = strlen(input);
|
||||
size_t hrp_len;
|
||||
int have_lower = 0, have_upper = 0;
|
||||
if (input_len < 8) {
|
||||
if (input_len < 8 || input_len > max_input_len) {
|
||||
return BECH32_ENCODING_NONE;
|
||||
}
|
||||
*data_len = 0;
|
||||
@@ -153,14 +154,6 @@ bech32_encoding bech32_decode_len(char* hrp, uint8_t *data, size_t *data_len, co
|
||||
}
|
||||
}
|
||||
|
||||
bech32_encoding bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input, size_t max_input_len) {
|
||||
size_t len = strlen(input);
|
||||
if (len > max_input_len) {
|
||||
return BECH32_ENCODING_NONE;
|
||||
}
|
||||
return bech32_decode_len(hrp, data, data_len, input, len);
|
||||
}
|
||||
|
||||
int bech32_convert_bits(uint8_t* out, size_t* outlen, int outbits, const uint8_t* in, size_t inlen, int inbits, int pad) {
|
||||
uint32_t val = 0;
|
||||
int bits = 0;
|
||||
|
||||
@@ -118,14 +118,6 @@ bech32_encoding bech32_decode(
|
||||
size_t max_input_len
|
||||
);
|
||||
|
||||
bech32_encoding bech32_decode_len(
|
||||
char *hrp,
|
||||
uint8_t *data,
|
||||
size_t *data_len,
|
||||
const char *input,
|
||||
size_t input_len
|
||||
);
|
||||
|
||||
/* Helper from bech32: translates inbits-bit bytes to outbits-bit bytes.
|
||||
* @outlen is incremented as bytes are added.
|
||||
* @pad is true if we're to pad, otherwise truncate last byte if necessary
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
//
|
||||
// block.h
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-04-09.
|
||||
//
|
||||
|
||||
#ifndef block_h
|
||||
#define block_h
|
||||
|
||||
#include "nostr_bech32.h"
|
||||
#include "str_block.h"
|
||||
|
||||
#define MAX_BLOCKS 1024
|
||||
|
||||
enum block_type {
|
||||
BLOCK_HASHTAG = 1,
|
||||
BLOCK_TEXT = 2,
|
||||
BLOCK_MENTION_INDEX = 3,
|
||||
BLOCK_MENTION_BECH32 = 4,
|
||||
BLOCK_URL = 5,
|
||||
BLOCK_INVOICE = 6,
|
||||
};
|
||||
|
||||
|
||||
typedef struct invoice_block {
|
||||
struct str_block invstr;
|
||||
union {
|
||||
struct bolt11 *bolt11;
|
||||
};
|
||||
} invoice_block_t;
|
||||
|
||||
typedef struct mention_bech32_block {
|
||||
struct str_block str;
|
||||
struct nostr_bech32 bech32;
|
||||
} mention_bech32_block_t;
|
||||
|
||||
typedef struct note_block {
|
||||
enum block_type type;
|
||||
union {
|
||||
struct str_block str;
|
||||
struct invoice_block invoice;
|
||||
struct mention_bech32_block mention_bech32;
|
||||
int mention_index;
|
||||
} block;
|
||||
} block_t;
|
||||
|
||||
typedef struct note_blocks {
|
||||
int words;
|
||||
int num_blocks;
|
||||
struct note_block *blocks;
|
||||
} blocks_t;
|
||||
|
||||
void blocks_init(struct note_blocks *blocks);
|
||||
void blocks_free(struct note_blocks *blocks);
|
||||
|
||||
#endif /* block_h */
|
||||
746
damus-c/cursor.h
746
damus-c/cursor.h
@@ -1,746 +0,0 @@
|
||||
|
||||
#ifndef JB55_CURSOR_H
|
||||
#define JB55_CURSOR_H
|
||||
|
||||
#include "typedefs.h"
|
||||
#include "varint.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define unlikely(x) __builtin_expect((x),0)
|
||||
#define likely(x) __builtin_expect((x),1)
|
||||
|
||||
struct cursor {
|
||||
unsigned char *start;
|
||||
unsigned char *p;
|
||||
unsigned char *end;
|
||||
};
|
||||
|
||||
struct array {
|
||||
struct cursor cur;
|
||||
unsigned int elem_size;
|
||||
};
|
||||
|
||||
static inline void reset_cursor(struct cursor *cursor)
|
||||
{
|
||||
cursor->p = cursor->start;
|
||||
}
|
||||
|
||||
static inline void wipe_cursor(struct cursor *cursor)
|
||||
{
|
||||
reset_cursor(cursor);
|
||||
memset(cursor->start, 0, cursor->end - cursor->start);
|
||||
}
|
||||
|
||||
static inline void make_cursor(u8 *start, u8 *end, struct cursor *cursor)
|
||||
{
|
||||
cursor->start = start;
|
||||
cursor->p = start;
|
||||
cursor->end = end;
|
||||
}
|
||||
|
||||
static inline void make_array(struct array *a, u8* start, u8 *end, unsigned int elem_size)
|
||||
{
|
||||
make_cursor(start, end, &a->cur);
|
||||
a->elem_size = elem_size;
|
||||
}
|
||||
|
||||
static inline int cursor_eof(struct cursor *c)
|
||||
{
|
||||
return c->p == c->end;
|
||||
}
|
||||
|
||||
static inline void *cursor_malloc(struct cursor *mem, unsigned long size)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if (mem->p + size > mem->end) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = mem->p;
|
||||
mem->p += size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void *cursor_alloc(struct cursor *mem, unsigned long size)
|
||||
{
|
||||
void *ret;
|
||||
if (!(ret = cursor_malloc(mem, size))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int cursor_slice(struct cursor *mem, struct cursor *slice, size_t size)
|
||||
{
|
||||
u8 *p;
|
||||
if (!(p = cursor_alloc(mem, size))) {
|
||||
return 0;
|
||||
}
|
||||
make_cursor(p, mem->p, slice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static inline void copy_cursor(struct cursor *src, struct cursor *dest)
|
||||
{
|
||||
dest->start = src->start;
|
||||
dest->p = src->p;
|
||||
dest->end = src->end;
|
||||
}
|
||||
|
||||
static inline int cursor_skip(struct cursor *cursor, int n)
|
||||
{
|
||||
if (cursor->p + n >= cursor->end)
|
||||
return 0;
|
||||
|
||||
cursor->p += n;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int pull_byte(struct cursor *cursor, u8 *c)
|
||||
{
|
||||
if (unlikely(cursor->p >= cursor->end))
|
||||
return 0;
|
||||
|
||||
*c = *cursor->p;
|
||||
cursor->p++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int parse_byte(struct cursor *cursor, u8 *c)
|
||||
{
|
||||
if (unlikely(cursor->p >= cursor->end))
|
||||
return 0;
|
||||
|
||||
*c = *cursor->p;
|
||||
//cursor->p++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int parse_char(struct cursor *cur, char c) {
|
||||
if (cur->p >= cur->end)
|
||||
return 0;
|
||||
|
||||
if (*cur->p == c) {
|
||||
cur->p++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int peek_char(struct cursor *cur, int ind) {
|
||||
if ((cur->p + ind < cur->start) || (cur->p + ind >= cur->end))
|
||||
return -1;
|
||||
|
||||
return *(cur->p + ind);
|
||||
}
|
||||
|
||||
static inline int cursor_pull_c_str(struct cursor *cursor, const char **str)
|
||||
{
|
||||
*str = (const char*)cursor->p;
|
||||
|
||||
for (; cursor->p < cursor->end; cursor->p++) {
|
||||
if (*cursor->p == 0) {
|
||||
cursor->p++;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static inline int cursor_push_byte(struct cursor *cursor, u8 c)
|
||||
{
|
||||
if (unlikely(cursor->p + 1 > cursor->end)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*cursor->p = c;
|
||||
cursor->p++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int cursor_pull(struct cursor *cursor, u8 *data, int len)
|
||||
{
|
||||
if (unlikely(cursor->p + len > cursor->end)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(data, cursor->p, len);
|
||||
cursor->p += len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int pull_data_into_cursor(struct cursor *cursor,
|
||||
struct cursor *dest,
|
||||
unsigned char **data,
|
||||
int len)
|
||||
{
|
||||
int ok;
|
||||
|
||||
if (unlikely(dest->p + len > dest->end)) {
|
||||
printf("not enough room in dest buffer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ok = cursor_pull(cursor, dest->p, len);
|
||||
if (!ok) return 0;
|
||||
|
||||
*data = dest->p;
|
||||
dest->p += len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int cursor_dropn(struct cursor *cur, int size, int n)
|
||||
{
|
||||
if (n == 0)
|
||||
return 1;
|
||||
|
||||
if (unlikely(cur->p - size*n < cur->start)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cur->p -= size*n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int cursor_drop(struct cursor *cur, int size)
|
||||
{
|
||||
return cursor_dropn(cur, size, 1);
|
||||
}
|
||||
|
||||
static inline unsigned char *cursor_topn(struct cursor *cur, int len, int n)
|
||||
{
|
||||
n += 1;
|
||||
if (unlikely(cur->p - len*n < cur->start)) {
|
||||
return NULL;
|
||||
}
|
||||
return cur->p - len*n;
|
||||
}
|
||||
|
||||
static inline unsigned char *cursor_top(struct cursor *cur, int len)
|
||||
{
|
||||
if (unlikely(cur->p - len < cur->start)) {
|
||||
return NULL;
|
||||
}
|
||||
return cur->p - len;
|
||||
}
|
||||
|
||||
static inline int cursor_top_int(struct cursor *cur, int *i)
|
||||
{
|
||||
u8 *p;
|
||||
if (unlikely(!(p = cursor_top(cur, sizeof(*i))))) {
|
||||
return 0;
|
||||
}
|
||||
*i = *((int*)p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int cursor_pop(struct cursor *cur, u8 *data, int len)
|
||||
{
|
||||
if (unlikely(cur->p - len < cur->start)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cur->p -= len;
|
||||
memcpy(data, cur->p, len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int cursor_push(struct cursor *cursor, u8 *data, int len)
|
||||
{
|
||||
if (unlikely(cursor->p + len >= cursor->end)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cursor->p != data)
|
||||
memcpy(cursor->p, data, len);
|
||||
|
||||
cursor->p += len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int cursor_push_int(struct cursor *cursor, int i)
|
||||
{
|
||||
return cursor_push(cursor, (u8*)&i, sizeof(i));
|
||||
}
|
||||
|
||||
static inline size_t cursor_count(struct cursor *cursor, size_t elem_size)
|
||||
{
|
||||
return (cursor->p - cursor->start)/elem_size;
|
||||
}
|
||||
|
||||
/* TODO: push_varint */
|
||||
static inline int push_varint(struct cursor *cursor, int n)
|
||||
{
|
||||
int ok, len;
|
||||
unsigned char b;
|
||||
len = 0;
|
||||
|
||||
while (1) {
|
||||
b = (n & 0xFF) | 0x80;
|
||||
n >>= 7;
|
||||
if (n == 0) {
|
||||
b &= 0x7F;
|
||||
ok = cursor_push_byte(cursor, b);
|
||||
len++;
|
||||
if (!ok) return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
ok = cursor_push_byte(cursor, b);
|
||||
len++;
|
||||
if (!ok) return 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* TODO: pull_varint */
|
||||
static inline int pull_varint(struct cursor *cursor, int *n)
|
||||
{
|
||||
int ok, i;
|
||||
unsigned char b;
|
||||
*n = 0;
|
||||
|
||||
for (i = 0;; i++) {
|
||||
ok = pull_byte(cursor, &b);
|
||||
if (!ok) return 0;
|
||||
|
||||
*n |= ((int)b & 0x7F) << (i * 7);
|
||||
|
||||
/* is_last */
|
||||
if ((b & 0x80) == 0) {
|
||||
return i+1;
|
||||
}
|
||||
|
||||
if (i == 4) return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int cursor_pull_int(struct cursor *cursor, int *i)
|
||||
{
|
||||
return cursor_pull(cursor, (u8*)i, sizeof(*i));
|
||||
}
|
||||
|
||||
static inline int cursor_push_u32(struct cursor *cursor, uint32_t i) {
|
||||
return cursor_push(cursor, (unsigned char*)&i, sizeof(i));
|
||||
}
|
||||
|
||||
static inline int cursor_push_u16(struct cursor *cursor, u16 i)
|
||||
{
|
||||
return cursor_push(cursor, (u8*)&i, sizeof(i));
|
||||
}
|
||||
|
||||
static inline void *index_cursor(struct cursor *cursor, unsigned int index, int elem_size)
|
||||
{
|
||||
u8 *p;
|
||||
p = &cursor->start[elem_size * index];
|
||||
|
||||
if (unlikely(p >= cursor->end))
|
||||
return NULL;
|
||||
|
||||
return (void*)p;
|
||||
}
|
||||
|
||||
|
||||
static inline int push_sized_str(struct cursor *cursor, const char *str, int len)
|
||||
{
|
||||
return cursor_push(cursor, (u8*)str, len);
|
||||
}
|
||||
|
||||
static inline int cursor_push_lowercase(struct cursor *cur, const char *str, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (unlikely(cur->p + len >= cur->end))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
cur->p[i] = tolower(str[i]);
|
||||
|
||||
cur->p += len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int cursor_push_str(struct cursor *cursor, const char *str)
|
||||
{
|
||||
return cursor_push(cursor, (u8*)str, (int)strlen(str));
|
||||
}
|
||||
|
||||
static inline int cursor_push_c_str(struct cursor *cursor, const char *str)
|
||||
{
|
||||
return cursor_push_str(cursor, str) && cursor_push_byte(cursor, 0);
|
||||
}
|
||||
|
||||
/* TODO: push varint size */
|
||||
static inline int push_prefixed_str(struct cursor *cursor, const char *str)
|
||||
{
|
||||
int ok, len;
|
||||
len = (int)strlen(str);
|
||||
ok = push_varint(cursor, len);
|
||||
if (!ok) return 0;
|
||||
return push_sized_str(cursor, str, len);
|
||||
}
|
||||
|
||||
static inline int pull_prefixed_str(struct cursor *cursor, struct cursor *dest_buf, const char **str)
|
||||
{
|
||||
int len, ok;
|
||||
|
||||
ok = pull_varint(cursor, &len);
|
||||
if (!ok) return 0;
|
||||
|
||||
if (unlikely(dest_buf->p + len > dest_buf->end)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ok = pull_data_into_cursor(cursor, dest_buf, (unsigned char**)str, len);
|
||||
if (!ok) return 0;
|
||||
|
||||
ok = cursor_push_byte(dest_buf, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int cursor_remaining_capacity(struct cursor *cursor)
|
||||
{
|
||||
return (int)(cursor->end - cursor->p);
|
||||
}
|
||||
|
||||
|
||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
||||
static inline void cursor_print_around(struct cursor *cur, int range)
|
||||
{
|
||||
unsigned char *c;
|
||||
|
||||
printf("[%ld/%ld]\n", cur->p - cur->start, cur->end - cur->start);
|
||||
|
||||
c = max(cur->p - range, cur->start);
|
||||
for (; c < cur->end && c < (cur->p + range); c++) {
|
||||
printf("%02x", *c);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
c = max(cur->p - range, cur->start);
|
||||
for (; c < cur->end && c < (cur->p + range); c++) {
|
||||
if (c == cur->p) {
|
||||
printf("^");
|
||||
continue;
|
||||
}
|
||||
printf(" ");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#undef max
|
||||
|
||||
static inline int pull_bytes(struct cursor *cur, int count, const u8 **bytes) {
|
||||
if (cur->p + count > cur->end)
|
||||
return 0;
|
||||
|
||||
*bytes = cur->p;
|
||||
cur->p += count;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int parse_str(struct cursor *cur, const char *str) {
|
||||
int i;
|
||||
char c, cs;
|
||||
unsigned long len;
|
||||
|
||||
len = strlen(str);
|
||||
|
||||
if (cur->p + len >= cur->end)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
c = tolower(cur->p[i]);
|
||||
cs = tolower(str[i]);
|
||||
|
||||
if (c != cs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
cur->p += len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int is_whitespace(int c) {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
|
||||
}
|
||||
|
||||
|
||||
static inline int next_char_is_whitespace(unsigned char *curChar, unsigned char *endChar) {
|
||||
unsigned char * next = curChar + 1;
|
||||
if(next > endChar) return 0;
|
||||
else if(next == endChar) return 1;
|
||||
return is_whitespace(*next);
|
||||
}
|
||||
|
||||
static int char_disallowed_at_end_url(char c){
|
||||
return c == '.' || c == ',';
|
||||
}
|
||||
|
||||
static inline int is_final_url_char(unsigned char *curChar, unsigned char *endChar){
|
||||
if(is_whitespace(*curChar)){
|
||||
return 1;
|
||||
}
|
||||
else if(next_char_is_whitespace(curChar, endChar)) {
|
||||
// next char is whitespace so this char could be the final char in the url
|
||||
return char_disallowed_at_end_url(*curChar);
|
||||
}
|
||||
else{
|
||||
// next char isn't whitespace so it can't be a final char
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int is_underscore(int c) {
|
||||
return c == '_';
|
||||
}
|
||||
|
||||
static inline int is_utf8_byte(u8 c) {
|
||||
return c & 0x80;
|
||||
}
|
||||
|
||||
static inline int parse_utf8_char(struct cursor *cursor, unsigned int *code_point, unsigned int *utf8_length)
|
||||
{
|
||||
u8 first_byte;
|
||||
if (!parse_byte(cursor, &first_byte))
|
||||
return 0; // Not enough data
|
||||
|
||||
// Determine the number of bytes in this UTF-8 character
|
||||
int remaining_bytes = 0;
|
||||
if (first_byte < 0x80) {
|
||||
*code_point = first_byte;
|
||||
return 1;
|
||||
} else if ((first_byte & 0xE0) == 0xC0) {
|
||||
remaining_bytes = 1;
|
||||
*utf8_length = remaining_bytes + 1;
|
||||
*code_point = first_byte & 0x1F;
|
||||
} else if ((first_byte & 0xF0) == 0xE0) {
|
||||
remaining_bytes = 2;
|
||||
*utf8_length = remaining_bytes + 1;
|
||||
*code_point = first_byte & 0x0F;
|
||||
} else if ((first_byte & 0xF8) == 0xF0) {
|
||||
remaining_bytes = 3;
|
||||
*utf8_length = remaining_bytes + 1;
|
||||
*code_point = first_byte & 0x07;
|
||||
} else {
|
||||
remaining_bytes = 0;
|
||||
*utf8_length = 1; // Assume 1 byte length for unrecognized UTF-8 characters
|
||||
// TODO: We need to gracefully handle unrecognized UTF-8 characters
|
||||
printf("Invalid UTF-8 byte: %x\n", *code_point);
|
||||
*code_point = ((first_byte & 0xF0) << 6); // Prevent testing as punctuation
|
||||
return 0; // Invalid first byte
|
||||
}
|
||||
|
||||
// Peek at remaining bytes
|
||||
for (int i = 0; i < remaining_bytes; ++i) {
|
||||
signed char next_byte;
|
||||
if ((next_byte = peek_char(cursor, i+1)) == -1) {
|
||||
*utf8_length = 1;
|
||||
return 0; // Not enough data
|
||||
}
|
||||
|
||||
// Debugging lines
|
||||
//printf("Cursor: %s\n", cursor->p);
|
||||
//printf("Codepoint: %x\n", *code_point);
|
||||
//printf("Codepoint <<6: %x\n", ((*code_point << 6) | (next_byte & 0x3F)));
|
||||
//printf("Remaining bytes: %x\n", remaining_bytes);
|
||||
//printf("First byte: %x\n", first_byte);
|
||||
//printf("Next byte: %x\n", next_byte);
|
||||
//printf("Bitwise AND result: %x\n", (next_byte & 0xC0));
|
||||
|
||||
if ((next_byte & 0xC0) != 0x80) {
|
||||
*utf8_length = 1;
|
||||
return 0; // Invalid byte in sequence
|
||||
}
|
||||
|
||||
*code_point = (*code_point << 6) | (next_byte & 0x3F);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given Unicode code point is a punctuation character
|
||||
*
|
||||
* @param codepoint The Unicode code point to check. @return true if the
|
||||
* code point is a punctuation character, false otherwise.
|
||||
*/
|
||||
static inline int is_punctuation(unsigned int codepoint) {
|
||||
|
||||
// Check for underscore (underscore is not treated as punctuation)
|
||||
if (is_underscore(codepoint))
|
||||
return 0;
|
||||
|
||||
// Check for ASCII punctuation
|
||||
if (ispunct(codepoint))
|
||||
return 1;
|
||||
|
||||
// Check for Unicode punctuation exceptions (punctuation allowed in hashtags)
|
||||
if (codepoint == 0x301C || codepoint == 0xFF5E) // Japanese Wave Dash / Tilde
|
||||
return 0;
|
||||
|
||||
// Check for Unicode punctuation
|
||||
// NOTE: We may need to adjust the codepoint ranges in the future,
|
||||
// to include/exclude certain types of Unicode characters in hashtags.
|
||||
// Unicode Blocks Reference: https://www.compart.com/en/unicode/block
|
||||
return (
|
||||
// Latin-1 Supplement No-Break Space (NBSP): U+00A0
|
||||
(codepoint == 0x00A0) ||
|
||||
|
||||
// Latin-1 Supplement Punctuation: U+00A1 to U+00BF
|
||||
(codepoint >= 0x00A1 && codepoint <= 0x00BF) ||
|
||||
|
||||
// General Punctuation: U+2000 to U+206F
|
||||
(codepoint >= 0x2000 && codepoint <= 0x206F) ||
|
||||
|
||||
// Currency Symbols: U+20A0 to U+20CF
|
||||
(codepoint >= 0x20A0 && codepoint <= 0x20CF) ||
|
||||
|
||||
// Supplemental Punctuation: U+2E00 to U+2E7F
|
||||
(codepoint >= 0x2E00 && codepoint <= 0x2E7F) ||
|
||||
|
||||
// CJK Symbols and Punctuation: U+3000 to U+303F
|
||||
(codepoint >= 0x3000 && codepoint <= 0x303F) ||
|
||||
|
||||
// Ideographic Description Characters: U+2FF0 to U+2FFF
|
||||
(codepoint >= 0x2FF0 && codepoint <= 0x2FFF)
|
||||
);
|
||||
}
|
||||
|
||||
static inline int is_right_boundary(int c) {
|
||||
return is_whitespace(c) || is_punctuation(c);
|
||||
}
|
||||
|
||||
static inline int is_left_boundary(char c) {
|
||||
return is_right_boundary(c) || is_utf8_byte(c);
|
||||
}
|
||||
|
||||
static inline int is_alphanumeric(char c) {
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
static inline int consume_until_boundary(struct cursor *cur) {
|
||||
unsigned int c;
|
||||
unsigned int char_length = 1;
|
||||
unsigned int *utf8_char_length = &char_length;
|
||||
|
||||
while (cur->p < cur->end) {
|
||||
c = *cur->p;
|
||||
|
||||
*utf8_char_length = 1;
|
||||
|
||||
if (is_whitespace(c))
|
||||
return 1;
|
||||
|
||||
// Need to check for UTF-8 characters, which can be multiple bytes long
|
||||
if (is_utf8_byte(c)) {
|
||||
if (!parse_utf8_char(cur, &c, utf8_char_length)) {
|
||||
if (!is_right_boundary(c)){
|
||||
// TODO: We should work towards handling all UTF-8 characters.
|
||||
printf("Invalid UTF-8 code point: %x\n", c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_right_boundary(c))
|
||||
return 1;
|
||||
|
||||
// Need to use a variable character byte length for UTF-8 (2-4 bytes)
|
||||
if (cur->p + *utf8_char_length <= cur->end)
|
||||
cur->p += *utf8_char_length;
|
||||
else
|
||||
cur->p++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int consume_until_whitespace(struct cursor *cur, int or_end) {
|
||||
char c;
|
||||
int consumedAtLeastOne = 0;
|
||||
|
||||
while (cur->p < cur->end) {
|
||||
c = *cur->p;
|
||||
|
||||
if (is_whitespace(c))
|
||||
return consumedAtLeastOne;
|
||||
|
||||
cur->p++;
|
||||
consumedAtLeastOne = 1;
|
||||
}
|
||||
|
||||
return or_end;
|
||||
}
|
||||
|
||||
static inline int consume_until_end_url(struct cursor *cur, int or_end) {
|
||||
char c;
|
||||
int consumedAtLeastOne = 0;
|
||||
|
||||
while (cur->p < cur->end) {
|
||||
c = *cur->p;
|
||||
|
||||
if (is_final_url_char(cur->p, cur->end))
|
||||
return consumedAtLeastOne;
|
||||
|
||||
cur->p++;
|
||||
consumedAtLeastOne = 1;
|
||||
}
|
||||
|
||||
return or_end;
|
||||
}
|
||||
|
||||
static inline int consume_until_non_alphanumeric(struct cursor *cur, int or_end) {
|
||||
char c;
|
||||
int consumedAtLeastOne = 0;
|
||||
|
||||
while (cur->p < cur->end) {
|
||||
c = *cur->p;
|
||||
|
||||
if (!is_alphanumeric(c))
|
||||
return consumedAtLeastOne;
|
||||
|
||||
cur->p++;
|
||||
consumedAtLeastOne = 1;
|
||||
}
|
||||
|
||||
return or_end;
|
||||
}
|
||||
|
||||
|
||||
static inline int cursor_memset(struct cursor *cursor, unsigned char c, int n)
|
||||
{
|
||||
if (cursor->p + n >= cursor->end)
|
||||
return 0;
|
||||
|
||||
memset(cursor->p, c, n);
|
||||
cursor->p += n;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -5,9 +5,3 @@
|
||||
#include "damus.h"
|
||||
#include "bolt11.h"
|
||||
#include "amount.h"
|
||||
#include "nostr_bech32.h"
|
||||
#include "wasm.h"
|
||||
#include "nostrscript.h"
|
||||
#include "nostrdb.h"
|
||||
#include "lmdb.h"
|
||||
|
||||
|
||||
293
damus-c/damus.c
293
damus-c/damus.c
@@ -6,12 +6,63 @@
|
||||
//
|
||||
|
||||
#include "damus.h"
|
||||
#include "cursor.h"
|
||||
#include "bolt11.h"
|
||||
#include "bech32.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef unsigned char u8;
|
||||
|
||||
struct cursor {
|
||||
const u8 *p;
|
||||
const u8 *start;
|
||||
const u8 *end;
|
||||
};
|
||||
|
||||
static inline int is_whitespace(char c) {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
|
||||
}
|
||||
|
||||
static void make_cursor(struct cursor *c, const u8 *content, size_t len)
|
||||
{
|
||||
c->start = content;
|
||||
c->end = content + len;
|
||||
c->p = content;
|
||||
}
|
||||
|
||||
static int consume_until_whitespace(struct cursor *cur, int or_end) {
|
||||
char c;
|
||||
|
||||
while (cur->p < cur->end) {
|
||||
c = *cur->p;
|
||||
|
||||
if (is_whitespace(c))
|
||||
return 1;
|
||||
|
||||
cur->p++;
|
||||
}
|
||||
|
||||
return or_end;
|
||||
}
|
||||
|
||||
static int parse_char(struct cursor *cur, char c) {
|
||||
if (cur->p >= cur->end)
|
||||
return 0;
|
||||
|
||||
if (*cur->p == c) {
|
||||
cur->p++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int peek_char(struct cursor *cur, int ind) {
|
||||
if ((cur->p + ind < cur->start) || (cur->p + ind >= cur->end))
|
||||
return -1;
|
||||
|
||||
return *(cur->p + ind);
|
||||
}
|
||||
|
||||
static int parse_digit(struct cursor *cur, int *digit) {
|
||||
int c;
|
||||
if ((c = peek_char(cur, 0)) == -1)
|
||||
@@ -27,10 +78,32 @@ static int parse_digit(struct cursor *cur, int *digit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_str(struct cursor *cur, const char *str) {
|
||||
int i;
|
||||
char c, cs;
|
||||
unsigned long len;
|
||||
|
||||
len = strlen(str);
|
||||
|
||||
if (cur->p + len >= cur->end)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
c = tolower(cur->p[i]);
|
||||
cs = tolower(str[i]);
|
||||
|
||||
if (c != cs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
cur->p += len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_mention_index(struct cursor *cur, struct note_block *block) {
|
||||
static int parse_mention(struct cursor *cur, struct block *block) {
|
||||
int d1, d2, d3, ind;
|
||||
u8 *start = cur->p;
|
||||
const u8 *start = cur->p;
|
||||
|
||||
if (!parse_str(cur, "#["))
|
||||
return 0;
|
||||
@@ -53,15 +126,15 @@ static int parse_mention_index(struct cursor *cur, struct note_block *block) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
block->type = BLOCK_MENTION_INDEX;
|
||||
block->block.mention_index = ind;
|
||||
block->type = BLOCK_MENTION;
|
||||
block->block.mention = ind;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_hashtag(struct cursor *cur, struct note_block *block) {
|
||||
static int parse_hashtag(struct cursor *cur, struct block *block) {
|
||||
int c;
|
||||
u8 *start = cur->p;
|
||||
const u8 *start = cur->p;
|
||||
|
||||
if (!parse_char(cur, '#'))
|
||||
return 0;
|
||||
@@ -72,7 +145,7 @@ static int parse_hashtag(struct cursor *cur, struct note_block *block) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
consume_until_boundary(cur);
|
||||
consume_until_whitespace(cur, 1);
|
||||
|
||||
block->type = BLOCK_HASHTAG;
|
||||
block->block.str.start = (const char*)(start + 1);
|
||||
@@ -81,7 +154,7 @@ static int parse_hashtag(struct cursor *cur, struct note_block *block) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_block(struct note_blocks *blocks, struct note_block block)
|
||||
static int add_block(struct blocks *blocks, struct block block)
|
||||
{
|
||||
if (blocks->num_blocks + 1 >= MAX_BLOCKS)
|
||||
return 0;
|
||||
@@ -90,9 +163,9 @@ static int add_block(struct note_blocks *blocks, struct note_block block)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_text_block(struct note_blocks *blocks, const u8 *start, const u8 *end)
|
||||
static int add_text_block(struct blocks *blocks, const u8 *start, const u8 *end)
|
||||
{
|
||||
struct note_block b;
|
||||
struct block b;
|
||||
|
||||
if (start == end)
|
||||
return 1;
|
||||
@@ -104,74 +177,8 @@ static int add_text_block(struct note_blocks *blocks, const u8 *start, const u8
|
||||
return add_block(blocks, b);
|
||||
}
|
||||
|
||||
static int consume_url_fragment(struct cursor *cur)
|
||||
{
|
||||
int c;
|
||||
|
||||
if ((c = peek_char(cur, 0)) < 0)
|
||||
return 1;
|
||||
|
||||
if (c != '#' && c != '?') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
cur->p++;
|
||||
|
||||
return consume_until_end_url(cur, 1);
|
||||
}
|
||||
|
||||
static int consume_url_path(struct cursor *cur)
|
||||
{
|
||||
int c;
|
||||
|
||||
if ((c = peek_char(cur, 0)) < 0)
|
||||
return 1;
|
||||
|
||||
if (c != '/') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (cur->p < cur->end) {
|
||||
c = *cur->p;
|
||||
|
||||
if (c == '?' || c == '#' || is_final_url_char(cur->p, cur->end)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
cur->p++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int consume_url_host(struct cursor *cur)
|
||||
{
|
||||
char c;
|
||||
int count = 0;
|
||||
|
||||
while (cur->p < cur->end) {
|
||||
c = *cur->p;
|
||||
// TODO: handle IDNs
|
||||
if ((is_alphanumeric(c) || c == '.' || c == '-') && !is_final_url_char(cur->p, cur->end))
|
||||
{
|
||||
count++;
|
||||
cur->p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return count != 0;
|
||||
}
|
||||
|
||||
|
||||
// this means the end of the URL hostname is the end of the buffer and we finished
|
||||
return count != 0;
|
||||
}
|
||||
|
||||
static int parse_url(struct cursor *cur, struct note_block *block) {
|
||||
u8 *start = cur->p;
|
||||
u8 *host;
|
||||
int host_len;
|
||||
struct cursor path_cur;
|
||||
static int parse_url(struct cursor *cur, struct block *block) {
|
||||
const u8 *start = cur->p;
|
||||
|
||||
if (!parse_str(cur, "http"))
|
||||
return 0;
|
||||
@@ -187,58 +194,12 @@ static int parse_url(struct cursor *cur, struct note_block *block) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure to save the hostname. We will use this to detect damus.io links
|
||||
host = cur->p;
|
||||
|
||||
if (!consume_url_host(cur)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
|
||||
if (!consume_until_whitespace(cur, 1)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get the length of the host string
|
||||
host_len = (int)(cur->p - host);
|
||||
|
||||
// save the current parse state so that we can continue from here when
|
||||
// parsing the bech32 in the damus.io link if we have it
|
||||
copy_cursor(cur, &path_cur);
|
||||
|
||||
// skip leading /
|
||||
cursor_skip(&path_cur, 1);
|
||||
|
||||
if (!consume_url_path(cur)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!consume_url_fragment(cur)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// smart parens
|
||||
if (start - 1 >= 0 &&
|
||||
start < cur->end &&
|
||||
*(start - 1) == '(' &&
|
||||
(cur->p - 1) < cur->end &&
|
||||
*(cur->p - 1) == ')')
|
||||
{
|
||||
cur->p--;
|
||||
}
|
||||
|
||||
// save the bech32 string pos in case we hit a damus.io link
|
||||
block->block.str.start = (const char *)path_cur.p;
|
||||
|
||||
// if we have a damus link, make it a mention
|
||||
if (host_len == 8
|
||||
&& !strncmp((const char *)host, "damus.io", 8)
|
||||
&& parse_nostr_bech32(&path_cur, &block->block.mention_bech32.bech32))
|
||||
{
|
||||
block->block.str.end = (const char *)path_cur.p;
|
||||
block->type = BLOCK_MENTION_BECH32;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
block->type = BLOCK_URL;
|
||||
block->block.str.start = (const char *)start;
|
||||
block->block.str.end = (const char *)cur->p;
|
||||
@@ -246,8 +207,8 @@ static int parse_url(struct cursor *cur, struct note_block *block) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_invoice(struct cursor *cur, struct note_block *block) {
|
||||
u8 *start, *end;
|
||||
static int parse_invoice(struct cursor *cur, struct block *block) {
|
||||
const u8 *start, *end;
|
||||
char *fail;
|
||||
struct bolt11 *bolt11;
|
||||
// optional
|
||||
@@ -285,28 +246,7 @@ static int parse_invoice(struct cursor *cur, struct note_block *block) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int parse_mention_bech32(struct cursor *cur, struct note_block *block) {
|
||||
u8 *start = cur->p;
|
||||
|
||||
parse_char(cur, '@');
|
||||
parse_str(cur, "nostr:");
|
||||
|
||||
block->block.str.start = (const char *)cur->p;
|
||||
|
||||
if (!parse_nostr_bech32(cur, &block->block.mention_bech32.bech32)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
block->block.str.end = (const char *)cur->p;
|
||||
|
||||
block->type = BLOCK_MENTION_BECH32;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_text_then_block(struct cursor *cur, struct note_blocks *blocks, struct note_block block, u8 **start, const u8 *pre_mention)
|
||||
static int add_text_then_block(struct cursor *cur, struct blocks *blocks, struct block block, const u8 **start, const u8 *pre_mention)
|
||||
{
|
||||
if (!add_text_block(blocks, *start, pre_mention))
|
||||
return 0;
|
||||
@@ -319,29 +259,23 @@ static int add_text_then_block(struct cursor *cur, struct note_blocks *blocks, s
|
||||
return 1;
|
||||
}
|
||||
|
||||
int damus_parse_content(struct note_blocks *blocks, const char *content) {
|
||||
int damus_parse_content(struct blocks *blocks, const char *content) {
|
||||
int cp, c;
|
||||
struct cursor cur;
|
||||
struct note_block block;
|
||||
u8 *start, *pre_mention;
|
||||
struct block block;
|
||||
const u8 *start, *pre_mention;
|
||||
|
||||
blocks->words = 0;
|
||||
blocks->num_blocks = 0;
|
||||
make_cursor((u8*)content, (u8*)content + strlen(content), &cur);
|
||||
make_cursor(&cur, (const u8*)content, strlen(content));
|
||||
|
||||
start = cur.p;
|
||||
while (cur.p < cur.end && blocks->num_blocks < MAX_BLOCKS) {
|
||||
cp = peek_char(&cur, -1);
|
||||
c = peek_char(&cur, 0);
|
||||
|
||||
// new word
|
||||
if (is_whitespace(cp) && !is_whitespace(c)) {
|
||||
blocks->words++;
|
||||
}
|
||||
|
||||
pre_mention = cur.p;
|
||||
if (cp == -1 || is_left_boundary(cp) || c == '#') {
|
||||
if (c == '#' && (parse_mention_index(&cur, &block) || parse_hashtag(&cur, &block))) {
|
||||
if (cp == -1 || is_whitespace(cp)) {
|
||||
if (c == '#' && (parse_mention(&cur, &block) || parse_hashtag(&cur, &block))) {
|
||||
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||
return 0;
|
||||
continue;
|
||||
@@ -353,10 +287,6 @@ int damus_parse_content(struct note_blocks *blocks, const char *content) {
|
||||
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||
return 0;
|
||||
continue;
|
||||
} else if ((c == 'n' || c == '@') && parse_mention_bech32(&cur, &block)) {
|
||||
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,23 +301,14 @@ int damus_parse_content(struct note_blocks *blocks, const char *content) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void blocks_init(struct note_blocks *blocks) {
|
||||
blocks->blocks = malloc(sizeof(struct note_block) * MAX_BLOCKS);
|
||||
void blocks_init(struct blocks *blocks) {
|
||||
blocks->blocks = malloc(sizeof(struct block) * MAX_BLOCKS);
|
||||
blocks->num_blocks = 0;
|
||||
}
|
||||
|
||||
void blocks_free(struct note_blocks *blocks) {
|
||||
if (!blocks->blocks) {
|
||||
return;
|
||||
void blocks_free(struct blocks *blocks) {
|
||||
if (blocks->blocks) {
|
||||
free(blocks->blocks);
|
||||
blocks->num_blocks = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < blocks->num_blocks; ++i) {
|
||||
if (blocks->blocks[i].type == BLOCK_MENTION_BECH32) {
|
||||
free(blocks->blocks[i].block.mention_bech32.bech32.buffer);
|
||||
blocks->blocks[i].block.mention_bech32.bech32.buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
free(blocks->blocks);
|
||||
blocks->num_blocks = 0;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,45 @@
|
||||
#define damus_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include "block.h"
|
||||
|
||||
typedef unsigned char u8;
|
||||
#define MAX_BLOCKS 1024
|
||||
|
||||
int damus_parse_content(struct note_blocks *blocks, const char *content);
|
||||
enum block_type {
|
||||
BLOCK_HASHTAG = 1,
|
||||
BLOCK_TEXT = 2,
|
||||
BLOCK_MENTION = 3,
|
||||
BLOCK_URL = 4,
|
||||
BLOCK_INVOICE = 5,
|
||||
};
|
||||
|
||||
typedef struct str_block {
|
||||
const char *start;
|
||||
const char *end;
|
||||
} str_block_t;
|
||||
|
||||
typedef struct invoice_block {
|
||||
struct str_block invstr;
|
||||
union {
|
||||
struct bolt11 *bolt11;
|
||||
};
|
||||
} invoice_block_t;
|
||||
|
||||
typedef struct block {
|
||||
enum block_type type;
|
||||
union {
|
||||
struct str_block str;
|
||||
struct invoice_block invoice;
|
||||
int mention;
|
||||
} block;
|
||||
} block_t;
|
||||
|
||||
typedef struct blocks {
|
||||
int num_blocks;
|
||||
struct block *blocks;
|
||||
} blocks_t;
|
||||
|
||||
void blocks_init(struct blocks *blocks);
|
||||
void blocks_free(struct blocks *blocks);
|
||||
int damus_parse_content(struct blocks *blocks, const char *content);
|
||||
|
||||
#endif /* damus_h */
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
#ifndef PROTOVERSE_DEBUG_H
|
||||
#define PROTOVERSE_DEBUG_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define unusual(...) fprintf(stderr, "UNUSUAL: " __VA_ARGS__)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define debug(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define debug(...)
|
||||
#endif
|
||||
|
||||
#endif /* PROTOVERSE_DEBUG_H */
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
int note_error_(struct errors *errs_, struct cursor *p, const char *fmt, ...)
|
||||
{
|
||||
static char buf[512];
|
||||
struct error err;
|
||||
struct cursor *errs;
|
||||
va_list ap;
|
||||
|
||||
errs = &errs_->cur;
|
||||
|
||||
if (errs_->enabled == 0)
|
||||
return 0;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsprintf(buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
err.msg = buf;
|
||||
err.pos = p ? (int)(p->p - p->start) : 0;
|
||||
|
||||
if (!cursor_push_error(errs, &err)) {
|
||||
fprintf(stderr, "arena OOM when recording error, ");
|
||||
fprintf(stderr, "errs->p at %ld, remaining %ld, strlen %ld\n",
|
||||
errs->p - errs->start, errs->end - errs->p, strlen(buf));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
|
||||
#ifndef PROTOVERSE_ERROR_H
|
||||
#define PROTOVERSE_ERROR_H
|
||||
|
||||
#include "cursor.h"
|
||||
|
||||
struct error {
|
||||
int pos;
|
||||
const char *msg;
|
||||
};
|
||||
|
||||
struct errors {
|
||||
struct cursor cur;
|
||||
int enabled;
|
||||
};
|
||||
|
||||
#define note_error(errs, p, fmt, ...) note_error_(errs, p, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
|
||||
|
||||
static inline int cursor_push_error(struct cursor *cur, struct error *err)
|
||||
{
|
||||
return cursor_push_int(cur, err->pos) &&
|
||||
cursor_push_c_str(cur, err->msg);
|
||||
}
|
||||
|
||||
static inline int cursor_pull_error(struct cursor *cur, struct error *err)
|
||||
{
|
||||
return cursor_pull_int(cur, &err->pos) &&
|
||||
cursor_pull_c_str(cur, &err->msg);
|
||||
}
|
||||
|
||||
int note_error_(struct errors *errs, struct cursor *p, const char *fmt, ...);
|
||||
|
||||
#endif /* PROTOVERSE_ERROR_H */
|
||||
@@ -39,6 +39,15 @@ bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize)
|
||||
return slen == 0 && bufsize == 0;
|
||||
}
|
||||
|
||||
static char hexchar(unsigned int val)
|
||||
{
|
||||
if (val < 10)
|
||||
return '0' + val;
|
||||
if (val < 16)
|
||||
return 'a' + val - 10;
|
||||
abort();
|
||||
}
|
||||
|
||||
bool hex_encode(const void *buf, size_t bufsize, char *dest, size_t destsize)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
@@ -26,7 +26,7 @@ bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize);
|
||||
/**
|
||||
* hex_encode - Create a nul-terminated hex string
|
||||
* @buf: the buffer to read the data from
|
||||
* @bufsize: the length of buf
|
||||
* @bufsize: the length of @buf
|
||||
* @dest: the string to fill
|
||||
* @destsize: the max size of the string
|
||||
*
|
||||
@@ -70,15 +70,4 @@ static inline size_t hex_data_size(size_t strlen)
|
||||
{
|
||||
return strlen / 2;
|
||||
}
|
||||
|
||||
static inline char hexchar(unsigned int val)
|
||||
{
|
||||
if (val < 10)
|
||||
return '0' + val;
|
||||
if (val < 16)
|
||||
return 'a' + val - 10;
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
#endif /* CCAN_HEX_H */
|
||||
|
||||
@@ -52,13 +52,9 @@
|
||||
*/
|
||||
#define unlikely(cond) __builtin_expect(!!(cond), 0)
|
||||
#else
|
||||
#ifndef likely
|
||||
#define likely(cond) (!!(cond))
|
||||
#endif
|
||||
#ifndef unlikely
|
||||
#define unlikely(cond) (!!(cond))
|
||||
#endif
|
||||
#endif
|
||||
#else /* CCAN_LIKELY_DEBUG versions */
|
||||
#include <ccan/str/str.h>
|
||||
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
//
|
||||
// nostr_bech32.c
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-04-09.
|
||||
//
|
||||
|
||||
#include "nostr_bech32.h"
|
||||
#include <stdlib.h>
|
||||
#include "endian.h"
|
||||
#include "cursor.h"
|
||||
#include "bech32.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAX_TLVS 16
|
||||
|
||||
#define TLV_SPECIAL 0
|
||||
#define TLV_RELAY 1
|
||||
#define TLV_AUTHOR 2
|
||||
#define TLV_KIND 3
|
||||
#define TLV_KNOWN_TLVS 4
|
||||
|
||||
struct nostr_tlv {
|
||||
u8 type;
|
||||
u8 len;
|
||||
const u8 *value;
|
||||
};
|
||||
|
||||
struct nostr_tlvs {
|
||||
struct nostr_tlv tlvs[MAX_TLVS];
|
||||
int num_tlvs;
|
||||
};
|
||||
|
||||
static int parse_nostr_tlv(struct cursor *cur, struct nostr_tlv *tlv) {
|
||||
// get the tlv tag
|
||||
if (!pull_byte(cur, &tlv->type))
|
||||
return 0;
|
||||
|
||||
// unknown, fail!
|
||||
if (tlv->type >= TLV_KNOWN_TLVS)
|
||||
return 0;
|
||||
|
||||
// get the length
|
||||
if (!pull_byte(cur, &tlv->len))
|
||||
return 0;
|
||||
|
||||
// is the reported length greater then our buffer? if so fail
|
||||
if (cur->p + tlv->len > cur->end)
|
||||
return 0;
|
||||
|
||||
tlv->value = cur->p;
|
||||
cur->p += tlv->len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_nostr_tlvs(struct cursor *cur, struct nostr_tlvs *tlvs) {
|
||||
int i;
|
||||
tlvs->num_tlvs = 0;
|
||||
|
||||
for (i = 0; i < MAX_TLVS; i++) {
|
||||
if (parse_nostr_tlv(cur, &tlvs->tlvs[i])) {
|
||||
tlvs->num_tlvs++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tlvs->num_tlvs == 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int find_tlv(struct nostr_tlvs *tlvs, u8 type, struct nostr_tlv **tlv) {
|
||||
*tlv = NULL;
|
||||
|
||||
for (int i = 0; i < tlvs->num_tlvs; i++) {
|
||||
if (tlvs->tlvs[i].type == type) {
|
||||
*tlv = &tlvs->tlvs[i];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_type(const char *prefix, enum nostr_bech32_type *type) {
|
||||
// Parse type
|
||||
if (strcmp(prefix, "note") == 0) {
|
||||
*type = NOSTR_BECH32_NOTE;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "npub") == 0) {
|
||||
*type = NOSTR_BECH32_NPUB;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "nsec") == 0) {
|
||||
*type = NOSTR_BECH32_NSEC;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "nprofile") == 0) {
|
||||
*type = NOSTR_BECH32_NPROFILE;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "nevent") == 0) {
|
||||
*type = NOSTR_BECH32_NEVENT;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "nrelay") == 0) {
|
||||
*type = NOSTR_BECH32_NRELAY;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "naddr") == 0) {
|
||||
*type = NOSTR_BECH32_NADDR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_note(struct cursor *cur, struct bech32_note *note) {
|
||||
return pull_bytes(cur, 32, ¬e->event_id);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_npub(struct cursor *cur, struct bech32_npub *npub) {
|
||||
return pull_bytes(cur, 32, &npub->pubkey);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_nsec(struct cursor *cur, struct bech32_nsec *nsec) {
|
||||
return pull_bytes(cur, 32, &nsec->nsec);
|
||||
}
|
||||
|
||||
static int tlvs_to_relays(struct nostr_tlvs *tlvs, struct relays *relays) {
|
||||
struct nostr_tlv *tlv;
|
||||
struct str_block *str;
|
||||
|
||||
relays->num_relays = 0;
|
||||
|
||||
for (int i = 0; i < tlvs->num_tlvs; i++) {
|
||||
tlv = &tlvs->tlvs[i];
|
||||
if (tlv->type != TLV_RELAY)
|
||||
continue;
|
||||
|
||||
if (relays->num_relays + 1 > MAX_RELAYS)
|
||||
break;
|
||||
|
||||
str = &relays->relays[relays->num_relays++];
|
||||
str->start = (const char*)tlv->value;
|
||||
str->end = (const char*)(tlv->value + tlv->len);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t decode_tlv_u32(const uint8_t *bytes) {
|
||||
beint32_t *be32_bytes = (beint32_t*)bytes;
|
||||
return be32_to_cpu(*be32_bytes);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_nevent(struct cursor *cur, struct bech32_nevent *nevent) {
|
||||
struct nostr_tlvs tlvs;
|
||||
struct nostr_tlv *tlv;
|
||||
|
||||
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||
return 0;
|
||||
|
||||
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||
return 0;
|
||||
|
||||
if (tlv->len != 32)
|
||||
return 0;
|
||||
|
||||
nevent->event_id = tlv->value;
|
||||
|
||||
if (find_tlv(&tlvs, TLV_AUTHOR, &tlv)) {
|
||||
nevent->pubkey = tlv->value;
|
||||
} else {
|
||||
nevent->pubkey = NULL;
|
||||
}
|
||||
|
||||
if(find_tlv(&tlvs, TLV_KIND, &tlv)) {
|
||||
nevent->kind = decode_tlv_u32(tlv->value);
|
||||
nevent->has_kind = true;
|
||||
} else {
|
||||
nevent->has_kind = false;
|
||||
}
|
||||
|
||||
return tlvs_to_relays(&tlvs, &nevent->relays);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_naddr(struct cursor *cur, struct bech32_naddr *naddr) {
|
||||
struct nostr_tlvs tlvs;
|
||||
struct nostr_tlv *tlv;
|
||||
|
||||
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||
return 0;
|
||||
|
||||
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||
return 0;
|
||||
|
||||
naddr->identifier.start = (const char*)tlv->value;
|
||||
naddr->identifier.end = (const char*)tlv->value + tlv->len;
|
||||
|
||||
if (!find_tlv(&tlvs, TLV_AUTHOR, &tlv))
|
||||
return 0;
|
||||
|
||||
naddr->pubkey = tlv->value;
|
||||
|
||||
if(!find_tlv(&tlvs, TLV_KIND, &tlv)) {
|
||||
return 0;
|
||||
}
|
||||
naddr->kind = decode_tlv_u32(tlv->value);
|
||||
|
||||
return tlvs_to_relays(&tlvs, &naddr->relays);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_nprofile(struct cursor *cur, struct bech32_nprofile *nprofile) {
|
||||
struct nostr_tlvs tlvs;
|
||||
struct nostr_tlv *tlv;
|
||||
|
||||
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||
return 0;
|
||||
|
||||
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||
return 0;
|
||||
|
||||
if (tlv->len != 32)
|
||||
return 0;
|
||||
|
||||
nprofile->pubkey = tlv->value;
|
||||
|
||||
return tlvs_to_relays(&tlvs, &nprofile->relays);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_nrelay(struct cursor *cur, struct bech32_nrelay *nrelay) {
|
||||
struct nostr_tlvs tlvs;
|
||||
struct nostr_tlv *tlv;
|
||||
|
||||
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||
return 0;
|
||||
|
||||
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||
return 0;
|
||||
|
||||
nrelay->relay.start = (const char*)tlv->value;
|
||||
nrelay->relay.end = (const char*)tlv->value + tlv->len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
|
||||
u8 *start, *end;
|
||||
|
||||
start = cur->p;
|
||||
|
||||
if (!consume_until_non_alphanumeric(cur, 1)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
end = cur->p;
|
||||
|
||||
size_t data_len;
|
||||
size_t input_len = end - start;
|
||||
if (input_len < 10 || input_len > 10000) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
obj->buffer = malloc(input_len * 2);
|
||||
if (!obj->buffer)
|
||||
return 0;
|
||||
|
||||
u8 data[input_len];
|
||||
char prefix[input_len];
|
||||
|
||||
if (bech32_decode_len(prefix, data, &data_len, (const char*)start, input_len) == BECH32_ENCODING_NONE) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
obj->buflen = 0;
|
||||
if (!bech32_convert_bits(obj->buffer, &obj->buflen, 8, data, data_len, 5, 0)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!parse_nostr_bech32_type(prefix, &obj->type)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
struct cursor bcur;
|
||||
make_cursor(obj->buffer, obj->buffer + obj->buflen, &bcur);
|
||||
|
||||
switch (obj->type) {
|
||||
case NOSTR_BECH32_NOTE:
|
||||
if (!parse_nostr_bech32_note(&bcur, &obj->data.note))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NPUB:
|
||||
if (!parse_nostr_bech32_npub(&bcur, &obj->data.npub))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NSEC:
|
||||
if (!parse_nostr_bech32_nsec(&bcur, &obj->data.nsec))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NEVENT:
|
||||
if (!parse_nostr_bech32_nevent(&bcur, &obj->data.nevent))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NADDR:
|
||||
if (!parse_nostr_bech32_naddr(&bcur, &obj->data.naddr))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NPROFILE:
|
||||
if (!parse_nostr_bech32_nprofile(&bcur, &obj->data.nprofile))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NRELAY:
|
||||
if (!parse_nostr_bech32_nrelay(&bcur, &obj->data.nrelay))
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
free(obj->buffer);
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
//
|
||||
// nostr_bech32.h
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-04-09.
|
||||
//
|
||||
|
||||
#ifndef nostr_bech32_h
|
||||
#define nostr_bech32_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include "str_block.h"
|
||||
#include "cursor.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef unsigned char u8;
|
||||
#define MAX_RELAYS 10
|
||||
|
||||
struct relays {
|
||||
struct str_block relays[MAX_RELAYS];
|
||||
int num_relays;
|
||||
};
|
||||
|
||||
enum nostr_bech32_type {
|
||||
NOSTR_BECH32_NOTE = 1,
|
||||
NOSTR_BECH32_NPUB = 2,
|
||||
NOSTR_BECH32_NPROFILE = 3,
|
||||
NOSTR_BECH32_NEVENT = 4,
|
||||
NOSTR_BECH32_NRELAY = 5,
|
||||
NOSTR_BECH32_NADDR = 6,
|
||||
NOSTR_BECH32_NSEC = 7,
|
||||
};
|
||||
|
||||
struct bech32_note {
|
||||
const u8 *event_id;
|
||||
};
|
||||
|
||||
struct bech32_npub {
|
||||
const u8 *pubkey;
|
||||
};
|
||||
|
||||
struct bech32_nsec {
|
||||
const u8 *nsec;
|
||||
};
|
||||
|
||||
struct bech32_nevent {
|
||||
struct relays relays;
|
||||
const u8 *event_id;
|
||||
const u8 *pubkey; // optional
|
||||
uint32_t kind;
|
||||
bool has_kind;
|
||||
};
|
||||
|
||||
struct bech32_nprofile {
|
||||
struct relays relays;
|
||||
const u8 *pubkey;
|
||||
};
|
||||
|
||||
struct bech32_naddr {
|
||||
struct relays relays;
|
||||
struct str_block identifier;
|
||||
const u8 *pubkey;
|
||||
uint32_t kind;
|
||||
};
|
||||
|
||||
struct bech32_nrelay {
|
||||
struct str_block relay;
|
||||
};
|
||||
|
||||
typedef struct nostr_bech32 {
|
||||
enum nostr_bech32_type type;
|
||||
u8 *buffer; // holds strings and tlv stuff
|
||||
size_t buflen;
|
||||
|
||||
union {
|
||||
struct bech32_note note;
|
||||
struct bech32_npub npub;
|
||||
struct bech32_nsec nsec;
|
||||
struct bech32_nevent nevent;
|
||||
struct bech32_nprofile nprofile;
|
||||
struct bech32_naddr naddr;
|
||||
struct bech32_nrelay nrelay;
|
||||
} data;
|
||||
} nostr_bech32_t;
|
||||
|
||||
|
||||
int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj);
|
||||
|
||||
#endif /* nostr_bech32_h */
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
#ifndef CURSOR_PARSER
|
||||
#define CURSOR_PARSER
|
||||
|
||||
#include "cursor.h"
|
||||
|
||||
static int consume_bytes(struct cursor *cursor, const unsigned char *match, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cursor->p + len > cursor->end) {
|
||||
fprintf(stderr, "consume_bytes overflow\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (cursor->p[i] != match[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
cursor->p += len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int consume_byte(struct cursor *cursor, unsigned char match)
|
||||
{
|
||||
if (unlikely(cursor->p >= cursor->end))
|
||||
return 0;
|
||||
if (*cursor->p != match)
|
||||
return 0;
|
||||
cursor->p++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int consume_u32(struct cursor *cursor, unsigned int match)
|
||||
{
|
||||
return consume_bytes(cursor, (unsigned char*)&match, sizeof(match));
|
||||
}
|
||||
|
||||
#endif /* CURSOR_PARSER */
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
//
|
||||
// str_block.h
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-04-09.
|
||||
//
|
||||
|
||||
#ifndef str_block_h
|
||||
#define str_block_h
|
||||
|
||||
typedef struct str_block {
|
||||
const char *start;
|
||||
const char *end;
|
||||
} str_block_t;
|
||||
|
||||
#endif /* str_block_h */
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
#ifndef PROTOVERSE_TYPEDEFS_H
|
||||
#define PROTOVERSE_TYPEDEFS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned short u16;
|
||||
typedef uint64_t u64;
|
||||
typedef int64_t s64;
|
||||
|
||||
|
||||
#endif /* PROTOVERSE_TYPEDEFS_H */
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
#ifndef PROTOVERSE_VARINT_H
|
||||
#define PROTOVERSE_VARINT_H
|
||||
|
||||
#define VARINT_MAX_LEN 9
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
size_t varint_put(unsigned char buf[VARINT_MAX_LEN], uint64_t v);
|
||||
size_t varint_size(uint64_t v);
|
||||
size_t varint_get(const unsigned char *p, size_t max, int64_t *val);
|
||||
|
||||
#endif /* PROTOVERSE_VARINT_H */
|
||||
7299
damus-c/wasm.c
7299
damus-c/wasm.c
File diff suppressed because it is too large
Load Diff
850
damus-c/wasm.h
850
damus-c/wasm.h
@@ -1,850 +0,0 @@
|
||||
|
||||
#ifndef PROTOVERSE_WASM_H
|
||||
#define PROTOVERSE_WASM_H
|
||||
|
||||
static const unsigned char WASM_MAGIC[] = {0,'a','s','m'};
|
||||
|
||||
#define WASM_VERSION 0x01
|
||||
#define MAX_U32_LEB128_BYTES 5
|
||||
#define MAX_U64_LEB128_BYTES 10
|
||||
#define MAX_CUSTOM_SECTIONS 32
|
||||
#define MAX_BUILTINS 64
|
||||
#define BUILTIN_SUSPEND 42
|
||||
|
||||
#define FUNC_TYPE_TAG 0x60
|
||||
|
||||
|
||||
#include "cursor.h"
|
||||
#include "error.h"
|
||||
|
||||
#ifdef NOINLINE
|
||||
#define INLINE __attribute__((noinline))
|
||||
#else
|
||||
#define INLINE inline
|
||||
#endif
|
||||
|
||||
|
||||
#define interp_error(p, fmt, ...) note_error(&((p)->errors), interp_codeptr(p), fmt, ##__VA_ARGS__)
|
||||
#define parse_err(p, fmt, ...) note_error(&((p)->errs), &(p)->cur, fmt, ##__VA_ARGS__)
|
||||
|
||||
enum valtype {
|
||||
val_i32 = 0x7F,
|
||||
val_i64 = 0x7E,
|
||||
val_f32 = 0x7D,
|
||||
val_f64 = 0x7C,
|
||||
val_ref_null = 0xD0,
|
||||
val_ref_func = 0x70,
|
||||
val_ref_extern = 0x6F,
|
||||
};
|
||||
|
||||
enum const_instr {
|
||||
ci_const_i32 = 0x41,
|
||||
ci_const_i64 = 0x42,
|
||||
ci_const_f32 = 0x43,
|
||||
ci_const_f64 = 0x44,
|
||||
ci_ref_null = 0xD0,
|
||||
ci_ref_func = 0xD2,
|
||||
ci_global_get = 0x23,
|
||||
ci_end = 0x0B,
|
||||
};
|
||||
|
||||
enum limit_type {
|
||||
limit_min = 0x00,
|
||||
limit_min_max = 0x01,
|
||||
};
|
||||
|
||||
struct limits {
|
||||
u32 min;
|
||||
u32 max;
|
||||
enum limit_type type;
|
||||
};
|
||||
|
||||
enum section_tag {
|
||||
section_custom,
|
||||
section_type,
|
||||
section_import,
|
||||
section_function,
|
||||
section_table,
|
||||
section_memory,
|
||||
section_global,
|
||||
section_export,
|
||||
section_start,
|
||||
section_element,
|
||||
section_code,
|
||||
section_data,
|
||||
section_data_count,
|
||||
section_name,
|
||||
num_sections,
|
||||
};
|
||||
|
||||
enum name_subsection_tag {
|
||||
name_subsection_module,
|
||||
name_subsection_funcs,
|
||||
name_subsection_locals,
|
||||
num_name_subsections,
|
||||
};
|
||||
|
||||
enum reftype {
|
||||
funcref = 0x70,
|
||||
externref = 0x6F,
|
||||
};
|
||||
|
||||
struct resulttype {
|
||||
unsigned char *valtypes; /* enum valtype */
|
||||
u32 num_valtypes;
|
||||
};
|
||||
|
||||
struct functype {
|
||||
struct resulttype params;
|
||||
struct resulttype result;
|
||||
};
|
||||
|
||||
struct table {
|
||||
enum reftype reftype;
|
||||
struct limits limits;
|
||||
};
|
||||
|
||||
struct tablesec {
|
||||
struct table *tables;
|
||||
u32 num_tables;
|
||||
};
|
||||
|
||||
enum elem_mode {
|
||||
elem_mode_passive,
|
||||
elem_mode_active,
|
||||
elem_mode_declarative,
|
||||
};
|
||||
|
||||
struct expr {
|
||||
u8 *code;
|
||||
u32 code_len;
|
||||
};
|
||||
|
||||
struct refval {
|
||||
u32 addr;
|
||||
};
|
||||
|
||||
struct table_inst {
|
||||
struct refval *refs;
|
||||
enum reftype reftype;
|
||||
u32 num_refs;
|
||||
};
|
||||
|
||||
struct numval {
|
||||
union {
|
||||
int i32;
|
||||
u32 u32;
|
||||
int64_t i64;
|
||||
uint64_t u64;
|
||||
float f32;
|
||||
double f64;
|
||||
};
|
||||
};
|
||||
|
||||
struct val {
|
||||
enum valtype type;
|
||||
union {
|
||||
struct numval num;
|
||||
struct refval ref;
|
||||
};
|
||||
};
|
||||
|
||||
struct elem_inst {
|
||||
struct val val;
|
||||
u16 elem;
|
||||
u16 init;
|
||||
};
|
||||
|
||||
struct elem {
|
||||
struct expr offset;
|
||||
u32 tableidx;
|
||||
struct expr *inits;
|
||||
u32 num_inits;
|
||||
enum elem_mode mode;
|
||||
enum reftype reftype;
|
||||
struct val val;
|
||||
};
|
||||
|
||||
struct customsec {
|
||||
const char *name;
|
||||
unsigned char *data;
|
||||
u32 data_len;
|
||||
};
|
||||
|
||||
struct elemsec {
|
||||
struct elem *elements;
|
||||
u32 num_elements;
|
||||
};
|
||||
|
||||
struct memsec {
|
||||
struct limits *mems; /* memtype */
|
||||
u32 num_mems;
|
||||
};
|
||||
|
||||
struct funcsec {
|
||||
u32 *type_indices;
|
||||
u32 num_indices;
|
||||
};
|
||||
|
||||
enum mut {
|
||||
mut_const,
|
||||
mut_var,
|
||||
};
|
||||
|
||||
struct globaltype {
|
||||
enum valtype valtype;
|
||||
enum mut mut;
|
||||
};
|
||||
|
||||
struct globalsec {
|
||||
struct global *globals;
|
||||
u32 num_globals;
|
||||
};
|
||||
|
||||
struct typesec {
|
||||
struct functype *functypes;
|
||||
u32 num_functypes;
|
||||
};
|
||||
|
||||
enum import_type {
|
||||
import_func,
|
||||
import_table,
|
||||
import_mem,
|
||||
import_global,
|
||||
};
|
||||
|
||||
struct importdesc {
|
||||
enum import_type type;
|
||||
union {
|
||||
u32 typeidx;
|
||||
struct limits tabletype;
|
||||
struct limits memtype;
|
||||
struct globaltype globaltype;
|
||||
};
|
||||
};
|
||||
|
||||
struct import {
|
||||
const char *module_name;
|
||||
const char *name;
|
||||
struct importdesc desc;
|
||||
int resolved_builtin;
|
||||
};
|
||||
|
||||
struct importsec {
|
||||
struct import *imports;
|
||||
u32 num_imports;
|
||||
};
|
||||
|
||||
struct global {
|
||||
struct globaltype type;
|
||||
struct expr init;
|
||||
struct val val;
|
||||
};
|
||||
|
||||
struct local_def {
|
||||
u32 num_types;
|
||||
enum valtype type;
|
||||
};
|
||||
|
||||
/* "code" */
|
||||
struct wasm_func {
|
||||
struct expr code;
|
||||
struct local_def *local_defs;
|
||||
u32 num_local_defs;
|
||||
};
|
||||
|
||||
enum func_type {
|
||||
func_type_wasm,
|
||||
func_type_builtin,
|
||||
};
|
||||
|
||||
struct func {
|
||||
union {
|
||||
struct wasm_func *wasm_func;
|
||||
struct builtin *builtin;
|
||||
};
|
||||
u32 num_locals;
|
||||
struct functype *functype;
|
||||
enum func_type type;
|
||||
const char *name;
|
||||
u32 idx;
|
||||
};
|
||||
|
||||
struct codesec {
|
||||
struct wasm_func *funcs;
|
||||
u32 num_funcs;
|
||||
};
|
||||
|
||||
enum exportdesc {
|
||||
export_func,
|
||||
export_table,
|
||||
export_mem,
|
||||
export_global,
|
||||
};
|
||||
|
||||
struct wexport {
|
||||
const char *name;
|
||||
u32 index;
|
||||
enum exportdesc desc;
|
||||
};
|
||||
|
||||
struct exportsec {
|
||||
struct wexport *exports;
|
||||
u32 num_exports;
|
||||
};
|
||||
|
||||
struct nameassoc {
|
||||
u32 index;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct namemap {
|
||||
struct nameassoc *names;
|
||||
u32 num_names;
|
||||
};
|
||||
|
||||
struct namesec {
|
||||
const char *module_name;
|
||||
struct namemap func_names;
|
||||
int parsed;
|
||||
};
|
||||
|
||||
struct wsection {
|
||||
enum section_tag tag;
|
||||
};
|
||||
|
||||
enum bulk_tag {
|
||||
i_memory_copy = 10,
|
||||
i_memory_fill = 11,
|
||||
i_table_init = 12,
|
||||
i_elem_drop = 13,
|
||||
i_table_copy = 14,
|
||||
i_table_grow = 15,
|
||||
i_table_size = 16,
|
||||
i_table_fill = 17,
|
||||
};
|
||||
|
||||
enum instr_tag {
|
||||
/* control instructions */
|
||||
i_unreachable = 0x00,
|
||||
i_nop = 0x01,
|
||||
i_block = 0x02,
|
||||
i_loop = 0x03,
|
||||
i_if = 0x04,
|
||||
i_else = 0x05,
|
||||
i_end = 0x0B,
|
||||
i_br = 0x0C,
|
||||
i_br_if = 0x0D,
|
||||
i_br_table = 0x0E,
|
||||
i_return = 0x0F,
|
||||
i_call = 0x10,
|
||||
i_call_indirect = 0x11,
|
||||
|
||||
/* parametric instructions */
|
||||
i_drop = 0x1A,
|
||||
i_select = 0x1B,
|
||||
i_selects = 0x1C,
|
||||
|
||||
/* variable instructions */
|
||||
i_local_get = 0x20,
|
||||
i_local_set = 0x21,
|
||||
i_local_tee = 0x22,
|
||||
i_global_get = 0x23,
|
||||
i_global_set = 0x24,
|
||||
i_table_get = 0x25,
|
||||
i_table_set = 0x26,
|
||||
|
||||
/* memory instructions */
|
||||
i_i32_load = 0x28,
|
||||
i_i64_load = 0x29,
|
||||
i_f32_load = 0x2A,
|
||||
i_f64_load = 0x2B,
|
||||
i_i32_load8_s = 0x2C,
|
||||
i_i32_load8_u = 0x2D,
|
||||
i_i32_load16_s = 0x2E,
|
||||
i_i32_load16_u = 0x2F,
|
||||
i_i64_load8_s = 0x30,
|
||||
i_i64_load8_u = 0x31,
|
||||
i_i64_load16_s = 0x32,
|
||||
i_i64_load16_u = 0x33,
|
||||
i_i64_load32_s = 0x34,
|
||||
i_i64_load32_u = 0x35,
|
||||
i_i32_store = 0x36,
|
||||
i_i64_store = 0x37,
|
||||
i_f32_store = 0x38,
|
||||
i_f64_store = 0x39,
|
||||
i_i32_store8 = 0x3A,
|
||||
i_i32_store16 = 0x3B,
|
||||
i_i64_store8 = 0x3C,
|
||||
i_i64_store16 = 0x3D,
|
||||
i_i64_store32 = 0x3E,
|
||||
i_memory_size = 0x3F,
|
||||
i_memory_grow = 0x40,
|
||||
|
||||
/* numeric instructions */
|
||||
i_i32_const = 0x41,
|
||||
i_i64_const = 0x42,
|
||||
i_f32_const = 0x43,
|
||||
i_f64_const = 0x44,
|
||||
|
||||
i_i32_eqz = 0x45,
|
||||
i_i32_eq = 0x46,
|
||||
i_i32_ne = 0x47,
|
||||
i_i32_lt_s = 0x48,
|
||||
i_i32_lt_u = 0x49,
|
||||
i_i32_gt_s = 0x4A,
|
||||
i_i32_gt_u = 0x4B,
|
||||
i_i32_le_s = 0x4C,
|
||||
i_i32_le_u = 0x4D,
|
||||
i_i32_ge_s = 0x4E,
|
||||
i_i32_ge_u = 0x4F,
|
||||
|
||||
i_i64_eqz = 0x50,
|
||||
i_i64_eq = 0x51,
|
||||
i_i64_ne = 0x52,
|
||||
i_i64_lt_s = 0x53,
|
||||
i_i64_lt_u = 0x54,
|
||||
i_i64_gt_s = 0x55,
|
||||
i_i64_gt_u = 0x56,
|
||||
i_i64_le_s = 0x57,
|
||||
i_i64_le_u = 0x58,
|
||||
i_i64_ge_s = 0x59,
|
||||
i_i64_ge_u = 0x5A,
|
||||
|
||||
i_f32_eq = 0x5B,
|
||||
i_f32_ne = 0x5C,
|
||||
i_f32_lt = 0x5D,
|
||||
i_f32_gt = 0x5E,
|
||||
i_f32_le = 0x5F,
|
||||
i_f32_ge = 0x60,
|
||||
|
||||
i_f64_eq = 0x61,
|
||||
i_f64_ne = 0x62,
|
||||
i_f64_lt = 0x63,
|
||||
i_f64_gt = 0x64,
|
||||
i_f64_le = 0x65,
|
||||
i_f64_ge = 0x66,
|
||||
|
||||
i_i32_clz = 0x67,
|
||||
i_i32_ctz = 0x68,
|
||||
i_i32_popcnt = 0x69,
|
||||
|
||||
i_i32_add = 0x6A,
|
||||
i_i32_sub = 0x6B,
|
||||
i_i32_mul = 0x6C,
|
||||
i_i32_div_s = 0x6D,
|
||||
i_i32_div_u = 0x6E,
|
||||
i_i32_rem_s = 0x6F,
|
||||
i_i32_rem_u = 0x70,
|
||||
i_i32_and = 0x71,
|
||||
i_i32_or = 0x72,
|
||||
i_i32_xor = 0x73,
|
||||
i_i32_shl = 0x74,
|
||||
i_i32_shr_s = 0x75,
|
||||
i_i32_shr_u = 0x76,
|
||||
i_i32_rotl = 0x77,
|
||||
i_i32_rotr = 0x78,
|
||||
|
||||
i_i64_clz = 0x79,
|
||||
i_i64_ctz = 0x7A,
|
||||
i_i64_popcnt = 0x7B,
|
||||
i_i64_add = 0x7C,
|
||||
i_i64_sub = 0x7D,
|
||||
i_i64_mul = 0x7E,
|
||||
i_i64_div_s = 0x7F,
|
||||
i_i64_div_u = 0x80,
|
||||
i_i64_rem_s = 0x81,
|
||||
i_i64_rem_u = 0x82,
|
||||
i_i64_and = 0x83,
|
||||
i_i64_or = 0x84,
|
||||
i_i64_xor = 0x85,
|
||||
i_i64_shl = 0x86,
|
||||
i_i64_shr_s = 0x87,
|
||||
i_i64_shr_u = 0x88,
|
||||
i_i64_rotl = 0x89,
|
||||
i_i64_rotr = 0x8A,
|
||||
|
||||
i_f32_abs = 0x8b,
|
||||
i_f32_neg = 0x8c,
|
||||
i_f32_ceil = 0x8d,
|
||||
i_f32_floor = 0x8e,
|
||||
i_f32_trunc = 0x8f,
|
||||
i_f32_nearest = 0x90,
|
||||
i_f32_sqrt = 0x91,
|
||||
i_f32_add = 0x92,
|
||||
i_f32_sub = 0x93,
|
||||
i_f32_mul = 0x94,
|
||||
i_f32_div = 0x95,
|
||||
i_f32_min = 0x96,
|
||||
i_f32_max = 0x97,
|
||||
i_f32_copysign = 0x98,
|
||||
|
||||
i_f64_abs = 0x99,
|
||||
i_f64_neg = 0x9a,
|
||||
i_f64_ceil = 0x9b,
|
||||
i_f64_floor = 0x9c,
|
||||
i_f64_trunc = 0x9d,
|
||||
i_f64_nearest = 0x9e,
|
||||
i_f64_sqrt = 0x9f,
|
||||
i_f64_add = 0xa0,
|
||||
i_f64_sub = 0xa1,
|
||||
i_f64_mul = 0xa2,
|
||||
i_f64_div = 0xa3,
|
||||
i_f64_min = 0xa4,
|
||||
i_f64_max = 0xa5,
|
||||
i_f64_copysign = 0xa6,
|
||||
|
||||
i_i32_wrap_i64 = 0xa7,
|
||||
i_i32_trunc_f32_s = 0xa8,
|
||||
i_i32_trunc_f32_u = 0xa9,
|
||||
i_i32_trunc_f64_s = 0xaa,
|
||||
i_i32_trunc_f64_u = 0xab,
|
||||
i_i64_extend_i32_s = 0xac,
|
||||
i_i64_extend_i32_u = 0xad,
|
||||
i_i64_trunc_f32_s = 0xae,
|
||||
i_i64_trunc_f32_u = 0xaf,
|
||||
i_i64_trunc_f64_s = 0xb0,
|
||||
i_i64_trunc_f64_u = 0xb1,
|
||||
i_f32_convert_i32_s = 0xb2,
|
||||
i_f32_convert_i32_u = 0xb3,
|
||||
i_f32_convert_i64_s = 0xb4,
|
||||
i_f32_convert_i64_u = 0xb5,
|
||||
i_f32_demote_f64 = 0xb6,
|
||||
i_f64_convert_i32_s = 0xb7,
|
||||
i_f64_convert_i32_u = 0xb8,
|
||||
i_f64_convert_i64_s = 0xb9,
|
||||
i_f64_convert_i64_u = 0xba,
|
||||
i_f64_promote_f32 = 0xbb,
|
||||
|
||||
i_i32_reinterpret_f32 = 0xbc,
|
||||
i_i64_reinterpret_f64 = 0xbd,
|
||||
i_f32_reinterpret_i32 = 0xbe,
|
||||
i_f64_reinterpret_i64 = 0xbf,
|
||||
|
||||
i_i32_extend8_s = 0xc0,
|
||||
i_i32_extend16_s = 0xc1,
|
||||
i_i64_extend8_s = 0xc2,
|
||||
i_i64_extend16_s = 0xc3,
|
||||
i_i64_extend32_s = 0xc4,
|
||||
|
||||
i_ref_null = 0xD0,
|
||||
i_ref_is_null = 0xD1,
|
||||
i_ref_func = 0xD2,
|
||||
|
||||
i_bulk_op = 0xFC,
|
||||
/* TODO: more instrs */
|
||||
|
||||
};
|
||||
|
||||
enum blocktype_tag {
|
||||
blocktype_empty,
|
||||
blocktype_valtype,
|
||||
blocktype_index,
|
||||
};
|
||||
|
||||
struct blocktype {
|
||||
enum blocktype_tag tag;
|
||||
union {
|
||||
enum valtype valtype;
|
||||
int type_index;
|
||||
};
|
||||
};
|
||||
|
||||
struct instrs {
|
||||
unsigned char *data;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
struct block {
|
||||
struct blocktype type;
|
||||
struct expr instrs;
|
||||
};
|
||||
|
||||
struct memarg {
|
||||
u32 offset;
|
||||
u32 align;
|
||||
};
|
||||
|
||||
struct br_table {
|
||||
u32 num_label_indices;
|
||||
u32 label_indices[512];
|
||||
u32 default_label;
|
||||
};
|
||||
|
||||
struct call_indirect {
|
||||
u32 tableidx;
|
||||
u32 typeidx;
|
||||
};
|
||||
|
||||
struct table_init {
|
||||
u32 tableidx;
|
||||
u32 elemidx;
|
||||
};
|
||||
|
||||
struct table_copy {
|
||||
u32 from;
|
||||
u32 to;
|
||||
};
|
||||
|
||||
struct bulk_op {
|
||||
enum bulk_tag tag;
|
||||
union {
|
||||
struct table_init table_init;
|
||||
struct table_copy table_copy;
|
||||
u32 idx;
|
||||
};
|
||||
};
|
||||
|
||||
struct select_instr {
|
||||
u8 *valtypes;
|
||||
u32 num_valtypes;
|
||||
};
|
||||
|
||||
struct instr {
|
||||
enum instr_tag tag;
|
||||
int pos;
|
||||
union {
|
||||
struct br_table br_table;
|
||||
struct bulk_op bulk_op;
|
||||
struct call_indirect call_indirect;
|
||||
struct memarg memarg;
|
||||
struct select_instr select;
|
||||
struct block block;
|
||||
struct expr else_block;
|
||||
double f64;
|
||||
float f32;
|
||||
int i32;
|
||||
u32 u32;
|
||||
int64_t i64;
|
||||
u64 u64;
|
||||
unsigned char memidx;
|
||||
enum reftype reftype;
|
||||
};
|
||||
};
|
||||
|
||||
enum datamode {
|
||||
datamode_active,
|
||||
datamode_passive,
|
||||
};
|
||||
|
||||
struct wdata_active {
|
||||
u32 mem_index;
|
||||
struct expr offset_expr;
|
||||
};
|
||||
|
||||
struct wdata {
|
||||
struct wdata_active active;
|
||||
u8 *bytes;
|
||||
u32 bytes_len;
|
||||
enum datamode mode;
|
||||
};
|
||||
|
||||
struct datasec {
|
||||
struct wdata *datas;
|
||||
u32 num_datas;
|
||||
};
|
||||
|
||||
struct startsec {
|
||||
u32 start_fn;
|
||||
};
|
||||
|
||||
struct module {
|
||||
unsigned int parsed;
|
||||
unsigned int custom_sections;
|
||||
|
||||
struct func *funcs;
|
||||
|
||||
u32 num_funcs;
|
||||
|
||||
struct customsec custom_section[MAX_CUSTOM_SECTIONS];
|
||||
struct typesec type_section;
|
||||
struct funcsec func_section;
|
||||
struct importsec import_section;
|
||||
struct exportsec export_section;
|
||||
struct codesec code_section;
|
||||
struct tablesec table_section;
|
||||
struct memsec memory_section;
|
||||
struct globalsec global_section;
|
||||
struct startsec start_section;
|
||||
struct elemsec element_section;
|
||||
struct datasec data_section;
|
||||
struct namesec name_section;
|
||||
};
|
||||
|
||||
// make sure the struct is packed so that
|
||||
struct label {
|
||||
u32 instr_pos; // resolved status is stored in HOB of pos
|
||||
u32 jump;
|
||||
};
|
||||
|
||||
struct callframe {
|
||||
struct cursor code;
|
||||
struct val *locals;
|
||||
struct func *func;
|
||||
u16 prev_stack_items;
|
||||
};
|
||||
|
||||
struct resolver {
|
||||
u16 label;
|
||||
u8 end_tag;
|
||||
u8 start_tag;
|
||||
};
|
||||
|
||||
struct global_inst {
|
||||
struct val val;
|
||||
};
|
||||
|
||||
struct module_inst {
|
||||
struct table_inst *tables;
|
||||
struct global_inst *globals;
|
||||
struct elem_inst *elements;
|
||||
|
||||
u32 num_tables;
|
||||
u32 num_globals;
|
||||
u32 num_elements;
|
||||
|
||||
int start_fn;
|
||||
unsigned char *globals_init;
|
||||
};
|
||||
|
||||
struct wasi {
|
||||
int argc;
|
||||
const char **argv;
|
||||
|
||||
int environc;
|
||||
const char **environ;
|
||||
};
|
||||
|
||||
struct wasm_interp;
|
||||
|
||||
struct builtin {
|
||||
const char *name;
|
||||
int (*fn)(struct wasm_interp *);
|
||||
int (*prepare_args)(struct wasm_interp *);
|
||||
};
|
||||
|
||||
struct wasm_interp {
|
||||
struct module *module;
|
||||
struct module_inst module_inst;
|
||||
struct wasi wasi;
|
||||
void *context;
|
||||
|
||||
struct builtin builtins[MAX_BUILTINS];
|
||||
int num_builtins;
|
||||
|
||||
int prev_resolvers, quitting;
|
||||
|
||||
struct errors errors; /* struct error */
|
||||
size_t ops;
|
||||
|
||||
struct cursor callframes; /* struct callframe */
|
||||
struct cursor stack; /* struct val */
|
||||
struct cursor mem; /* u8/mixed */
|
||||
|
||||
struct cursor memory; /* memory pages (65536 blocks) */
|
||||
|
||||
struct cursor locals; /* struct val */
|
||||
struct cursor labels; /* struct labels */
|
||||
struct cursor num_labels;
|
||||
|
||||
// resolve stack for the current function. every time a control
|
||||
// instruction is encountered, the label index is pushed. When an
|
||||
// instruction is popped, we can resolve the label
|
||||
struct cursor resolver_stack; /* struct resolver */
|
||||
struct cursor resolver_offsets; /* int */
|
||||
};
|
||||
|
||||
struct wasm_parser {
|
||||
struct module module;
|
||||
struct builtin *builtins;
|
||||
u32 num_builtins;
|
||||
struct cursor cur;
|
||||
struct cursor mem;
|
||||
struct errors errs;
|
||||
};
|
||||
|
||||
|
||||
int run_wasm(unsigned char *wasm, unsigned long len, int argc, const char **argv, char **env, int *retval);
|
||||
int parse_wasm(struct wasm_parser *p);
|
||||
int wasm_interp_init(struct wasm_interp *interp, struct module *module);
|
||||
void wasm_parser_free(struct wasm_parser *parser);
|
||||
void wasm_parser_init(struct wasm_parser *p, u8 *wasm, size_t wasm_len, size_t arena_size, struct builtin *, int num_builtins);
|
||||
void wasm_interp_free(struct wasm_interp *interp);
|
||||
int interp_wasm_module(struct wasm_interp *interp, int *retval);
|
||||
int interp_wasm_module_resume(struct wasm_interp *interp, int *retval);
|
||||
void print_error_backtrace(struct errors *errors);
|
||||
void setup_wasi(struct wasm_interp *interp, int argc, const char **argv, char **env);
|
||||
void print_callstack(struct wasm_interp *interp);
|
||||
|
||||
// builtin helpers
|
||||
int get_params(struct wasm_interp *interp, struct val** vals, u32 num_vals);
|
||||
int get_var_params(struct wasm_interp *interp, struct val** vals, u32 *num_vals);
|
||||
u8 *interp_mem_ptr(struct wasm_interp *interp, u32 ptr, int size);
|
||||
|
||||
static INLINE struct callframe *top_callframe(struct cursor *cur)
|
||||
{
|
||||
return (struct callframe*)cursor_top(cur, sizeof(struct callframe));
|
||||
}
|
||||
|
||||
|
||||
static INLINE struct cursor *interp_codeptr(struct wasm_interp *interp)
|
||||
{
|
||||
struct callframe *frame;
|
||||
if (unlikely(!(frame = top_callframe(&interp->callframes))))
|
||||
return 0;
|
||||
return &frame->code;
|
||||
}
|
||||
|
||||
|
||||
static INLINE int mem_ptr_str(struct wasm_interp *interp, u32 ptr,
|
||||
const char **str)
|
||||
{
|
||||
// still technically unsafe if the string runs over the end of memory...
|
||||
if (!(*str = (const char*)interp_mem_ptr(interp, ptr, 1))) {
|
||||
return interp_error(interp, "int memptr");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static INLINE int mem_ptr_i32(struct wasm_interp *interp, u32 ptr, int **i)
|
||||
{
|
||||
if (!(*i = (int*)interp_mem_ptr(interp, ptr, sizeof(int))))
|
||||
return interp_error(interp, "int memptr");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static INLINE int cursor_pushval(struct cursor *cur, struct val *val)
|
||||
{
|
||||
return cursor_push(cur, (u8*)val, sizeof(*val));
|
||||
}
|
||||
|
||||
static INLINE int cursor_push_i32(struct cursor *stack, int i)
|
||||
{
|
||||
struct val val;
|
||||
val.type = val_i32;
|
||||
val.num.i32 = i;
|
||||
|
||||
return cursor_pushval(stack, &val);
|
||||
}
|
||||
|
||||
static INLINE int stack_push_i32(struct wasm_interp *interp, int i)
|
||||
{
|
||||
return cursor_push_i32(&interp->stack, i);
|
||||
}
|
||||
|
||||
static INLINE struct callframe *top_callframes(struct cursor *cur, int top)
|
||||
{
|
||||
return (struct callframe*)cursor_topn(cur, sizeof(struct callframe), top);
|
||||
}
|
||||
|
||||
static INLINE int was_section_parsed(struct module *module,
|
||||
enum section_tag section)
|
||||
{
|
||||
if (section == section_custom)
|
||||
return module->custom_sections > 0;
|
||||
|
||||
return module->parsed & (1 << section);
|
||||
}
|
||||
|
||||
|
||||
#endif /* PROTOVERSE_WASM_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,65 +1,12 @@
|
||||
{
|
||||
"originHash" : "1fc7e0b44329ba72cd285eeb022b5b92582cd01586b920d243cb0485c2e69dcc",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "codescanner",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/twostraws/CodeScanner.git",
|
||||
"state" : {
|
||||
"revision" : "9fa582f4b36c69c2a55bff5fb3377eb170ae273c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "cryptoswift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/krzyzanowskim/CryptoSwift.git",
|
||||
"state" : {
|
||||
"revision" : "e74bbbfbef939224b242ae7c342a90e60b88b5ce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "emojikit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/tyiu/EmojiKit",
|
||||
"state" : {
|
||||
"revision" : "47a4b1402de26be0299dcb4d667c1faaf21a7874",
|
||||
"version" : "0.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "emojipicker",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/tyiu/EmojiPicker.git",
|
||||
"state" : {
|
||||
"revision" : "3f48903721eae223238ff0af17c22d6373d33813",
|
||||
"version" : "0.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "faviconfinder",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/will-lumley/FaviconFinder.git",
|
||||
"state" : {
|
||||
"revision" : "9279f4371f4877ca302ba3bf1015f3f58ae4a56c",
|
||||
"version" : "5.1.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "gsplayer",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/wxxsw/GSPlayer",
|
||||
"state" : {
|
||||
"revision" : "aa6dad7943d52f5207f7fcc2ad3e4274583443b8",
|
||||
"version" : "0.2.26"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "kingfisher",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/onevcat/Kingfisher",
|
||||
"state" : {
|
||||
"revision" : "4c6b067f96953ee19526e49e4189403a2be21fb3",
|
||||
"version" : "8.3.1"
|
||||
"revision" : "017f94ccfdacabb1ae7f45b75b4217b24c06e6ac",
|
||||
"version" : "7.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -71,74 +18,31 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-collections",
|
||||
"identity" : "starscream",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections.git",
|
||||
"location" : "https://github.com/daltoniam/Starscream",
|
||||
"state" : {
|
||||
"revision" : "ee97538f5b81ae89698fd95938896dec5217b148",
|
||||
"version" : "1.1.1"
|
||||
"revision" : "df8d82047f6654d8e4b655d1b1525c64e1059d21",
|
||||
"version" : "4.0.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-markdown-ui",
|
||||
"identity" : "svgkit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/damus-io/swift-markdown-ui",
|
||||
"location" : "https://github.com/SVGKit/SVGKit",
|
||||
"state" : {
|
||||
"revision" : "76bb7971da7fbf429de1c84f1244adf657242fee"
|
||||
"revision" : "e1f13e27b1e4c0ffe20e7d8d3984bf49c2a584d0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-snapshot-testing",
|
||||
"identity" : "vault",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
|
||||
"location" : "https://github.com/SparrowTek/Vault",
|
||||
"state" : {
|
||||
"revision" : "5b356adceabff6ca027f6574aac79e9fee145d26",
|
||||
"version" : "1.14.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-syntax",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-syntax.git",
|
||||
"state" : {
|
||||
"revision" : "74203046135342e4a4a627476dd6caf8b28fe11b",
|
||||
"version" : "509.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-trie",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/tyiu/swift-trie",
|
||||
"state" : {
|
||||
"revision" : "4c50bff6c168f74425f70476be62a072980d2da7",
|
||||
"version" : "0.1.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftsoup",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/scinfu/SwiftSoup.git",
|
||||
"state" : {
|
||||
"revision" : "bba848db50462894e7fc0891d018dfecad4ef11e",
|
||||
"version" : "2.8.7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftycrop",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/benedom/SwiftyCrop",
|
||||
"state" : {
|
||||
"revision" : "454d0a0d4faf6f3a19c8d817ab9d7d27524bd79f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swipeactions",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/damus-io/SwipeActions.git",
|
||||
"state" : {
|
||||
"revision" : "33d99756c3112e1a07c1732e3cddc5ad5bd0c5f4"
|
||||
"revision" : "87db56c3c8b6421c65b0745f73e08b0dc56f79d4",
|
||||
"version" : "1.0.3"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
"version" : 2
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1520"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D79C4C132AFEB061003A41B4"
|
||||
BuildableName = "DamusNotificationService.appex"
|
||||
BlueprintName = "DamusNotificationService"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "1"
|
||||
BundleIdentifier = "com.jb55.damus2"
|
||||
RemotePath = "/Users/danielnogueira/Library/Developer/CoreSimulator/Devices/99E60B35-CE5D-4B45-AC35-00818C0AF3CB/data/Containers/Bundle/Application/7D0A5302-D07E-4C7C-B509-A7C552BD5A65/damus.app">
|
||||
</RemoteRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,101 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1540"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D703D7162C66E47100A400EA"
|
||||
BuildableName = "HighlighterActionExtension.appex"
|
||||
BlueprintName = "HighlighterActionExtension"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "1"
|
||||
BundleIdentifier = "com.apple.mobilesafari"
|
||||
RemotePath = "/Library/Developer/CoreSimulator/Volumes/iOS_21F79/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.5.simruntime/Contents/Resources/RuntimeRoot/Applications/MobileSafari.app">
|
||||
</RemoteRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,98 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1520"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEF227F7A08200C66700"
|
||||
BuildableName = "damusTests.xctest"
|
||||
BlueprintName = "damusTests"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEFC27F7A08200C66700"
|
||||
BuildableName = "damusUITests.xctest"
|
||||
BlueprintName = "damusUITests"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,102 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1520"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEF227F7A08200C66700"
|
||||
BuildableName = "damusTests.xctest"
|
||||
BlueprintName = "damusTests"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEFC27F7A08200C66700"
|
||||
BuildableName = "damusUITests.xctest"
|
||||
BlueprintName = "damusUITests"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<StoreKitConfigurationFileReference
|
||||
identifier = "../../Purple.storekit">
|
||||
</StoreKitConfigurationFileReference>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
|
||||
BuildableName = "damus.app"
|
||||
BlueprintName = "damus"
|
||||
ReferencedContainer = "container:damus.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -11,6 +11,24 @@
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC5",
|
||||
"green" : "0x43",
|
||||
"red" : "0xCC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<svg width="430" height="813" viewBox="0 0 430 813" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_f_1069_29012)">
|
||||
<path d="M44.0811 256.851L186.315 111L276.203 223.574L244.02 388.295L69.9751 697.084L100.678 498.338L44.0811 256.851Z" fill="url(#paint0_linear_1069_29012)"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_f_1069_29012)">
|
||||
<path d="M116.509 587.348L206.677 479.401L230.746 273.265L266.666 231.183L367.424 396.975L281.292 659.008L266.665 801.413L66.889 763.694L116.509 587.348Z" fill="url(#paint1_linear_1069_29012)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_1069_29012" x="-66.6248" y="0.294121" width="453.534" height="807.496" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="55.3529" result="effect1_foregroundBlur_1069_29012"/>
|
||||
</filter>
|
||||
<filter id="filter1_f_1069_29012" x="-43.8172" y="120.477" width="521.947" height="791.642" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="55.3529" result="effect1_foregroundBlur_1069_29012"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_1069_29012" x1="230.179" y1="166.577" x2="-67.7956" y2="310.108" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D34CD9"/>
|
||||
<stop offset="1" stop-color="#4E4DF4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1069_29012" x1="139.483" y1="462.902" x2="377.854" y2="565.47" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0DE8FF"/>
|
||||
<stop offset="1" stop-color="#641AAE"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 118 KiB |
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "header.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 511 KiB |
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1A",
|
||||
"green" : "0x93",
|
||||
"red" : "0xF7"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0x00",
|
||||
"red" : "0x00"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xD7",
|
||||
"green" : "0xD1",
|
||||
"red" : "0xD1"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x13",
|
||||
"green" : "0x11",
|
||||
"red" : "0x11"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF4",
|
||||
"green" : "0xEE",
|
||||
"red" : "0xEE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1E",
|
||||
"green" : "0x1C",
|
||||
"red" : "0x1C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF9",
|
||||
"green" : "0xF3",
|
||||
"red" : "0xF3"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x25",
|
||||
"green" : "0x22",
|
||||
"red" : "0x22"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "244",
|
||||
"green" : "218",
|
||||
"red" : "244"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "92",
|
||||
"green" : "45",
|
||||
"red" : "93"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "236",
|
||||
"green" : "194",
|
||||
"red" : "238"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "109",
|
||||
"green" : "49",
|
||||
"red" : "111"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "197",
|
||||
"green" : "67",
|
||||
"red" : "204"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "255",
|
||||
"green" : "194",
|
||||
"red" : "255"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0x00",
|
||||
"red" : "0x00"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,24 @@
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0x00",
|
||||
"red" : "0x00"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
@@ -11,6 +11,24 @@
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0x4D",
|
||||
"red" : "0x4B"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"red" : "0xBE",
|
||||
"green" : "0x5F",
|
||||
"blue" : "0x00"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xE3",
|
||||
"green" : "0xD7",
|
||||
"red" : "0xF7"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x20",
|
||||
"green" : "0x13",
|
||||
"red" : "0x61"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x63",
|
||||
"green" : "0x11",
|
||||
"red" : "0xF5"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x6E",
|
||||
"green" : "0x20",
|
||||
"red" : "0xF8"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xEE",
|
||||
"green" : "0xE8",
|
||||
"red" : "0xF7"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x35",
|
||||
"green" : "0x04",
|
||||
"red" : "0x8B"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x3D",
|
||||
"green" : "0x07",
|
||||
"red" : "0x9C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x44",
|
||||
"green" : "0x06",
|
||||
"red" : "0xB2"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x2D",
|
||||
"green" : "0x05",
|
||||
"red" : "0x75"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xD8",
|
||||
"green" : "0xC2",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,24 @@
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1E",
|
||||
"green" : "0x1C",
|
||||
"red" : "0x1C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xED",
|
||||
"green" : "0x26",
|
||||
"red" : "0xBF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,24 @@
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x4F",
|
||||
"green" : "0xC3",
|
||||
"red" : "0x66"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF2",
|
||||
"green" : "0xD8",
|
||||
"red" : "0xF4"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x45",
|
||||
"green" : "0x17",
|
||||
"red" : "0x47"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,24 @@
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF4",
|
||||
"green" : "0xEE",
|
||||
"red" : "0xEE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
@@ -11,6 +11,24 @@
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x5F",
|
||||
"green" : "0x5F",
|
||||
"red" : "0x5F"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFA",
|
||||
"green" : "0xFA",
|
||||
"red" : "0xF9"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x24",
|
||||
"green" : "0x22",
|
||||
"red" : "0x20"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xE3",
|
||||
"green" : "0xE1",
|
||||
"red" : "0xDD"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x2A",
|
||||
"green" : "0x26",
|
||||
"red" : "0x23"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x59",
|
||||
"green" : "0x53",
|
||||
"red" : "0x4A"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x85",
|
||||
"green" : "0x7A",
|
||||
"red" : "0x6A"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,24 @@
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC5",
|
||||
"green" : "0x43",
|
||||
"red" : "0xCC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xE4",
|
||||
"green" : "0xF1",
|
||||
"red" : "0xD6"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x38",
|
||||
"green" : "0x5C",
|
||||
"red" : "0x12"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x5A",
|
||||
"green" : "0xAB",
|
||||
"red" : "0x04"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x64",
|
||||
"green" : "0xBF",
|
||||
"red" : "0x03"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF0",
|
||||
"green" : "0xF7",
|
||||
"red" : "0xE8"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1F",
|
||||
"green" : "0x33",
|
||||
"red" : "0x0A"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x34",
|
||||
"green" : "0x64",
|
||||
"red" : "0x02"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x3F",
|
||||
"green" : "0x79",
|
||||
"red" : "0x02"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1F",
|
||||
"green" : "0x3C",
|
||||
"red" : "0x01"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xE4",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xAD"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xD1",
|
||||
"green" : "0xEE",
|
||||
"red" : "0xFE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x12",
|
||||
"green" : "0x43",
|
||||
"red" : "0x5C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1C",
|
||||
"green" : "0xAD",
|
||||
"red" : "0xF9"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x2C",
|
||||
"green" : "0xB5",
|
||||
"red" : "0xFC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xE1",
|
||||
"green" : "0xF4",
|
||||
"red" : "0xFE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x0A",
|
||||
"green" : "0x25",
|
||||
"red" : "0x33"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x06",
|
||||
"green" : "0x85",
|
||||
"red" : "0xC6"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x03",
|
||||
"green" : "0x93",
|
||||
"red" : "0xDD"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x04",
|
||||
"green" : "0x6A",
|
||||
"red" : "0x9F"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xCC",
|
||||
"green" : "0xF5",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,24 @@
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user