Hello.

I am Paul Kinlan.

A Developer Advocate for Chrome and the Open Web at Google.

Creating a commit with multiple files to Github with JS on the web

Paul Kinlan

Мой сайт entirely static . Он построен на Hugo и размещен на Zeit . Я очень доволен настройкой, у меня почти мгновенные сборки и сверхбыстрая доставка контента CDN, и я могу делать все, что мне нужно, потому что мне не нужно управлять каким-либо состоянием. Я создал simple UI для этого сайта, а также мой podcast creator который позволяет мне быстро публиковать новый контент на моем статически размещенном сайте. Так. Как я это сделал?

Read More

Paul Kinlan

Trying to make the web and developers better.

RSS Github Medium

Screen Recorder: recording microphone and the desktop audio at the same time

Paul Kinlan

У меня есть цель создать самое простое в мире программное обеспечение для записи экрана, и я в течение последних нескольких месяцев медленно работала над проектом (я имею в виду очень медленно).

В предыдущих постах я получил screen recording and a voice overlay , суетясь с потоками из всех входных источников. Однако одной из проблем было то, что я не мог понять, как получить звук с рабочего стола * и * наложить звук из динамика. Я наконец-то понял, как это сделать.

Во-первых, getDisplayMedia в Chrome теперь позволяет захватывать звук, в Spec есть странный недосмотр, поскольку он не позволяет вам указывать audio: true в вызове функции, теперь вы можете это сделать.

const audio = audioToggle.checked || false;
desktopStream = await navigator.mediaDevices.getDisplayMedia({ video:true, audio: audio });

Во-вторых, я изначально думал, что, создав две дорожки в аудиопотоке, я смогу получить то, что хотел, однако я узнал, что API-интерфейс MediaRecorder Chrome может выводить только одну дорожку, а во- MediaRecorder , это не сработало бы, потому что дорожки похожи на аудиофонограммы DVD, которые могут воспроизводить только одна.

Решение, вероятно, простое для многих людей, но для меня оно было новым: используйте Web Audio.

Оказывается, в API WebAudio есть createMediaStreamSource и createMediaStreamDestination , оба из которых являются API, необходимыми для решения проблемы. createMediaStreamSource может брать потоки с моего настольного аудио и микрофона, и, соединяя их вместе в объект, созданный createMediaStreamDestination он дает мне возможность MediaRecorder этот поток в API MediaRecorder .

const mergeAudioStreams = (desktopStream, voiceStream) => {
  const context = new AudioContext();
    
  // Create a couple of sources
  const source1 = context.createMediaStreamSource(desktopStream);
  const source2 = context.createMediaStreamSource(voiceStream);
  const destination = context.createMediaStreamDestination();
  
  const desktopGain = context.createGain();
  const voiceGain = context.createGain();
    
  desktopGain.gain.value = 0.7;
  voiceGain.gain.value = 0.7;
   
  source1.connect(desktopGain).connect(destination);
  // Connect source2
  source2.connect(voiceGain).connect(destination);
    
  return destination.stream.getAudioTracks();
};

Simples.

Полный код можно найти на my glitch , а демоверсию можно найти здесь: https://screen-record-voice.glitch.me/

{{<fast-youtube oGIdqcMFKlA>}}

Extracting text from an image: Experiments with Shape Detection

Paul Kinlan

У меня было немного простоя после Google IO, и я хотел покончить с долговременным зудом, который у меня был. Я просто хочу иметь возможность копировать текст, который хранится внутри изображений в браузере. Это все. Я думаю, что это будет отличная особенность для всех.

Нелегко добавить функциональность непосредственно в Chrome, но я знаю, что могу использовать систему намерений на Android, и теперь я могу сделать это с помощью Интернета (или, по крайней мере, Chrome на Android).

Два новых дополнения к веб-платформе - Share Target Level 2 (или, как мне нравится называть это File Share) и TextDetector в API обнаружения формы - have allowed me to build a utility that I can Share images to and get the text held inside them .

Базовая реализация относительно проста: вы создаете Share Share Target и обработчик в Service Worker, а затем, когда у вас есть образ, которым поделился пользователь, вы запускаете TextDetector на нем.

Share Target API позволяет вашему веб-приложению быть частью собственной подсистемы совместного использования, и в этом случае вы теперь можете зарегистрироваться для обработки всех типов image/* , объявив его внутри Web App Manifest следующим образом.

"share_target": {
  "action": "/index.html",
  "method": "POST",
  "enctype": "multipart/form-data",
  "params": {
    "files": [
      {
        "name": "file",
        "accept": ["image/*"]
      }
    ]
  }
}

Когда ваш PWA установлен, вы увидите его во всех местах, где вы обмениваетесь изображениями следующим образом:

API Share Target рассматривает совместное использование файлов как форму сообщения. Когда файл fetch в веб-приложение, сервисный работник активируется, обработчик fetch вызывается с данными файла. Данные теперь находятся внутри Service Worker, но они мне нужны в текущем окне, чтобы я мог их обработать, служба знает, какое окно вызвало запрос, чтобы вы могли легко ориентироваться на клиента и отправлять ему данные.

self.addEventListener('fetch', event => {
  if (event.request.method === 'POST') {
    event.respondWith(Response.redirect('/index.html'));
    event.waitUntil(async function () {
      const data = await event.request.formData();
      const client = await self.clients.get(event.resultingClientId || event.clientId);
      const file = data.get('file');
      client.postMessage({ file, action: 'load-image' });
    }());
    
    return;
  }
  ...
  ...
}

Когда изображение находится в пользовательском интерфейсе, я обрабатываю его с помощью API обнаружения текста.

navigator.serviceWorker.onmessage = (event) => {  
  const file = event.data.file;
  const imgEl = document.getElementById('img');
  const outputEl = document.getElementById('output');
  const objUrl = URL.createObjectURL(file);
  imgEl.src = objUrl;
  imgEl.onload = () => {
    const texts = await textDetector.detect(imgEl);
    texts.forEach(text => {
      const textEl = document.createElement('p');
      textEl.textContent = text.rawValue;
      outputEl.appendChild(textEl);
    });
  };
  ...
};

Самая большая проблема заключается в том, что браузер не поворачивает изображение естественным образом (как вы можете видеть ниже), а API обнаружения формы необходимо, чтобы текст был в правильной ориентации чтения.

Я использовал довольно простой в использовании EXIF-Js library чтобы обнаружить вращение, а затем выполнить некоторые базовые манипуляции с холстом, чтобы переориентировать изображение.

EXIF.getData(imgEl, async function() {
  // http://sylvana.net/jpegcrop/exif_orientation.html
  const orientation = EXIF.getTag(this, 'Orientation');
  const [width, height] = (orientation > 4) 
                  ? [ imgEl.naturalWidth, imgEl.naturalHeight ]
                  : [ imgEl.naturalHeight, imgEl.naturalWidth ];

  canvas.width = width;
  canvas.height = height;
  const context = canvas.getContext('2d');
  // We have to get the correct orientation for the image
  // See also https://stackoverflow.com/questions/20600800/js-client-side-exif-orientation-rotate-and-mirror-jpeg-images
  switch(orientation) {
    case 2: context.transform(-1, 0, 0, 1, width, 0); break;
    case 3: context.transform(-1, 0, 0, -1, width, height); break;
    case 4: context.transform(1, 0, 0, -1, 0, height); break;
    case 5: context.transform(0, 1, 1, 0, 0, 0); break;
    case 6: context.transform(0, 1, -1, 0, height, 0); break;
    case 7: context.transform(0, -1, -1, 0, height, width); break;
    case 8: context.transform(0, -1, 1, 0, 0, width); break;
  }
  context.drawImage(imgEl, 0, 0);
}

И вуаля, если вы поделитесь изображением с приложением, оно будет вращать изображение, а затем анализировать его, возвращая вывод найденного текста.

Создать этот маленький эксперимент было невероятно весело, и он сразу пригодился мне. Это, однако, выделить inconsistency of the web platform . Эти API доступны не во всех браузерах, они недоступны даже во всех версиях Chrome - это означает, что, когда я пишу эту статью для Chrome OS, я не могу использовать приложение, но в то же время могу использовать его … О боже, так круто.

Wood Carving found in Engakuji Shrine near Kamakura

Sakura

Paul Kinlan

Мне сказали, что более конкретно, что это «Yaezakura»

Read More

Debugging Web Pages on the Nokia 8110 with KaiOS using Chrome OS

Paul Kinlan

Этот пост является продолжением поста об отладке KaiOS device with Web IDE , но вместо macOS теперь можно использовать Chrome OS (m75) с Crostini. Я пишу с KaiOS Environment Setup который является хорошим началом, но этого недостаточно для начала работы с Chrome OS и Crostini. Ниже приведено грубое руководство, которому я следовал. Убедитесь, что вы используете хотя бы Chrome OS m75 (в настоящее время канал разработки по состоянию на 15 апреля), а затем:

Read More

New WebKit Features in Safari 12.1 | WebKit

Paul Kinlan

Большие обновления для последней версии Safari!

Я думал, что это было довольно громкое объявление, и противоположность Google, который некоторое время назад сказал, что Google Pay Lib - это рекомендуемый способ осуществления платежей … Я имею в виду, что это не за миллион миль, Google Pay построен на вершине запроса на оплату, но это не PR в первую очередь.

Payment Request is now the recommended way to pay implement Apple Pay on the web.

Read full post .

И моя любимая особенность, учитывая мою историю с Web Intents.

Web Share API

The Web Share API adds navigator.share(), a promise-based API developers can use to invoke a native sharing dialog provided the host operating system. This allows users to share text, links, and other content to an arbitrary destination of their choice, such as apps or contacts.

Теперь просто получить API Share Target, и мы находимся на пути к победителю! :)

Offline fallback page with service worker

Paul Kinlan

Несколько лет назад я провел некоторое исследование того, как нативные приложения реагируют на отсутствие сетевого подключения. Хотя я потерял ссылку на анализ (могу поклясться, что это было в Google+), всеобъемлющим повествованием было то, что многие нативные приложения неразрывно связаны с Интернетом, и они просто отказываются работать. Похоже, что многие веб-приложения отличаются от веба тем, что этот опыт все еще остается «фирменным», Барт Симпсон скажет вам, что вам нужно быть онлайн (например), и все же для В подавляющем большинстве случаев вы получаете «Dino» (см. chrome: // dino).

Мы работаем над Service Worker в течение долгого времени, и, хотя мы видим, что все больше и больше сайтов имеют страницы, контролируемые Service Worker, подавляющее большинство сайтов даже не имеют базового запасного варианта, когда сеть не работает. имеется в наличии.

Я спросил моего хорошего друга Джейка, есть ли у нас какие-либо рекомендации о том, как создать общую резервную страницу, исходя из предположения, что вы не хотите создавать полностью автономный первый опыт, и в течение 10 минут он его создал. Check it out .

Для краткости, я вставил код ниже, потому что он всего около 20 строк. Он кэширует автономные ресурсы, а затем для каждой выборки, которая является «навигационной» выборкой, он увидит ошибки (из-за сети) и затем отобразит автономную страницу вместо исходного содержимого.

addEventListener('install', (event) => {
  event.waitUntil(async function() {
    const cache = await caches.open('static-v1');
    await cache.addAll(['offline.html', 'styles.css']);
  }());
});

// See https://developers.google.com/web/updates/2017/02/navigation-preload#activating_navigation_preload
addEventListener('activate', event => {
  event.waitUntil(async function() {
    // Feature-detect
    if (self.registration.navigationPreload) {
      // Enable navigation preloads!
      await self.registration.navigationPreload.enable();
    }
  }());
});

addEventListener('fetch', (event) => {
  const { request } = event;

  // Always bypass for range requests, due to browser bugs
  if (request.headers.has('range')) return;
  event.respondWith(async function() {
    // Try to get from the cache:
    const cachedResponse = await caches.match(request);
    if (cachedResponse) return cachedResponse;

    try {
      // See https://developers.google.com/web/updates/2017/02/navigation-preload#using_the_preloaded_response
      const response = await event.preloadResponse;
      if (response) return response;

      // Otherwise, get from the network
      return await fetch(request);
    } catch (err) {
      // If this was a navigation, show the offline page:
      if (request.mode === 'navigate') {
        return caches.match('offline.html');
      }

      // Otherwise throw
      throw err;
    }
  }());
});

Это все. Когда пользователь в сети, он увидит опыт по умолчанию.

А когда пользователь не в сети, он получит запасную страницу.

Я считаю этот простой сценарий невероятно мощным, и да, хотя его еще можно улучшить, я верю, что даже простое изменение в том, как мы говорим с нашими пользователями, когда есть проблема с сетью, способно существенно улучшить восприятие Интернета для пользователей по всему миру.

** Обновление ** Jeffrey Posnick kinldy напомнил мне об использовании предварительной загрузки Navigation для того, чтобы не нужно было ждать загрузки SW для всех запросов, это особенно важно, если вы управляете только failed сетевыми запросами.

testing block image upload

Paul Kinlan

Это всего лишь тест, чтобы увидеть, правильно ли я загрузил изображение. Если вы видите это, то да, я сделал :)

Read More

Editor.js

Paul Kinlan

Я обновил редактор на основе Hugo, чтобы попытаться использовать EditorJS в качестве редактора для блога.

Workspace in classic editors is made of a single contenteditable element, used to create different HTML markups. Editor.js workspace consists of separate Blocks: paragraphs, headings, images, lists, quotes, etc. Each of them is an independent contenteditable element (or more complex structure) provided by Plugin and united by Editor’s Core.

Read full post .

Я думаю, что это работает.

Я немного боролся с кодовой базой, во всех примерах используется ES-модуль, однако NPM-дистрибутив выводится в коде IIFE ES5. Но как только я преодолел это препятствие, стало довольно легко создать пользовательский интерфейс, который больше похож на средний.

Debugging Web Pages on the Nokia 8110 with KaiOS

Paul Kinlan

В последнее время мы много работали над функциональными телефонами, и это было сложно, но весело. Самое сложное в том, что на KaiOS мы обнаружили, что невозможно отлаживать веб-страницы, особенно на имеющемся у нас оборудовании (Nokia 8110). Nokia - отличное устройство, оно построено на KaiOS, которое, как мы знаем, основано на чем-то похожем на Firefox 48, но оно заблокировано, нет традиционного режима разработчика, как на других устройствах Android, что означает, что вы не можете подключить Firefox.

Read More

Object Detection and Augmentation

Paul Kinlan

Я много играл с Shape Detection API в Chrome, и мне очень нравится его потенциал, например, очень простой QRCode detector я написал давным-давно, имеет полизаполнение JS, но использует new BarcodeDetector() API, если он доступен.

Вы можете увидеть некоторые другие демонстрации, которые я построил здесь, используя другие возможности API обнаружения формы: Face Detection , Barcode Detection и Text Detection .

Я был приятно удивлен, когда наткнулся на Jeeliz на выходных и был невероятно впечатлен производительностью их инструментария - разумеется, я использовал Pixel3 XL, но обнаружение лиц казалось значительно быстрее, чем это возможно с API FaceDetector .

Checkout some of their demos .

Это заставило меня много думать. Этот инструментарий для обнаружения объектов (и подобных ему) использует API-интерфейсы, которые широко доступны в Интернете, в частности доступ к камере, WebGL и WASM, что в отличие от Chrome Shape Detection API (которое имеется только в Chrome и не совместимо на всех платформах, на которых работает Chrome). ) можно легко использовать для создания богатого опыта и охвата миллиардов пользователей с единообразным опытом на всех платформах.

Расширение - это то, где это становится интересным (и действительно то, что я хотел показать в этом посте), и где вам нужны библиотеки промежуточного программного обеспечения, которые сейчас выходят на платформу, мы можем создавать забавные приложения для настройки лица в стиле Snapchat, не заставляя пользователей устанавливать MASSIVE приложения которые собирают огромное количество данных с устройства пользователя (потому что нет доступа к системе).

Помимо забавных демонстраций, можно быстро и просто решить очень сложные варианты использования, такие как:

  • Выбор текста прямо с камеры или фото от пользователя
  • Живой перевод языков с камеры
  • Встроенное обнаружение QRCode, чтобы людям не приходилось постоянно открывать WeChat :)
  • Автоматическое извлечение URL сайта или адреса из изображения
  • Обнаружение кредитной карты и извлечение номера (чтобы пользователи быстрее регистрировались на вашем сайте)
  • Визуальный поиск товаров в веб-приложении вашего магазина.
  • Поиск штрих-кода для получения дополнительной информации о продукте в веб-приложении вашего магазина.
  • Быстрая обрезка фотографий профиля на лицах людей.
  • Простые функции A11Y, позволяющие пользователю услышать текст, найденный на изображениях.

Я просто потратил 5 минут на обдумывание этих вариантов использования - я знаю, что их гораздо больше - но меня поразило, что мы не видим много сайтов или веб-приложений, использующих камеру, вместо этого мы видим, что многие сайты спрашивают их пользователи загружают приложение, и я не думаю, что нам нужно больше это делать.

** Обновление ** Томас Штайнер в нашей команде упомянул в ShapeDetection нашей команды, что, похоже, мне не нравится текущий API ShapeDetection . Мне нравится тот факт, что этот API дает нам доступ к собственным реализациям доставки каждой из соответствующих систем, однако, как я писал в The Lumpy Web , веб-разработчики жаждут согласованности в платформе, и с API Shape Detection существует ряд проблем, которые могут быть обобщено как:

  1. API есть только в Chrome
  2. API в Chrome сильно отличается на всех платформах, потому что их базовые реализации различны. Android имеет только точки для таких ориентиров, как рот и глаза, где у macOS есть контуры. В Android TextDetector возвращает обнаруженный текст, в то время как в macOS он возвращает индикатор «Text Presence» … Это не говоря уже о всех ошибках, обнаруженных Surma.

Сеть как платформа для распространения имеет такой смысл для такого опыта, что я думаю, что было бы упущением с нашей стороны не делать этого, но две вышеупомянутые группы вопросов заставляют меня усомниться в долгосрочной необходимости реализации каждой функции на веб-платформа изначально, когда мы могли бы реализовать хорошие решения в пакете, который поставляется с использованием функций платформы сегодня, таких как WebGL, WASM и в будущем Web GPU.

В любом случае, мне нравится тот факт, что мы можем сделать это в Интернете, и я с нетерпением жду возможности увидеть, что сайты будут с ними.

Got web performance problems? Just wait...

Paul Kinlan

Я видел твит от хорошего Mariko и коллеги, Mariko , о тестировании на линейке бюджетных устройств, которые держат вас в курсе.

Контекст твита заключается в том, что мы смотрим на то, на что похожа веб-разработка при создании для пользователей, которые ежедневно живут на этих классах устройств.

Сейчас команда проделывает большую работу в этом пространстве, но я потратил целый день на то, чтобы создать сайт, и было невероятно сложно заставить что-либо работать даже при слегка приемлемом уровне производительности - вот некоторые из проблем, с которыми я столкнулся:

  • Странности видового экрана и таинственное повторное введение 300-миллисекундной задержки нажатия (можно обойти).
  • Огромные перекраски всего экрана, и это медленно.
  • Сеть медленная
  • Память ограничена, и последующие GC блокируют основной поток на несколько секунд
  • Невероятно медленное выполнение JS
  • DOM манипуляция медленная

Для многих страниц, которые я создавал, даже на страницах с быстрым Wi-Fi-соединением для загрузки требовалось несколько секунд, а последующие взаимодействия были просто медленными. Это было тяжело, требовалось попытаться получить как можно больше от основного потока, но на техническом уровне было также невероятно приятно видеть изменения в алгоритмах и логике, которые я бы не сделал для всей своей традиционной веб-разработки. большие улучшения в производительности.

Я не уверен, что делать в долгосрочной перспективе, я подозреваю, что у огромного числа разработчиков, с которыми мы работаем на более развитых рынках, будет реакция «Я не создаю сайты для пользователей в [вставить страну x]», и на на высоком уровне трудно поспорить с этим утверждением, но я не могу игнорировать тот факт, что десятки миллионов новых пользователей приходят на компьютер каждый год, и они будут использовать эти устройства, и мы хотим, чтобы Интернет был * * платформой Выбор контента и приложений, чтобы мы не были довольны rise of the meta platform .

Нам нужно продолжать повышать производительность в течение долгого времени. Мы будем продолжать создавать инструменты и руководства, чтобы помочь разработчикам быстро загружаться и иметь плавные пользовательские интерфейсы :)

Browser Bug Searcher

Paul Kinlan

Я только размышлял над некоторыми из work our team has done и нашел проект 2017 года, который создали Роберт Найман и Эрик Бидельман. Browser Bug Searcher! .

Невероятно, что с помощью всего нескольких нажатий клавиш у вас есть отличный обзор ваших любимых функций во всех основных браузерных движках.

Source code available .

Это фактически выдвигает на первый план одну из проблем, с которыми я сталкиваюсь с трекерами ошибок crbug и webkit: у них нет простого способа получать потоки данных в таких форматах, как RSS. Я хотел бы иметь возможность использовать мой агрегатор topicdeck с категориями ошибок и т. Д., Поэтому у меня есть панель со всеми интересующими меня вещами, основанная на последней информации от каждого из трекеров ошибок.

Github's Web Components

Paul Kinlan

Я искал быстрый редактор https://www.webcomponents.org/ на https://www.webcomponents.org/ чтобы я мог упростить публикацию в этом блоге, и я наткнулся на аккуратный набор компонентов github .

Я знал, что у них были <time-element> но я не знал, что у них был такой хороший и простой набор полезных элементов.

London from Kingscross

Paul Kinlan

Выглядит неплохо сегодня.

Read More

The GDPR mess

Paul Kinlan

То, как мы (как отрасль) реализуем согласие GDPR, - беспорядок. Я не уверен, почему кто-то выбрал бы что-либо, кроме «Использовать только необходимые файлы cookie», однако я действительно не могу определить разницу между любым из вариантов и компромиссом между обоими вариантами, не говоря уже о том, что я могу убедиться, что это только используя только необходимые куки.

Read More

Brexit: History will judge us all

Paul Kinlan

История рассудит нас всех по этому беспорядку, и я надеюсь, что это будет тематическое исследование для всех о влиянии национализма, личных интересов, колониального высокомерия, знаменитостей и шуток.

Fuckers.

File Web Share Target

Paul Kinlan

Я часто говорил, что для того, чтобы веб-приложения эффективно конкурировали в мире приложений, они должны быть интегрированы во все места, где пользователи ожидают, что приложения будут. Обмен данными между приложениями является одной из основных недостающих частей веб-платформы, и, в частности, одной из последних основных отсутствующих функций является общий data out of their silo на уровне: веб-приложения должны иметь возможность получать data out of their silo и data out of their silo в другие веб-сайты и приложения; они также должны иметь возможность получать данные из других собственных приложений и сайтов.

Read More

Testing-file-share-target-from-camera

Paul Kinlan

Это тестирование обмена непосредственно из приложения камеры. Похоже, это сработало :)

Read More