1945 stories
·
1 follower

Which Emoji Scissors Close

1 Share

“Wh0”:

Ah, scissors. They’re important enough that we have an emoji for them. On your device, it appears as ✂️. Unlike the real world tool it represents, the emoji’s job is to convey the idea, especially at small sizes. It doesn’t need to be able to swing or cut things. Nevertheless, let’s judge them on that irrelevant criterion.

Fun work. Turns out most emoji scissors wouldn’t actually close. I’m curious if the ones that would close somehow look worse at small sizes, or if this is something that most scissor emoji artists never bothered to consider. (Via Andy Baio.)

Link: wh0.github.io/2020/01/02/scissors.html

Read the whole story
emrox
1 day ago
reply
Hamburg, Germany
Share this story
Delete

SKYWATCH: A Sci-Fi Short

1 Share

When two outcast teens hack into a ubiquitous drone delivery system to pull a prank on their neighbor, they accidentally crash-land a dangerous prototype – and find themselves entangled in a life-and-death conspiracy.

Good story, good acting, great VFX quality, … and totally plausible too!

There’s also an accompanying movie available, explaining how they got Jude Law to star in this short (be sure to watch the short itself first, before you watch this extra video)

Read the whole story
emrox
2 days ago
reply
Hamburg, Germany
Share this story
Delete

Someone At Amazon Accidentally Sent Out Their E-Mail Template, And It’s Hilarious

1 Share

Now listen. When Reddit user loyaltystar posted a screenshot on r/funny, they went to work without giving it much thought. When they came back, it had tens of thousands of upvotes. As of this article, the actual number is 76K.

Supposedly, its Amazon’s e-mail template. And no, nobody hacked it or anything. It came into this world the same way I did. By accident. Someone at the company sent it out and we don’t know if that person still has their job or not, but let’s appreciate the brilliant text either way. It’s a comprehensive tutorial to better writing.

Image credits: loyaltystar

The piece actually belongs to Gary Provost (1944–1995). He was an American writer and writing instructor, author of works including Make Every Word Count: A Guide to Writing that Works–for fiction and nonfiction and 100 Ways to Improve Your Writing: Proven Professional Techniques for Writing with Style and Power. The little text is a reminder that you have to keep your audience on their toes. To dance with them. And it gets the message across perfectly.

Here’s what people said about the e-mail template

Read the whole story
emrox
2 days ago
reply
Hamburg, Germany
Share this story
Delete

Debunking the Myth: Accessibility and React

1 Share

Mark Steadman from Deque:

React can be an accessible application framework with the right knowledge and the right know-how. The stigma that it is not an accessible framework is simply not true. It has some of the best built-in accessibility functionality there is out there, and a large community of accessibility advocates that are creating content that is easily consumable in your application.

As Chris Coyier said it:

React didn’t use a <div> for a <button>, you did. React didn’t force extra markup all over the page when you decided to not use a Fragment. React didn’t forget to change the title of the page because that was something you neglected.

Yes! Yes! Yes! … A thousand times YES!

I see many (new) devs enter the JS game to just start throwing things around, totally unaware of (or even worse: neglecting) the foundation layers which JS enriches: HTML and CSS. It all starts with HTML and semantics — many unfortunately tend to forget this.

With a huge chance of sounding like a skipping record regarding this: go read Jeremy Keith’s post “Robustness and least power”. In said post he quotes Derek Featherstone who said:

In the web front-end stack — HTML, CSS, JS, and ARIA — if you can solve a problem with a simpler solution lower in the stack, you should. It’s less fragile, more foolproof, and just works.

Data attributes & progressive enhancement by Derek Featherstone

Embrace the platform, folks.

Debunking the Myth: Accessibility and React →

Read the whole story
emrox
2 days ago
reply
Hamburg, Germany
Share this story
Delete

Writing a Lightroom Classic plug-in

1 Share

As part of my migration to Lightroom Classic from Photos, I needed to recreate my folder and album structure. In Photos, folders container albums (or other folders) and albums contain photos. An album cannot contain a folder. This maps directly to Lightroom Classic which has "collection sets" for folders and "collections" for albums.

To transfer the folder and album information over to Lightroom Classic, I wrote an Apple Script for Photos that added a keyword of the format PhotosExport>folder1>folder2>albumPhotosExport>folder1>folder2>album to each photo. As > is the hierarchical separator in Lightroom Classic, this became a nested set of keywords that I then needed to turn into collections within nested collection sets.

This is done using a plug-in.

The SDK

To create a Lightroom Classic plug-in you need the SDK. Go to the Adobe I/O page for Lightroom Classic and click the "Start building today" button. Click enough times and you'll end up with a zip file on your hard drive. For me, this file is LR_9.0_201910171147-f2855d44.master_mac_SDK.zip.

Unzip and you get a number of folders. The most useful are the HTML files in API Reference and the Lightroom SDK Guide PDF in Manual. You'll refer to these resources a lot!

Creating a Plug-in

To create a plug-in you need a directory with an extension of .lrplugin. This is treated as a package by macOS, so you can also use .lrdevplugin so that it works as a standard folder in Finder.

We'll create hello.devplugin.

The manifest file

Every Lightroom Classic plug-in has a manifest file called Info.lua. This file returns a table with metadata about the plug-in along with menu definitions and information on which files to call when an event happens.

A minimal Info.lua looks like this:

hello.devplugin/Info.lua:

return {
  VERSION = { major=1, minor=0, revision=0, },

  LrSdkVersion = 9.0,
  LrSdkMinimumVersion = 4.0,

  LrToolkitIdentifier = "com.akrabat.hello",
  LrPluginName = "Hello",
  LrPluginInfoUrl="https://akrabat.com/writing-a-lightroom-classic-plug-in",
}

LrSdkVersion, LrToolkitIdentifier & LrPluginName are required and your plug-in will not work without them. See the "Writing standard plug-ins for Lightroom" section of Chapter 2 of the Lightroom SDK Guide for details on all the possible items you can put in Info.lua.

Installing our Plug-in

To install our plug-in, in Lightroom Classic, go to File -> Plug-in Manager…:

  • Click Add at the bottom of the left hand list.
  • Navigate to and select the hello.lrdevplugin directory and click Add Plug-in.
  • Click Enable in the Status section for the Hello plugin.

You'll see something like this:


Lr plugin manager

This plugin currently does nothing, of course!

Menu items

For the plug-in I wrote, I simply needed a way to run the code and the way to do this is to add a menu item to Lightroom Classic which will run a file in the plug-in.

To add a menu item, we edit Info.lua and add LrFileMenuItems and/or LrLibraryMenuItems tables to the returned table.

To add a menu item to Library->Plug-in Extras, we add this to the table returned by Info.lua:

LrLibraryMenuItems = {
    {
      title = "Hello",
      file = "hello.lua",
    },
  },

LrLibraryMenuItems is a table of tables where each inner table is a menu item consisting of the displayed text and the file to be executed on selection. This code will create a menu item called "Hello" which, when selected, will run the hello.lua file.


Lr plugin manager

Hello.lua file

Let's create a simple hello.lua to prove it works:

hello.devplugin/hello.lua:

local LrDialogs = import 'LrDialogs'
LrDialogs.message("Hello World")

To use any of the SDK functionality, we have to import it into our file. The list of available tables to import is in the API reference. LrDialogs provides various dialog boxes and message() is the simplest to use, so is a good choice for testing we're all set up.

Now you can reload the plug-in in the Plugin Manager and the menu item should appear and upon clicking it, you'll see this dialog:


Lr dialog hello

We have a working plug-in!

If you get an error that hello.lua cannot be found, try restarting Lightroom Classic.

Interacting with the catalog

To interact with the catalog, you must place your code in an async function started by startAsyncTask(). Fortunately, Lua supports closures which makes it easy as you can see in thsi code that gets the filename of the selected photo:

hello.devplugin/hello.lua:

local LrApplication = import 'LrApplication'
local LrDialogs = import 'LrDialogs'
local LrTasks = import 'LrTasks'

LrTasks.startAsyncTask(function ()
  local catalog = LrApplication.activeCatalog()

  local photo = catalog:getTargetPhoto()
  if photo == nil then
    LrDialogs.message("Hello World", "Please select a photo")
    return
  end

  local filename = photo:getFormattedMetadata("fileName")
  local msg = string.format("The selected photo's filename is %q", filename)
  LrDialogs.message("Hello World", msg)
end)

Run the plug-in by selecting the Library -> Plug-in Extras -> Hello menu item and you should see a dialog like this:


Lr selected filename

Writing to the catalog

If you want to write to the catalog to say create a keyword, collection, or collection set, etc. then you must do this within a withWriteAccessDo() function like this:

hello.devplugin/hello.lua:

local LrApplication = import 'LrApplication'
local LrDialogs = import 'LrDialogs'
local LrTasks = import 'LrTasks'

LrTasks.startAsyncTask(function ()
  local catalog = LrApplication.activeCatalog()
  local collection

  catalog:withWriteAccessDo("Create _Hello collection", function()
    collection = catalog:createCollection("_Hello", nil, true)
  end)

  catalog:withWriteAccessDo("Add selected photos to the _Hello collection", function()
    local photos = catalog:getTargetPhotos();
    collection:addPhotos(photos)
    local msg = string.format("Added selected photos to %q", collection:getName())
    LrDialogs.message("Hello World", msg)
  end)
end)

Note that most of the time you have to start a separate withWriteAccessDo() block if you want to operate on the thing you've created, though there are some exceptions. The API Reference has this note about it:

As of version 3.0, changes you make to the database within this call do not have immediate effect, but are written to the database upon successful completion of the callback function. This means, in general, that if you create a new item (for instance, by calling catalog:createCollection()), you cannot retrieve information about that item until the with___AccessDo has finished. There are some special cases where you can reference the newly-created item; for instance, after creating a collection set, you can create a collection within that set. These cases are noted in the API documentation.

In this example, I create a top level collection called "_Hello" in the first withWriteAccessDo() block and then in the second, I add all the selected photos to it. Be aware of your local scope when doing this sort of thing though.

Logging

I've found that logging is vital for developing a plug-in. The LrLogger table allows for writing messages to the operating system's console or to file. The simplest way to set this up is to add this near the top of your file:

local LrLogger = import 'LrLogger'
local logger = LrLogger('HelloWorldPlugin')
logger:enable("print")
local log = logger:quickf('info')

When you call enable() can you pass in "print" to output the console or "logfile" to write to disk. The log file is named after your loggre's name and written to the ~/Documents/lrClassicLogs/ directory (or equivalent on Windows). In this case, the full filename is ~/Documents/lrClassicLogs/HelloWorldPlugin.log. Further details are in the "Debugging your plug-in" section of the Lightroom SDK Guide.

You can now write log messages using the string.format style:

log("Created collection %q", collection.getName())

In conclusion

I've found that it is relatively easy to get started writing a plug-in for Lightroom Classic and the docs are quite good. The Internet is a good resource for further information though you need to craft your queries carefully to get answers about the SDK specifically rather than about how to use the Lightroom Classic GUI. Also, note that Lightroom Classic used to be called just plain Lightroom, so most SDK resources reference the old name.

I've published one Lightroom Classic plug-in so far. My collection-creator plug-in creates collections within nested collection sets from a keyword hierarchy and was useful for migrating from Apple Photos. It's on GitHub, so you have a look at how I approached it. There are also many other Lightroom plug-ins on GitHub which are useful to look at.

Have a play – this is a good way to automate work within Lightroom Classic!

Read the whole story
emrox
3 days ago
reply
Hamburg, Germany
Share this story
Delete

On let vs const

3 Shares

My previous post included this paragraph:

let vs const vs var: Usually you want let. If you want to forbid assignment to this variable, you can use const. (Some codebases and coworkers are pedantic and force you to use const when there is only one assignment.)

This turned out to be very controversial, sparking conversations on Twitter and Reddit. It seems that the majority view (or at least, the most vocally expressed view) is that one should use const wherever possible, only falling back to let where necessary, as can be enforced with the prefer-const ESLint rule.

In this post, I will briefly summarize some of the arguments and counter-arguments I’ve encountered, as well as my personal conclusion on this topic.

Why prefer-const

  • One Way to Do It: It is mental overhead to have to choose between let and const every time. A rule like “always use const where it works” lets you stop thinking about it and can be enforced by a linter.
  • Reassignments May Cause Bugs: In a longer function, it can be easy to miss when a variable is reassigned. This may cause bugs. Particularly in closures, const gives you confidence you’ll always “see” the same value.
  • Learning About Mutation: Folks new to JavaScript often get confused thinking const implies immutability. However, one could argue that it’s important to learn the difference between variable mutation and assignment anyway, and preferring const forces you to confront this distinction early on.
  • Meaningless Assignments: Sometimes, an assignment doesn’t make sense at all. For example, with React Hooks, the values you get from a Hook like useState are more like parameters. They flow in one direction. Seeing an error on their assignment helps you learn earlier about the React data flow.
  • Performance Benefits: There are occasional claims that JavaScript engines could make code using const run faster due to the knowledge the variable won’t be reassigned.

Why Not prefer-const

  • Loss of Intent: If we force const everywhere it can work, we lose the ability to communicate whether it was important for something to not be reassigned.
  • Confusion with Immutability: In every discussion about why you should prefer const, someone always confuses with immutability. This is unsurprising, as both assignment and mutation use the same = operator. In response, people are usually told that they should “just learn the language”. However, the counter-argument is that if a feature that prevents mostly beginner mistakes is confusing to beginners, it isn’t very helpful. And unfortunately, it doesn’t help prevent mutation mistakes which span across modules and affect everyone.
  • Pressure to Avoid Redeclaring: A const-first codebase creates a pressure to not use let for conditionally assigned variables. For example, you might write const a = cond ? b : c instead of an if condition, even if both b and c branches are convoluted and giving them explicit names is awkward.
  • Reassignments May Not Cause Bugs: There are three common cases when reassignments cause bugs: when the scope is very large (such as module scope or huge functions), when the value is a parameter (so it’s unexpected that it would be equal to something other than what was passed), and when a variable is used in a nested function. However, in many codebases most variables won’t satisfy either of those cases, and parameters can’t be marked as constant at all.
  • No Performance Benefits: It is my understanding that the engines are already aware of which variables are only assigned once — even if you use var or let. If we insist on speculating, we could just as well speculate that extra checks can create performance cost rather than reduce it. But really, engines are smart.

My Conclusion

I don’t care.

I would use whatever convention already exists in the codebase.

If you care, use a linter that automates checking and fixing this so that changing let to const doesn’t become a delay in code review.

Finally, remember that linters exist to serve you. If a linter rule annoys you and your team, delete it. It may not be worth it. Learn from your own mistakes.

(This is an article posted to my blog at overreacted.io. You can read it online by clicking here.)
Read the whole story
emrox
4 days ago
reply
Hamburg, Germany
alvinashcraft
27 days ago
reply
West Grove, PA
Share this story
Delete
Next Page of Stories