Measurement FormatStyle (#1) - #22

Closed
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main
Closed

Measurement FormatStyle (#1)#22
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main

Conversation

@wildthink

Copy link
Copy Markdown

Added Measurement and Percent FormatStyle. Tests and updated README included.

@NeedleInAJayStackNeedleInAJayStack left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Thanks for contributing!

Comment on lines +1 to +8
//
// Formatter.swift
// Units
// (aka Fountation.FormatStyle)
//
// Created by Jason Jobe on 10/24/25.
//

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this header; we can track dates/contributions using Git.

Comment on lines +9 to +32
public extension Measurement {
struct Formatter<Output> {
let format: (Measurement) -> Output
}

func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
formatter.format(self)
}

func formatted(_ formatter: Formatter<String> = .measurement()) -> String {
formatter.format(self)
}

func formatted(
minimumFractionDigits: Int = 0,
maximumFractionDigits: Int = 4
) -> String {
Formatter
.measurement(
minimumFractionDigits: minimumFractionDigits,
maximumFractionDigits: maximumFractionDigits)
.format(self)
}
}

@NeedleInAJayStackNeedleInAJayStackOct 27, 2025

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using Swift's built-in NumberFormatter instead of building our own format system? Example:

// Implementation
extensionNumberFormatter{func string(from measurement:Measurement){return"\(self.string(from:.init(value: measurement.value))\(measurement.unit.symbol)"}}
// Usage
letmeasurement=28.123.measured(in:.meter)letformatter=NumberFormatter()
formatter.maximumFractionDigits =2print(formatter.string(from: measurement)) // Prints `28.12 m`

This seems like this approach would inherit very configurable options, while also simplifying the implementation and usage. Thoughts?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was leaning into the newer Format API. The custom implementation is limiting. I'll work on that.

Comment on lines +68 to +74
extension Percent: Equatable {
/// Implemented as "nearly" equal
public static func ==(lhs: Percent, rhs: Percent) -> Bool {
lhs.magnitude >= rhs.magnitude.nextDown
&& lhs.magnitude <= lhs.magnitude.nextUp
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change Equatable implementation to nearly equal? Is this related to the tests that were added?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The significant digits of a "percent" is usually small so representing them with a Double in the arithmetic can lead to values that might result in something like 37.989488484% leading to comparisons that break our intuition (like == 38%). This felt like a reasonable compromise.

Comment on lines +219 to +231
extension Percent {
/**
Returns a random value within the given range.

```
Percent.random(in: 10%...20%)
// 10%, 11%, 12%, 19.98%, etc.
```
*/
public static func random(in range: ClosedRange<Self>) -> Self {
self.init(magnitude: .random(in: range.lowerBound.magnitude...range.upperBound.magnitude))
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is Percent.random(in: 10%...20%)) a big improvement over Percent(magnitude: .random(in: 10...20))? I'm inclined to think that the existing approach is fine, but I'd be interested in your thoughts.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one sense, it is just syntactic sugar but it makes the intent clearer and more succinct and encapsulates the internals reducing developer cognitive load.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@wildthink@NeedleInAJayStack
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Measurement FormatStyle (#1) - #22

Closed
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main
Closed

Measurement FormatStyle (#1)#22
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main

Conversation

@wildthink

Copy link
Copy Markdown

Added Measurement and Percent FormatStyle. Tests and updated README included.

@NeedleInAJayStackNeedleInAJayStack left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Thanks for contributing!

Comment on lines +1 to +8
//
// Formatter.swift
// Units
// (aka Fountation.FormatStyle)
//
// Created by Jason Jobe on 10/24/25.
//

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this header; we can track dates/contributions using Git.

Comment on lines +9 to +32
public extension Measurement {
struct Formatter<Output> {
let format: (Measurement) -> Output
}

func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
formatter.format(self)
}

func formatted(_ formatter: Formatter<String> = .measurement()) -> String {
formatter.format(self)
}

func formatted(
minimumFractionDigits: Int = 0,
maximumFractionDigits: Int = 4
) -> String {
Formatter
.measurement(
minimumFractionDigits: minimumFractionDigits,
maximumFractionDigits: maximumFractionDigits)
.format(self)
}
}

@NeedleInAJayStackNeedleInAJayStackOct 27, 2025

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using Swift's built-in NumberFormatter instead of building our own format system? Example:

// Implementation
extensionNumberFormatter{func string(from measurement:Measurement){return"\(self.string(from:.init(value: measurement.value))\(measurement.unit.symbol)"}}
// Usage
letmeasurement=28.123.measured(in:.meter)letformatter=NumberFormatter()
formatter.maximumFractionDigits =2print(formatter.string(from: measurement)) // Prints `28.12 m`

This seems like this approach would inherit very configurable options, while also simplifying the implementation and usage. Thoughts?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was leaning into the newer Format API. The custom implementation is limiting. I'll work on that.

Comment on lines +68 to +74
extension Percent: Equatable {
/// Implemented as "nearly" equal
public static func ==(lhs: Percent, rhs: Percent) -> Bool {
lhs.magnitude >= rhs.magnitude.nextDown
&& lhs.magnitude <= lhs.magnitude.nextUp
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change Equatable implementation to nearly equal? Is this related to the tests that were added?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The significant digits of a "percent" is usually small so representing them with a Double in the arithmetic can lead to values that might result in something like 37.989488484% leading to comparisons that break our intuition (like == 38%). This felt like a reasonable compromise.

Comment on lines +219 to +231
extension Percent {
/**
Returns a random value within the given range.

```
Percent.random(in: 10%...20%)
// 10%, 11%, 12%, 19.98%, etc.
```
*/
public static func random(in range: ClosedRange<Self>) -> Self {
self.init(magnitude: .random(in: range.lowerBound.magnitude...range.upperBound.magnitude))
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is Percent.random(in: 10%...20%)) a big improvement over Percent(magnitude: .random(in: 10...20))? I'm inclined to think that the existing approach is fine, but I'd be interested in your thoughts.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one sense, it is just syntactic sugar but it makes the intent clearer and more succinct and encapsulates the internals reducing developer cognitive load.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@wildthink@NeedleInAJayStack
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Measurement FormatStyle (#1) - #22

Closed
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main
Closed

Measurement FormatStyle (#1)#22
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main

Conversation

@wildthink

Copy link
Copy Markdown

Added Measurement and Percent FormatStyle. Tests and updated README included.

@NeedleInAJayStackNeedleInAJayStack left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Thanks for contributing!

Comment on lines +1 to +8
//
// Formatter.swift
// Units
// (aka Fountation.FormatStyle)
//
// Created by Jason Jobe on 10/24/25.
//

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this header; we can track dates/contributions using Git.

Comment on lines +9 to +32
public extension Measurement {
struct Formatter<Output> {
let format: (Measurement) -> Output
}

func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
formatter.format(self)
}

func formatted(_ formatter: Formatter<String> = .measurement()) -> String {
formatter.format(self)
}

func formatted(
minimumFractionDigits: Int = 0,
maximumFractionDigits: Int = 4
) -> String {
Formatter
.measurement(
minimumFractionDigits: minimumFractionDigits,
maximumFractionDigits: maximumFractionDigits)
.format(self)
}
}

@NeedleInAJayStackNeedleInAJayStackOct 27, 2025

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using Swift's built-in NumberFormatter instead of building our own format system? Example:

// Implementation
extensionNumberFormatter{func string(from measurement:Measurement){return"\(self.string(from:.init(value: measurement.value))\(measurement.unit.symbol)"}}
// Usage
letmeasurement=28.123.measured(in:.meter)letformatter=NumberFormatter()
formatter.maximumFractionDigits =2print(formatter.string(from: measurement)) // Prints `28.12 m`

This seems like this approach would inherit very configurable options, while also simplifying the implementation and usage. Thoughts?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was leaning into the newer Format API. The custom implementation is limiting. I'll work on that.

Comment on lines +68 to +74
extension Percent: Equatable {
/// Implemented as "nearly" equal
public static func ==(lhs: Percent, rhs: Percent) -> Bool {
lhs.magnitude >= rhs.magnitude.nextDown
&& lhs.magnitude <= lhs.magnitude.nextUp
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change Equatable implementation to nearly equal? Is this related to the tests that were added?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The significant digits of a "percent" is usually small so representing them with a Double in the arithmetic can lead to values that might result in something like 37.989488484% leading to comparisons that break our intuition (like == 38%). This felt like a reasonable compromise.

Comment on lines +219 to +231
extension Percent {
/**
Returns a random value within the given range.

```
Percent.random(in: 10%...20%)
// 10%, 11%, 12%, 19.98%, etc.
```
*/
public static func random(in range: ClosedRange<Self>) -> Self {
self.init(magnitude: .random(in: range.lowerBound.magnitude...range.upperBound.magnitude))
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is Percent.random(in: 10%...20%)) a big improvement over Percent(magnitude: .random(in: 10...20))? I'm inclined to think that the existing approach is fine, but I'd be interested in your thoughts.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one sense, it is just syntactic sugar but it makes the intent clearer and more succinct and encapsulates the internals reducing developer cognitive load.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@wildthink@NeedleInAJayStack
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Measurement FormatStyle (#1) - #22

Closed
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main
Closed

Measurement FormatStyle (#1)#22
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main

Conversation

@wildthink

Copy link
Copy Markdown

Added Measurement and Percent FormatStyle. Tests and updated README included.

@NeedleInAJayStackNeedleInAJayStack left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Thanks for contributing!

Comment on lines +1 to +8
//
// Formatter.swift
// Units
// (aka Fountation.FormatStyle)
//
// Created by Jason Jobe on 10/24/25.
//

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this header; we can track dates/contributions using Git.

Comment on lines +9 to +32
public extension Measurement {
struct Formatter<Output> {
let format: (Measurement) -> Output
}

func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
formatter.format(self)
}

func formatted(_ formatter: Formatter<String> = .measurement()) -> String {
formatter.format(self)
}

func formatted(
minimumFractionDigits: Int = 0,
maximumFractionDigits: Int = 4
) -> String {
Formatter
.measurement(
minimumFractionDigits: minimumFractionDigits,
maximumFractionDigits: maximumFractionDigits)
.format(self)
}
}

@NeedleInAJayStackNeedleInAJayStackOct 27, 2025

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using Swift's built-in NumberFormatter instead of building our own format system? Example:

// Implementation
extensionNumberFormatter{func string(from measurement:Measurement){return"\(self.string(from:.init(value: measurement.value))\(measurement.unit.symbol)"}}
// Usage
letmeasurement=28.123.measured(in:.meter)letformatter=NumberFormatter()
formatter.maximumFractionDigits =2print(formatter.string(from: measurement)) // Prints `28.12 m`

This seems like this approach would inherit very configurable options, while also simplifying the implementation and usage. Thoughts?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was leaning into the newer Format API. The custom implementation is limiting. I'll work on that.

Comment on lines +68 to +74
extension Percent: Equatable {
/// Implemented as "nearly" equal
public static func ==(lhs: Percent, rhs: Percent) -> Bool {
lhs.magnitude >= rhs.magnitude.nextDown
&& lhs.magnitude <= lhs.magnitude.nextUp
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change Equatable implementation to nearly equal? Is this related to the tests that were added?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The significant digits of a "percent" is usually small so representing them with a Double in the arithmetic can lead to values that might result in something like 37.989488484% leading to comparisons that break our intuition (like == 38%). This felt like a reasonable compromise.

Comment on lines +219 to +231
extension Percent {
/**
Returns a random value within the given range.

```
Percent.random(in: 10%...20%)
// 10%, 11%, 12%, 19.98%, etc.
```
*/
public static func random(in range: ClosedRange<Self>) -> Self {
self.init(magnitude: .random(in: range.lowerBound.magnitude...range.upperBound.magnitude))
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is Percent.random(in: 10%...20%)) a big improvement over Percent(magnitude: .random(in: 10...20))? I'm inclined to think that the existing approach is fine, but I'd be interested in your thoughts.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one sense, it is just syntactic sugar but it makes the intent clearer and more succinct and encapsulates the internals reducing developer cognitive load.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@wildthink@NeedleInAJayStack
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Measurement FormatStyle (#1) - #22

Closed
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main
Closed

Measurement FormatStyle (#1)#22
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main

Conversation

@wildthink

Copy link
Copy Markdown

Added Measurement and Percent FormatStyle. Tests and updated README included.

@NeedleInAJayStackNeedleInAJayStack left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Thanks for contributing!

Comment on lines +1 to +8
//
// Formatter.swift
// Units
// (aka Fountation.FormatStyle)
//
// Created by Jason Jobe on 10/24/25.
//

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this header; we can track dates/contributions using Git.

Comment on lines +9 to +32
public extension Measurement {
struct Formatter<Output> {
let format: (Measurement) -> Output
}

func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
formatter.format(self)
}

func formatted(_ formatter: Formatter<String> = .measurement()) -> String {
formatter.format(self)
}

func formatted(
minimumFractionDigits: Int = 0,
maximumFractionDigits: Int = 4
) -> String {
Formatter
.measurement(
minimumFractionDigits: minimumFractionDigits,
maximumFractionDigits: maximumFractionDigits)
.format(self)
}
}

@NeedleInAJayStackNeedleInAJayStackOct 27, 2025

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using Swift's built-in NumberFormatter instead of building our own format system? Example:

// Implementation
extensionNumberFormatter{func string(from measurement:Measurement){return"\(self.string(from:.init(value: measurement.value))\(measurement.unit.symbol)"}}
// Usage
letmeasurement=28.123.measured(in:.meter)letformatter=NumberFormatter()
formatter.maximumFractionDigits =2print(formatter.string(from: measurement)) // Prints `28.12 m`

This seems like this approach would inherit very configurable options, while also simplifying the implementation and usage. Thoughts?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was leaning into the newer Format API. The custom implementation is limiting. I'll work on that.

Comment on lines +68 to +74
extension Percent: Equatable {
/// Implemented as "nearly" equal
public static func ==(lhs: Percent, rhs: Percent) -> Bool {
lhs.magnitude >= rhs.magnitude.nextDown
&& lhs.magnitude <= lhs.magnitude.nextUp
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change Equatable implementation to nearly equal? Is this related to the tests that were added?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The significant digits of a "percent" is usually small so representing them with a Double in the arithmetic can lead to values that might result in something like 37.989488484% leading to comparisons that break our intuition (like == 38%). This felt like a reasonable compromise.

Comment on lines +219 to +231
extension Percent {
/**
Returns a random value within the given range.

```
Percent.random(in: 10%...20%)
// 10%, 11%, 12%, 19.98%, etc.
```
*/
public static func random(in range: ClosedRange<Self>) -> Self {
self.init(magnitude: .random(in: range.lowerBound.magnitude...range.upperBound.magnitude))
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is Percent.random(in: 10%...20%)) a big improvement over Percent(magnitude: .random(in: 10...20))? I'm inclined to think that the existing approach is fine, but I'd be interested in your thoughts.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one sense, it is just syntactic sugar but it makes the intent clearer and more succinct and encapsulates the internals reducing developer cognitive load.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@wildthink@NeedleInAJayStack
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Measurement FormatStyle (#1) - #22

Closed
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main
Closed

Measurement FormatStyle (#1)#22
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main

Conversation

@wildthink

Copy link
Copy Markdown

Added Measurement and Percent FormatStyle. Tests and updated README included.

@NeedleInAJayStackNeedleInAJayStack left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Thanks for contributing!

Comment on lines +1 to +8
//
// Formatter.swift
// Units
// (aka Fountation.FormatStyle)
//
// Created by Jason Jobe on 10/24/25.
//

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this header; we can track dates/contributions using Git.

Comment on lines +9 to +32
public extension Measurement {
struct Formatter<Output> {
let format: (Measurement) -> Output
}

func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
formatter.format(self)
}

func formatted(_ formatter: Formatter<String> = .measurement()) -> String {
formatter.format(self)
}

func formatted(
minimumFractionDigits: Int = 0,
maximumFractionDigits: Int = 4
) -> String {
Formatter
.measurement(
minimumFractionDigits: minimumFractionDigits,
maximumFractionDigits: maximumFractionDigits)
.format(self)
}
}

@NeedleInAJayStackNeedleInAJayStackOct 27, 2025

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using Swift's built-in NumberFormatter instead of building our own format system? Example:

// Implementation
extensionNumberFormatter{func string(from measurement:Measurement){return"\(self.string(from:.init(value: measurement.value))\(measurement.unit.symbol)"}}
// Usage
letmeasurement=28.123.measured(in:.meter)letformatter=NumberFormatter()
formatter.maximumFractionDigits =2print(formatter.string(from: measurement)) // Prints `28.12 m`

This seems like this approach would inherit very configurable options, while also simplifying the implementation and usage. Thoughts?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was leaning into the newer Format API. The custom implementation is limiting. I'll work on that.

Comment on lines +68 to +74
extension Percent: Equatable {
/// Implemented as "nearly" equal
public static func ==(lhs: Percent, rhs: Percent) -> Bool {
lhs.magnitude >= rhs.magnitude.nextDown
&& lhs.magnitude <= lhs.magnitude.nextUp
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change Equatable implementation to nearly equal? Is this related to the tests that were added?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The significant digits of a "percent" is usually small so representing them with a Double in the arithmetic can lead to values that might result in something like 37.989488484% leading to comparisons that break our intuition (like == 38%). This felt like a reasonable compromise.

Comment on lines +219 to +231
extension Percent {
/**
Returns a random value within the given range.

```
Percent.random(in: 10%...20%)
// 10%, 11%, 12%, 19.98%, etc.
```
*/
public static func random(in range: ClosedRange<Self>) -> Self {
self.init(magnitude: .random(in: range.lowerBound.magnitude...range.upperBound.magnitude))
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is Percent.random(in: 10%...20%)) a big improvement over Percent(magnitude: .random(in: 10...20))? I'm inclined to think that the existing approach is fine, but I'd be interested in your thoughts.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one sense, it is just syntactic sugar but it makes the intent clearer and more succinct and encapsulates the internals reducing developer cognitive load.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@wildthink@NeedleInAJayStack
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Measurement FormatStyle (#1) - #22

Closed
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main
Closed

Measurement FormatStyle (#1)#22
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main

Conversation

@wildthink

Copy link
Copy Markdown

Added Measurement and Percent FormatStyle. Tests and updated README included.

@NeedleInAJayStackNeedleInAJayStack left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Thanks for contributing!

Comment on lines +1 to +8
//
// Formatter.swift
// Units
// (aka Fountation.FormatStyle)
//
// Created by Jason Jobe on 10/24/25.
//

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this header; we can track dates/contributions using Git.

Comment on lines +9 to +32
public extension Measurement {
struct Formatter<Output> {
let format: (Measurement) -> Output
}

func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
formatter.format(self)
}

func formatted(_ formatter: Formatter<String> = .measurement()) -> String {
formatter.format(self)
}

func formatted(
minimumFractionDigits: Int = 0,
maximumFractionDigits: Int = 4
) -> String {
Formatter
.measurement(
minimumFractionDigits: minimumFractionDigits,
maximumFractionDigits: maximumFractionDigits)
.format(self)
}
}

@NeedleInAJayStackNeedleInAJayStackOct 27, 2025

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using Swift's built-in NumberFormatter instead of building our own format system? Example:

// Implementation
extensionNumberFormatter{func string(from measurement:Measurement){return"\(self.string(from:.init(value: measurement.value))\(measurement.unit.symbol)"}}
// Usage
letmeasurement=28.123.measured(in:.meter)letformatter=NumberFormatter()
formatter.maximumFractionDigits =2print(formatter.string(from: measurement)) // Prints `28.12 m`

This seems like this approach would inherit very configurable options, while also simplifying the implementation and usage. Thoughts?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was leaning into the newer Format API. The custom implementation is limiting. I'll work on that.

Comment on lines +68 to +74
extension Percent: Equatable {
/// Implemented as "nearly" equal
public static func ==(lhs: Percent, rhs: Percent) -> Bool {
lhs.magnitude >= rhs.magnitude.nextDown
&& lhs.magnitude <= lhs.magnitude.nextUp
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change Equatable implementation to nearly equal? Is this related to the tests that were added?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The significant digits of a "percent" is usually small so representing them with a Double in the arithmetic can lead to values that might result in something like 37.989488484% leading to comparisons that break our intuition (like == 38%). This felt like a reasonable compromise.

Comment on lines +219 to +231
extension Percent {
/**
Returns a random value within the given range.

```
Percent.random(in: 10%...20%)
// 10%, 11%, 12%, 19.98%, etc.
```
*/
public static func random(in range: ClosedRange<Self>) -> Self {
self.init(magnitude: .random(in: range.lowerBound.magnitude...range.upperBound.magnitude))
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is Percent.random(in: 10%...20%)) a big improvement over Percent(magnitude: .random(in: 10...20))? I'm inclined to think that the existing approach is fine, but I'd be interested in your thoughts.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one sense, it is just syntactic sugar but it makes the intent clearer and more succinct and encapsulates the internals reducing developer cognitive load.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@wildthink@NeedleInAJayStack
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Measurement FormatStyle (#1) - #22

Closed
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main
Closed

Measurement FormatStyle (#1)#22
wildthink wants to merge 0 commit into
NeedleInAJayStack:mainfrom
wildthink:main

Conversation

@wildthink

Copy link
Copy Markdown

Added Measurement and Percent FormatStyle. Tests and updated README included.

@NeedleInAJayStackNeedleInAJayStack left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Thanks for contributing!

Comment on lines +1 to +8
//
// Formatter.swift
// Units
// (aka Fountation.FormatStyle)
//
// Created by Jason Jobe on 10/24/25.
//

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this header; we can track dates/contributions using Git.

Comment on lines +9 to +32
public extension Measurement {
struct Formatter<Output> {
let format: (Measurement) -> Output
}

func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
formatter.format(self)
}

func formatted(_ formatter: Formatter<String> = .measurement()) -> String {
formatter.format(self)
}

func formatted(
minimumFractionDigits: Int = 0,
maximumFractionDigits: Int = 4
) -> String {
Formatter
.measurement(
minimumFractionDigits: minimumFractionDigits,
maximumFractionDigits: maximumFractionDigits)
.format(self)
}
}

@NeedleInAJayStackNeedleInAJayStackOct 27, 2025

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using Swift's built-in NumberFormatter instead of building our own format system? Example:

// Implementation
extensionNumberFormatter{func string(from measurement:Measurement){return"\(self.string(from:.init(value: measurement.value))\(measurement.unit.symbol)"}}
// Usage
letmeasurement=28.123.measured(in:.meter)letformatter=NumberFormatter()
formatter.maximumFractionDigits =2print(formatter.string(from: measurement)) // Prints `28.12 m`

This seems like this approach would inherit very configurable options, while also simplifying the implementation and usage. Thoughts?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was leaning into the newer Format API. The custom implementation is limiting. I'll work on that.

Comment on lines +68 to +74
extension Percent: Equatable {
/// Implemented as "nearly" equal
public static func ==(lhs: Percent, rhs: Percent) -> Bool {
lhs.magnitude >= rhs.magnitude.nextDown
&& lhs.magnitude <= lhs.magnitude.nextUp
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change Equatable implementation to nearly equal? Is this related to the tests that were added?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The significant digits of a "percent" is usually small so representing them with a Double in the arithmetic can lead to values that might result in something like 37.989488484% leading to comparisons that break our intuition (like == 38%). This felt like a reasonable compromise.

Comment on lines +219 to +231
extension Percent {
/**
Returns a random value within the given range.

```
Percent.random(in: 10%...20%)
// 10%, 11%, 12%, 19.98%, etc.
```
*/
public static func random(in range: ClosedRange<Self>) -> Self {
self.init(magnitude: .random(in: range.lowerBound.magnitude...range.upperBound.magnitude))
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is Percent.random(in: 10%...20%)) a big improvement over Percent(magnitude: .random(in: 10...20))? I'm inclined to think that the existing approach is fine, but I'd be interested in your thoughts.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one sense, it is just syntactic sugar but it makes the intent clearer and more succinct and encapsulates the internals reducing developer cognitive load.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@wildthink@NeedleInAJayStack