mirror of
https://github.com/google/pebble.git
synced 2025-05-30 23:13:11 +00:00
Import the pebble dev site into devsite/
This commit is contained in:
parent
3b92768480
commit
527858cf4c
1359 changed files with 265431 additions and 0 deletions
53
devsite/plugins/blog_authors_generator.rb
Normal file
53
devsite/plugins/blog_authors_generator.rb
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Generates a list page for each blog author.
|
||||
# The list of authors is in /source/_data/blog_authors.yml
|
||||
|
||||
module Jekyll
|
||||
|
||||
class AuthorPage < Page
|
||||
|
||||
def initialize(site, base, dir, author)
|
||||
@site = site
|
||||
@base = base
|
||||
@dir = dir
|
||||
@name = author[0] + '/index.html'
|
||||
|
||||
self.process(@name)
|
||||
self.read_yaml(File.join(base, '_layouts'), 'blog/author_page.html')
|
||||
self.data['posts'] = site.posts.docs.select { |post| post['author'] == author[0] }
|
||||
self.data['author_name'] = author[1]['name']
|
||||
self.data['author'] = author[0]
|
||||
self.data['title'] = "Pebble Developer Blog: #{author[1]['name']}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class AuthorPageGenerator < Generator
|
||||
|
||||
def generate(site)
|
||||
if ! site.layouts.key? 'blog/author_page'
|
||||
throw 'Layout for the blog author pages is missing.'
|
||||
end
|
||||
dir = site.config['tag_dir'] || 'blog/authors'
|
||||
site.data['authors'].each do |author|
|
||||
author[1]['num_posts'] = site.posts.docs.select { |post| post['author'] == author[0] }.length
|
||||
site.pages << AuthorPage.new(site, site.source, dir, author)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
69
devsite/plugins/blog_tags_generator.rb
Normal file
69
devsite/plugins/blog_tags_generator.rb
Normal file
|
@ -0,0 +1,69 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Generates a list page for each blog tag.
|
||||
|
||||
require 'slugize'
|
||||
|
||||
module Jekyll
|
||||
|
||||
class TagPage < Page
|
||||
|
||||
def initialize(site, base, dir, tag)
|
||||
@site = site
|
||||
@base = base
|
||||
@dir = dir
|
||||
@name = tag[0].slugize + '/index.html'
|
||||
|
||||
self.process(@name)
|
||||
self.read_yaml(File.join(base, '_layouts'), 'blog/tag_page.html')
|
||||
self.data['posts'] = tag[1].sort_by(&:date).reverse
|
||||
self.data['name'] = tag[0]
|
||||
self.data['tag'] = tag[0]
|
||||
self.data['title'] = "Pebble Developer Blog: #{tag[0]}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class TagPageRedirect < Page
|
||||
|
||||
def initialize(site, base, dir, tag)
|
||||
@site = site
|
||||
@base = base
|
||||
@dir = dir
|
||||
@name = tag[0].slugize + '.html'
|
||||
|
||||
self.process(@name)
|
||||
self.read_yaml(File.join(base, '_layouts'), 'utils/redirect_permanent.html')
|
||||
self.data['path'] = '/' + File.join(dir, tag[0].slugize) + '/'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class TagPageGenerator < Generator
|
||||
|
||||
def generate(site)
|
||||
if ! site.layouts.key? 'blog/tag_page'
|
||||
throw 'Layout for the blog tag pages is missing.'
|
||||
end
|
||||
dir = site.config['tag_dir'] || 'blog/tags'
|
||||
site.tags.each do |tag|
|
||||
site.pages << TagPage.new(site, site.source, dir, tag)
|
||||
site.pages << TagPageRedirect.new(site, site.source, dir, tag)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
50
devsite/plugins/environment_generator.rb
Normal file
50
devsite/plugins/environment_generator.rb
Normal file
|
@ -0,0 +1,50 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
module Jekyll
|
||||
|
||||
class EnvironmentGenerator < Generator
|
||||
|
||||
priority :highest
|
||||
|
||||
def initialize(site)
|
||||
# TODO: Figure out how to check for the environment type.
|
||||
require 'dotenv'
|
||||
Dotenv.load
|
||||
end
|
||||
|
||||
def generate(site)
|
||||
if !ENV.has_key?('URL') && ENV.has_key?('HEROKU_APP_NAME')
|
||||
ENV['URL'] = "https://#{ENV['HEROKU_APP_NAME']}.herokuapp.com"
|
||||
ENV['HTTPS_URL'] = "https://#{ENV['HEROKU_APP_NAME']}.herokuapp.com"
|
||||
end
|
||||
site.data['env'].each do |item|
|
||||
if ENV.has_key?(item['env'])
|
||||
set_config(site.config, item['config'], ENV[item['env']])
|
||||
elsif item.has_key?('default')
|
||||
set_config(site.config, item['config'], item['default'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# TODO: Rewrite this function to allow for nested keys.
|
||||
def set_config(config, key, value)
|
||||
config[key] = value
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
23
devsite/plugins/filter_assetify.rb
Normal file
23
devsite/plugins/filter_assetify.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# FilterAssetify is a Liquid filter to prepend the asset_path when needed
|
||||
module FilterAssetify
|
||||
def assetify(input)
|
||||
asset_path = @context.registers[:site].config['asset_path']
|
||||
%r{^/[^/]}.match(input) ? (asset_path + input) : input
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_filter(FilterAssetify)
|
21
devsite/plugins/filter_basename.rb
Normal file
21
devsite/plugins/filter_basename.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
module FilterBasename
|
||||
def basename(input, suffix)
|
||||
File.basename(input, suffix)
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_filter(FilterBasename)
|
28
devsite/plugins/filter_fake_platform.rb
Normal file
28
devsite/plugins/filter_fake_platform.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
module FilterFakePlatform
|
||||
def fake_platform(input)
|
||||
case input
|
||||
when 'aplite'
|
||||
'SDK 3'
|
||||
when 'basalt'
|
||||
'SDK 4'
|
||||
else
|
||||
'??'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_filter(FilterFakePlatform)
|
29
devsite/plugins/filter_hash_sort.rb
Normal file
29
devsite/plugins/filter_hash_sort.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
module FilterHashSort
|
||||
def hash_sort(hash, property=nil)
|
||||
return [] if hash.nil?
|
||||
sorted_hash = []
|
||||
hash.each { |key, value| sorted_hash << [key, value] }
|
||||
if property.nil?
|
||||
sorted_hash.sort! { |a, b| a[0] <=> b[0] }
|
||||
else
|
||||
sorted_hash.sort! { |a, b| a[1][property] <=> b[1][property] }
|
||||
end
|
||||
sorted_hash
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_filter(FilterHashSort)
|
28
devsite/plugins/filter_pluralize.rb
Normal file
28
devsite/plugins/filter_pluralize.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Liquid filter that does magic to make plurals easy.
|
||||
module Pluralize
|
||||
def pluralize(number, singular, plural = nil)
|
||||
if number == 1
|
||||
"#{number} #{singular}"
|
||||
elsif plural.nil?
|
||||
"#{number} #{singular}s"
|
||||
else
|
||||
"#{number} #{plural}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_filter(Pluralize)
|
25
devsite/plugins/filter_slugize.rb
Normal file
25
devsite/plugins/filter_slugize.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'slugize'
|
||||
|
||||
# Liquid filter that turns a string into a slug.
|
||||
# Used to turn tag names into the tag URL part.
|
||||
module FilterSlugize
|
||||
def slugize(input)
|
||||
input.slugize
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_filter(FilterSlugize)
|
236
devsite/plugins/generator_algolia.rb
Normal file
236
devsite/plugins/generator_algolia.rb
Normal file
|
@ -0,0 +1,236 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'htmlentities'
|
||||
require 'algoliasearch'
|
||||
require 'slugize'
|
||||
require 'dotenv'
|
||||
require 'securerandom'
|
||||
|
||||
module Jekyll
|
||||
class GeneratorAlgolia < Generator
|
||||
# Do this last so everything else has been processed already.
|
||||
priority :lowest
|
||||
|
||||
def initialize(_config)
|
||||
Dotenv.load
|
||||
end
|
||||
|
||||
def generate(site)
|
||||
@site = site
|
||||
return unless check_config?
|
||||
@prefix = site.config['algolia_prefix'] || ''
|
||||
@random_code = random_code
|
||||
Algolia.init(application_id: site.config['algolia_app_id'],
|
||||
api_key: site.config['algolia_api_key'])
|
||||
@indexes = setup_indexes
|
||||
generate_all
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_config?
|
||||
if @site.config['algolia_app_id'].nil? || @site.config['algolia_app_id'].empty?
|
||||
Jekyll.logger.warn(
|
||||
'Config Warning:',
|
||||
'You did not provide a ALGOLIA_APP_ID environment variable.'
|
||||
)
|
||||
return false
|
||||
end
|
||||
if @site.config['algolia_api_key'].nil? || @site.config['algolia_api_key'].empty?
|
||||
Jekyll.logger.warn(
|
||||
'Config Warning:',
|
||||
'You did not provide a ALGOLIA_API_KEY environment variable.'
|
||||
)
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def generate_all
|
||||
generate_blog_posts
|
||||
generate_guides
|
||||
generate_documentation
|
||||
generate_none_guide_guides
|
||||
generate_other
|
||||
end
|
||||
|
||||
def random_code
|
||||
SecureRandom.hex
|
||||
end
|
||||
|
||||
def setup_indexes
|
||||
indexes = {}
|
||||
@site.data['search_indexes'].each do |name, properties|
|
||||
index = Algolia::Index.new(@prefix + name)
|
||||
unless properties['settings'].nil?
|
||||
index.set_settings(properties['settings'])
|
||||
end
|
||||
indexes[name] = index
|
||||
end
|
||||
indexes
|
||||
end
|
||||
|
||||
def generate_documentation
|
||||
return if @site.data['docs'].nil?
|
||||
|
||||
documents = @site.data['docs'][:symbols].map do |item|
|
||||
next if item[:language] == 'c_preview'
|
||||
|
||||
if item[:summary].nil? || item[:summary].strip.length == 0
|
||||
Jekyll.logger.warn(
|
||||
'Search Warning:',
|
||||
"There was no summary for the symbol '#{item[:name]}' in #{item[:language]}."
|
||||
)
|
||||
end
|
||||
{
|
||||
'objectID' => item[:url],
|
||||
'title' => item[:name],
|
||||
'splitTitle' => item[:name].split(/(?=[A-Z])/).join(' '),
|
||||
'url' => item[:url],
|
||||
'summary' => item[:summary],
|
||||
'kind' => item[:kind],
|
||||
'language' => item[:language],
|
||||
'type' => 'documentation',
|
||||
'ranking' => doc_language_rank[item[:language]] * 1000,
|
||||
'randomCode' => @random_code
|
||||
}
|
||||
end.compact
|
||||
@indexes['documentation'].save_objects(documents)
|
||||
end
|
||||
|
||||
def generate_blog_posts
|
||||
documents = []
|
||||
@site.posts.docs.each do | post |
|
||||
# Calculate the age of the post so we can prioritise newer posts
|
||||
# over older ones.
|
||||
# NOTE: post.date is actually a Time object, despite its name
|
||||
age = (Time.now - post.date).round
|
||||
author = post.data['author']
|
||||
|
||||
post.get_sections.each do | section |
|
||||
# Ignore sections without any contents.
|
||||
if section[:contents].strip.size == 0
|
||||
next
|
||||
end
|
||||
|
||||
if section[:title].nil?
|
||||
url = post.url
|
||||
else
|
||||
url = post.url + '#' + section[:title].slugize
|
||||
end
|
||||
|
||||
document = {
|
||||
'objectID' => url,
|
||||
'title' => post.data['title'],
|
||||
'sectionTitle' => section[:title],
|
||||
'url' => url,
|
||||
'urlDisplay' => post.url,
|
||||
'author' => author,
|
||||
'content' => HTMLEntities.new.decode(section[:contents]),
|
||||
'posted' => post.date,
|
||||
'age' => age,
|
||||
'type' => 'blog post',
|
||||
'randomCode' => @random_code
|
||||
}
|
||||
documents << document
|
||||
end
|
||||
end
|
||||
@indexes['blog-posts'].save_objects(documents)
|
||||
end
|
||||
|
||||
def generate_guides
|
||||
documents = []
|
||||
return if @site.collections['guides'].nil?
|
||||
|
||||
@site.collections['guides'].docs.each do | guide |
|
||||
group = @site.data['guides'][guide.data['guide_group']]
|
||||
unless group.nil? || group['subgroups'].nil? || guide.data['guide_subgroup'].nil?
|
||||
subgroup = group.nil? ? '' : group['subgroups'][guide.data['guide_subgroup']]
|
||||
end
|
||||
|
||||
guide.get_sections.each do | section |
|
||||
url = guide.url
|
||||
unless section[:title].nil?
|
||||
url = url + '#' + section[:title].slugize
|
||||
end
|
||||
|
||||
document = {
|
||||
'objectID' => url,
|
||||
'title' => guide.data['title'],
|
||||
'sectionTitle' => section[:title],
|
||||
'url' => url,
|
||||
'urlDisplay' => guide.url,
|
||||
'content' => HTMLEntities.new.decode(section[:contents]),
|
||||
'group' => group.nil? ? '' : group['title'],
|
||||
'subgroup' => subgroup.nil? ? '' : subgroup['title'],
|
||||
'type' => 'guide',
|
||||
'randomCode' => @random_code
|
||||
}
|
||||
documents << document
|
||||
end
|
||||
end
|
||||
|
||||
@indexes['guides'].save_objects(documents)
|
||||
end
|
||||
|
||||
def generate_none_guide_guides
|
||||
documents = []
|
||||
gs_pages = @site.pages.select { |page| page.data['search_index'] }
|
||||
gs_pages.each do |page|
|
||||
page.get_sections.each do |section|
|
||||
url = page.url
|
||||
url = url + '#' + section[:title].slugize unless section[:title].nil?
|
||||
document = {
|
||||
'objectID' => url,
|
||||
'title' => page.data['title'],
|
||||
'sectionTitle' => section[:title],
|
||||
'url' => url,
|
||||
'urlDisplay' => page.url,
|
||||
'content' => HTMLEntities.new.decode(section[:contents]),
|
||||
'group' => page.data['search_group'],
|
||||
'subgroup' => page.data['sub_group'],
|
||||
'type' => 'not-guide',
|
||||
'randomCode' => @random_code
|
||||
}
|
||||
documents << document
|
||||
end
|
||||
end
|
||||
@indexes['guides'].save_objects(documents)
|
||||
end
|
||||
|
||||
def generate_other
|
||||
documents = @site.data['search-other'].map do |other|
|
||||
{
|
||||
'objectID' => other['id'],
|
||||
'title' => other['title'],
|
||||
'url' => other['url'],
|
||||
'content' => other['description'],
|
||||
'randomCode' => @random_code
|
||||
}
|
||||
end
|
||||
@indexes['other'].save_objects(documents)
|
||||
end
|
||||
|
||||
def doc_language_rank
|
||||
{
|
||||
'c' => 10,
|
||||
'rockyjs' => 9,
|
||||
'pebblekit_js' => 8,
|
||||
'pebblekit_android' => 6,
|
||||
'pebblekit_ios' => 4
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
166
devsite/plugins/generator_docs.rb
Normal file
166
devsite/plugins/generator_docs.rb
Normal file
|
@ -0,0 +1,166 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'open-uri'
|
||||
require 'zip'
|
||||
require 'nokogiri'
|
||||
|
||||
require_relative '../lib/pebble_documentation_pebblekit_android.rb'
|
||||
require_relative '../lib/pebble_documentation_c.rb'
|
||||
require_relative '../lib/pebble_documentation_js.rb'
|
||||
require_relative '../lib/pebble_documentation_pebblekit_ios.rb'
|
||||
require_relative '../lib/toc_generator.rb'
|
||||
|
||||
OpenURI::Buffer.send :remove_const, 'StringMax' if OpenURI::Buffer.const_defined?('StringMax')
|
||||
OpenURI::Buffer.const_set 'StringMax', 0
|
||||
|
||||
# Master plugins for processing the documentation on the site.
|
||||
# The actual work is done in individual classes for each language type.
|
||||
# Each class generates three types of data:
|
||||
# - Symbols
|
||||
# - Pages
|
||||
# - Tree
|
||||
#
|
||||
# The Symbols are a list of objects that are things such as methods, classes
|
||||
# or enums, that are used to populate the search indexes, and power the double
|
||||
# backtick docs linking.
|
||||
#
|
||||
# The Pages are the Jekyll pages that will be part of the built site and are
|
||||
# what the user will see.
|
||||
#
|
||||
# The Tree is used to build the site navigation.
|
||||
#
|
||||
# Note: The docs_url variable is created from the environment.
|
||||
# See environment.md for more information.
|
||||
|
||||
module Jekyll
|
||||
class DocsGenerator < Generator
|
||||
priority :high
|
||||
|
||||
def generate(site)
|
||||
@site = site
|
||||
@docs = {
|
||||
symbols: [],
|
||||
pages: [],
|
||||
tree: {}
|
||||
}
|
||||
if @site.config['docs_url'].nil? || @site.config['docs_url'].empty?
|
||||
Jekyll.logger.warn(
|
||||
'Config Warning:',
|
||||
'You did not provide a DOCS_URL environment variable.'
|
||||
)
|
||||
elsif !@site.config['skip_docs'].nil? && (@site.config['skip_docs'] == 'true')
|
||||
Jekyll.logger.info('Docs Generation:', 'Skipping documentation generation...')
|
||||
else
|
||||
Jekyll.logger.info('Docs Generation:', 'Generating pages...')
|
||||
generate_docs
|
||||
render_pages
|
||||
Jekyll.logger.info('Docs Generation:', 'Done.')
|
||||
end
|
||||
set_data
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_docs
|
||||
# The order of these functions will determine the order of preference
|
||||
# when looking up symbols e.g. double backticks
|
||||
# DO NOT CHANGE THE ORDER UNLESS YOU KNOW WHAT YOU ARE DOING
|
||||
generate_docs_c
|
||||
generate_docs_c_preview unless @site.data['docs']['c_preview'].nil?
|
||||
generate_docs_rocky_js
|
||||
generate_docs_pebblekit_js
|
||||
generate_docs_pebblekit_android
|
||||
generate_docs_pebblekit_ios
|
||||
end
|
||||
|
||||
def render_pages
|
||||
@docs[:pages].each { |page| @site.pages << page }
|
||||
end
|
||||
|
||||
def set_data
|
||||
# A somewhat ugly hack to let the Markdown parser have access
|
||||
# to this data.
|
||||
@site.config[:docs] = @docs
|
||||
@site.data['docs'] = @docs
|
||||
# Another ugly hack to make accessing the data much easier from Liquid.
|
||||
@site.data['docs_tree'] = JSON.parse(JSON.dump(@docs[:tree]))
|
||||
@site.data['symbols'] = JSON.parse(JSON.dump(@docs[:symbols]))
|
||||
end
|
||||
|
||||
def generate_docs_c
|
||||
docs = Pebble::DocumentationC.new(
|
||||
@site,
|
||||
@site.config['docs_url'] + @site.data['docs']['c'],
|
||||
'/docs/c/'
|
||||
)
|
||||
load_data(docs, :c)
|
||||
end
|
||||
|
||||
def generate_docs_c_preview
|
||||
docs = Pebble::DocumentationC.new(
|
||||
@site,
|
||||
@site.config['docs_url'] + @site.data['docs']['c_preview'],
|
||||
'/docs/c/preview/',
|
||||
'c_preview'
|
||||
)
|
||||
load_data(docs, :c_preview)
|
||||
end
|
||||
|
||||
def generate_docs_rocky_js
|
||||
docs = Pebble::DocumentationJs.new(
|
||||
@site,
|
||||
@site.data['docs']['rocky_js'],
|
||||
'/docs/rockyjs/',
|
||||
'rockyjs',
|
||||
true
|
||||
)
|
||||
load_data(docs, :rockyjs)
|
||||
end
|
||||
|
||||
def generate_docs_pebblekit_js
|
||||
docs = Pebble::DocumentationJs.new(
|
||||
@site,
|
||||
@site.data['docs']['pebblekit_js'],
|
||||
'/docs/pebblekit-js/',
|
||||
'pebblekit_js'
|
||||
)
|
||||
load_data(docs, :pebblekit_js)
|
||||
end
|
||||
|
||||
def generate_docs_pebblekit_android
|
||||
docs = Pebble::DocumentationPebbleKitAndroid.new(
|
||||
@site,
|
||||
@site.config['docs_url'] + @site.data['docs']['pebblekit_android']
|
||||
)
|
||||
load_data(docs, :pebblekit_android)
|
||||
end
|
||||
|
||||
def generate_docs_pebblekit_ios
|
||||
docs = Pebble::DocumentationPebbleKitIos.new(
|
||||
@site,
|
||||
@site.config['docs_url'] + @site.data['docs']['pebblekit_ios'],
|
||||
'/docs/pebblekit-ios/'
|
||||
)
|
||||
load_data(docs, :pebblekit_ios)
|
||||
end
|
||||
|
||||
def load_data(docs, type)
|
||||
@docs[:tree][type] = []
|
||||
docs.load_symbols(@docs[:symbols])
|
||||
docs.create_pages(@docs[:pages])
|
||||
docs.build_tree(@docs[:tree][type])
|
||||
end
|
||||
end
|
||||
end
|
65
devsite/plugins/generator_examples.rb
Normal file
65
devsite/plugins/generator_examples.rb
Normal file
|
@ -0,0 +1,65 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
module Jekyll
|
||||
class GeneratorExamples < Generator
|
||||
def initialize(_config)
|
||||
end
|
||||
|
||||
def generate(site)
|
||||
@site = site
|
||||
process_tags
|
||||
process_languages
|
||||
process_hardware_platforms
|
||||
@site.data['examples_metadata'] = {
|
||||
'tags' => @tags,
|
||||
'languages' => @languages,
|
||||
'hardware_platforms' => @hardware_platforms
|
||||
}
|
||||
end
|
||||
|
||||
def process_tags
|
||||
@tags = {}
|
||||
@site.data['examples'].each do |example|
|
||||
next if example['tags'].nil?
|
||||
example['tags'].each do |tag|
|
||||
@tags[tag] = { 'count' => 0 } unless @tags.has_key?(tag)
|
||||
@tags[tag]['count'] += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def process_languages
|
||||
@languages = {}
|
||||
@site.data['examples'].each do |example|
|
||||
next if example['languages'].nil?
|
||||
example['languages'].each do |language|
|
||||
@languages[language] = { 'count' => 0 } unless @languages.has_key?(language)
|
||||
@languages[language]['count'] += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def process_hardware_platforms
|
||||
@hardware_platforms = {}
|
||||
@site.data['examples'].each do |example|
|
||||
next if example['hardware_platforms'].nil?
|
||||
example['hardware_platforms'].each do |hw|
|
||||
@hardware_platforms[hw] = { 'count' => 0 } unless @hardware_platforms.has_key?(hw)
|
||||
@hardware_platforms[hw]['count'] += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
94
devsite/plugins/generator_guides.rb
Normal file
94
devsite/plugins/generator_guides.rb
Normal file
|
@ -0,0 +1,94 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'htmlentities'
|
||||
require 'algoliasearch'
|
||||
require 'slugize'
|
||||
require 'dotenv'
|
||||
|
||||
module Jekyll
|
||||
|
||||
class GeneratorGuides < Generator
|
||||
|
||||
priority :highest
|
||||
|
||||
def initialize(config)
|
||||
end
|
||||
|
||||
def generate(site)
|
||||
@site = site
|
||||
site.collections['guides'].docs.each do |guide|
|
||||
group = find_group(guide)
|
||||
subgroup = find_subgroup(guide, group)
|
||||
guide.data['group_data'] = group
|
||||
guide.data['subgroup_data'] = subgroup
|
||||
unless subgroup.nil?
|
||||
subgroup['guides'] << {
|
||||
'title' => guide.data['title'],
|
||||
'url' => guide.url,
|
||||
'menu' => guide.data['menu'],
|
||||
'order' => guide.data['order'],
|
||||
'summary' => guide.data['description']
|
||||
}
|
||||
else
|
||||
unless group.nil?
|
||||
group['guides'] << {
|
||||
'title' => guide.data['title'],
|
||||
'url' => guide.url,
|
||||
'menu' => guide.data['menu'],
|
||||
'order' => guide.data['order'],
|
||||
'summary' => guide.data['description']
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
site.data['guides'] = [] if site.data['guides'].nil?
|
||||
|
||||
site.data['guides'].each do |id, group|
|
||||
group['url'] = "/guides/#{id}/"
|
||||
if group['subgroups'].nil?
|
||||
group['subgroups'] = []
|
||||
next
|
||||
end
|
||||
group['subgroups'].each do |id, subgroup|
|
||||
subgroup['url'] = "#{group['url']}#{id}/"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def find_group(guide)
|
||||
@site.data['guides'].each do |id, group|
|
||||
if id == guide.data['guide_group']
|
||||
group['guides'] = [] if group['guides'].nil?
|
||||
return group
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def find_subgroup(guide, group)
|
||||
return if group.nil? || group['subgroups'].nil?
|
||||
group['subgroups'].each do |id, subgroup|
|
||||
if id == guide.data['guide_subgroup']
|
||||
subgroup['guides'] = [] if subgroup['guides'].nil?
|
||||
return subgroup
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
35
devsite/plugins/generator_meetups.rb
Normal file
35
devsite/plugins/generator_meetups.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'googlestaticmap'
|
||||
|
||||
module Jekyll
|
||||
class GeneratorMeetups < Generator
|
||||
def initialize(config)
|
||||
end
|
||||
|
||||
def generate(site)
|
||||
@site = site
|
||||
map = GoogleStaticMap.new(:width => 700, :height => 500)
|
||||
site.data['meetups'].each do |meetup|
|
||||
map.markers << MapMarker.new(:color => "0x9D49D5FF",
|
||||
:location => MapLocation.new(:latitude => meetup['pin']['latitude'],
|
||||
:longitude => meetup['pin']['longitude']
|
||||
)
|
||||
)
|
||||
end
|
||||
@site.data['meetups_map_url'] = map.url(:auto)
|
||||
end
|
||||
end
|
||||
end
|
58
devsite/plugins/generator_minify_js.rb
Normal file
58
devsite/plugins/generator_minify_js.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'uglifier'
|
||||
require 'digest'
|
||||
|
||||
module Jekyll
|
||||
# Jekyll Generator for concatenating and minifying JS for production site
|
||||
class GeneratorMinifyJS < Generator
|
||||
priority :highest
|
||||
|
||||
def initialize(_)
|
||||
end
|
||||
|
||||
def generate(site)
|
||||
return if site.config['rack_env'] == 'development'
|
||||
@site = site
|
||||
@tmp_dir = File.join(site.source, '../tmp/')
|
||||
@js_dir = 'assets/js/'
|
||||
@tmp_js_dir = File.join(@tmp_dir, @js_dir)
|
||||
libs_js = uglify_libs
|
||||
libs_md5 = Digest::MD5.hexdigest(libs_js)
|
||||
@site.data['js']['lib_hash'] = libs_md5
|
||||
create_libs_js(libs_js, libs_md5)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def uglify_libs
|
||||
ugly_libs = []
|
||||
@site.data['js']['libs'].each do |lib|
|
||||
lib_path = File.join(@site.source, 'assets', lib['path'])
|
||||
ugly_libs << Uglifier.compile(File.read(lib_path))
|
||||
end
|
||||
ugly_libs.join("\n\n")
|
||||
end
|
||||
|
||||
def create_libs_js(js, md5)
|
||||
FileUtils.mkdir_p(@tmp_js_dir)
|
||||
File.open(File.join(@tmp_js_dir, "libs-#{md5}.js"), 'w') do |f|
|
||||
f.write(js)
|
||||
end
|
||||
@site.static_files << Jekyll::StaticFile.new(@site, @tmp_dir, @js_dir,
|
||||
"libs-#{md5}.js")
|
||||
end
|
||||
end
|
||||
end
|
62
devsite/plugins/generator_redirects.rb
Normal file
62
devsite/plugins/generator_redirects.rb
Normal file
|
@ -0,0 +1,62 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
module Jekyll
|
||||
|
||||
class GeneratorRedirects < Generator
|
||||
|
||||
priority :lowest
|
||||
|
||||
def initialize(config)
|
||||
end
|
||||
|
||||
def generate(site)
|
||||
@site = site
|
||||
site.data['redirects'].each do |redirect|
|
||||
if is_infinite_redirect?(redirect[0], redirect[1])
|
||||
Jekyll.logger.warn "Redirect Warning:", "Skipping redirect of #{redirect[0]} to #{redirect[1]}"
|
||||
next
|
||||
end
|
||||
@site.pages << RedirectPage.new(@site, @site.source, File.dirname(redirect[0]), File.basename(redirect[0]), redirect[1])
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns true if the redirect pair (from, to) will cause an infinite
|
||||
# redirect.
|
||||
def is_infinite_redirect?(from, to)
|
||||
return true if from == to
|
||||
return true if File.basename(from) == 'index.html' && File.dirname(from) == File.dirname(to + 'index.html')
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class RedirectPage < Page
|
||||
|
||||
def initialize(site, base, dir, name, redirect_to)
|
||||
@site = site
|
||||
@base = base
|
||||
@dir = dir
|
||||
@name = name.empty? ? 'index.html' : name
|
||||
|
||||
self.process(@name)
|
||||
self.read_yaml(File.join(base, '_layouts', 'utils'), 'redirect_permanent.html')
|
||||
self.data['redirect_to'] = redirect_to
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
54
devsite/plugins/jekyll_convertible.rb
Normal file
54
devsite/plugins/jekyll_convertible.rb
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'redcarpet'
|
||||
require_relative '../lib/search_markdown'
|
||||
|
||||
module Jekyll
|
||||
|
||||
module Convertible
|
||||
|
||||
def get_output
|
||||
process_search
|
||||
@search_markdown.get_contents
|
||||
end
|
||||
|
||||
def get_sections
|
||||
process_search
|
||||
@search_markdown.get_sections
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_search
|
||||
unless @search_markdown.nil?
|
||||
return
|
||||
end
|
||||
@search_markdown = Pebble::SearchMarkdown.new()
|
||||
redcarpet = Redcarpet::Markdown.new(@search_markdown,
|
||||
fenced_code_blocks: true,
|
||||
autolink: true,
|
||||
tables: true,
|
||||
no_intra_emphasis: true,
|
||||
strikethrough: true,
|
||||
highlight: true)
|
||||
payload = {}
|
||||
info = { :filters => [Jekyll::Filters], :registers => { :site => site, :page => payload['page'] } }
|
||||
raw_content = render_liquid(content, payload, info, '.')
|
||||
redcarpet.render(raw_content)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
85
devsite/plugins/jekyll_document.rb
Normal file
85
devsite/plugins/jekyll_document.rb
Normal file
|
@ -0,0 +1,85 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require_relative '../lib/toc_generator'
|
||||
|
||||
module Jekyll
|
||||
|
||||
class Document
|
||||
|
||||
include Convertible
|
||||
|
||||
alias_method :parent_to_liquid, :to_liquid
|
||||
|
||||
def to_liquid
|
||||
Utils.deep_merge_hashes parent_to_liquid, {
|
||||
'toc' => toc,
|
||||
'related_docs' => related_docs
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def toc
|
||||
unless @toc
|
||||
generate_toc if data['generate_toc']
|
||||
end
|
||||
(@toc.nil? || @toc.empty?) ? nil : @toc
|
||||
end
|
||||
|
||||
def related_docs
|
||||
# Skip the warning, we don't want docs or links to them
|
||||
if !@site.config['skip_docs'].nil? && (@site.config['skip_docs'] == 'true')
|
||||
return
|
||||
end
|
||||
|
||||
return nil if data['related_docs'].nil?
|
||||
|
||||
docs = data['related_docs'].map do | doc |
|
||||
# Use existing doc data if it exists
|
||||
if !doc.nil? and doc.is_a?(Hash) and doc.has_key?("name") and doc.has_key?("url")
|
||||
doc
|
||||
else
|
||||
# use nil if data is formated in an unexpected way
|
||||
if doc.nil? or !doc.is_a? String
|
||||
next
|
||||
else
|
||||
# Otherwise search for the symbol
|
||||
symbol = @site.config[:docs][:symbols].find do |symbol|
|
||||
symbol[:name].downcase == doc.downcase
|
||||
end
|
||||
|
||||
if symbol.nil?
|
||||
Jekyll.logger.warn "Related Warning:", "Could not find symbol '#{doc}' in '#{data['title']}'"
|
||||
next
|
||||
else
|
||||
{
|
||||
'name' => symbol[:name],
|
||||
'url' => symbol[:url],
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def generate_toc
|
||||
generator = Pebble::TocGenerator.new(data['toc_max_depth'] || -1)
|
||||
@toc = generator.generate(content)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
45
devsite/plugins/jekyll_page.rb
Normal file
45
devsite/plugins/jekyll_page.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require_relative '../lib/toc_generator'
|
||||
|
||||
# Overriding the Jekyll Page class to do magic
|
||||
|
||||
module Jekyll
|
||||
|
||||
class Page
|
||||
|
||||
def to_liquid(attrs = ATTRIBUTES_FOR_LIQUID)
|
||||
super(attrs + %w[
|
||||
toc
|
||||
])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def toc
|
||||
unless @toc
|
||||
generate_toc if data['generate_toc']
|
||||
end
|
||||
(@toc.nil? || @toc.empty?) ? nil : @toc
|
||||
end
|
||||
|
||||
def generate_toc
|
||||
generator = Pebble::TocGenerator.new(data['toc_max_depth'] || -1)
|
||||
@toc = generator.generate(content)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
45
devsite/plugins/jekyll_post.rb
Normal file
45
devsite/plugins/jekyll_post.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require_relative '../lib/toc_generator'
|
||||
|
||||
# Overriding the Jekyll Page class to do magic
|
||||
|
||||
module Jekyll
|
||||
|
||||
class Post
|
||||
|
||||
def to_liquid(attrs = ATTRIBUTES_FOR_LIQUID)
|
||||
super(attrs + %w[
|
||||
toc
|
||||
])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def toc
|
||||
unless @toc
|
||||
generate_toc
|
||||
end
|
||||
(@toc.nil? || @toc.empty?) ? nil : @toc
|
||||
end
|
||||
|
||||
def generate_toc
|
||||
generator = Pebble::TocGenerator.new(data['toc_max_depth'] || -1)
|
||||
@toc = generator.generate(content)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
323
devsite/plugins/pebble_markdown_parser.rb
Normal file
323
devsite/plugins/pebble_markdown_parser.rb
Normal file
|
@ -0,0 +1,323 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'redcarpet'
|
||||
require 'pygments'
|
||||
require 'slugize'
|
||||
require 'nokogiri'
|
||||
|
||||
module Jekyll
|
||||
module Converters
|
||||
# Jekyll Markdown Converter wrapper for Redcarpet using the PebbleMarkdown
|
||||
# HTML render class.
|
||||
class Markdown::PebbleMarkdownParser
|
||||
def initialize(config)
|
||||
@site_config = config
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
content = '' if content.nil?
|
||||
pbl_md = PebbleMarkdown.new(@site_config)
|
||||
Redcarpet::Markdown.new(pbl_md,
|
||||
fenced_code_blocks: true,
|
||||
autolink: true,
|
||||
tables: true,
|
||||
no_intra_emphasis: true,
|
||||
strikethrough: true,
|
||||
highlight: true).render(content)
|
||||
end
|
||||
|
||||
# Redcarpet HTML render class to handle the extra functionality.
|
||||
class PebbleMarkdown < Redcarpet::Render::HTML
|
||||
def initialize(config)
|
||||
@site_config = config
|
||||
super()
|
||||
end
|
||||
|
||||
def preprocess(document)
|
||||
process_links_with_double_backticks(document)
|
||||
process_double_backticks(document)
|
||||
document
|
||||
end
|
||||
|
||||
# Add ID and anchors to all headers.
|
||||
def header(text, header_level)
|
||||
if text.include?('<')
|
||||
id = Nokogiri::HTML(text).text.slugize
|
||||
else
|
||||
id = text.slugize
|
||||
end
|
||||
str = "<h#{header_level} id=\"#{id}\" class=\"anchor\">"
|
||||
str += text
|
||||
str += "</h#{header_level}>"
|
||||
str
|
||||
end
|
||||
|
||||
def paragraph(text)
|
||||
if (match = /^\^(CP|LC)\^/.match(text))
|
||||
"<p class=\"platform-specific\" data-sdk-platform=\"#{shortcode_to_platform(match[1])}\">#{text[(match[1].length + 2)..-1].strip}</p>"
|
||||
else
|
||||
"<p>#{text}</p>"
|
||||
end
|
||||
end
|
||||
|
||||
# Use Pygments to generate the syntax highlighting markup.
|
||||
def block_code(code, language)
|
||||
classes = ['highlight']
|
||||
if /^nc\|/.match(language)
|
||||
classes << 'no-copy'
|
||||
language = language[3..-1]
|
||||
end
|
||||
if language == 'text'
|
||||
"<div class=\"#{classes.join(' ')}\"><pre>#{code}</pre></div>"
|
||||
else
|
||||
set_classes(Pygments.highlight(code, lexer: language), classes)
|
||||
end
|
||||
end
|
||||
|
||||
def link(url, title, content)
|
||||
if content == 'EMBED'
|
||||
embed(url)
|
||||
else
|
||||
classes = []
|
||||
if /^DOCS:/.match(title)
|
||||
title = title[5..-1]
|
||||
classes << 'link--docs'
|
||||
end
|
||||
# URL begins with a single slash (but not double slash)
|
||||
url = baseurl + url if %r{^/[^/]}.match(url)
|
||||
data_str = ''
|
||||
if (match = regex_button.match(content))
|
||||
classes << 'btn'
|
||||
classes << 'btn--markdown'
|
||||
classes.concat(match[3].split(',').map { |cls| 'btn--' + cls })
|
||||
content = match[1]
|
||||
end
|
||||
if (match = regex_link_data.match(title))
|
||||
match[3].split(',').each do |item|
|
||||
item = item.split(':')
|
||||
data_str += ' data-' + item[0] + '="' + item[1] + '"'
|
||||
end
|
||||
title = match[1]
|
||||
end
|
||||
"<a href=\"#{url}\"" \
|
||||
" title=\"#{title}\"" \
|
||||
" class=\"#{classes.join(' ')}\"#{data_str}>#{content}</a>"
|
||||
end
|
||||
end
|
||||
|
||||
# Better image handling.
|
||||
# * Add size specificiations (taken from RDiscount)
|
||||
# * Prepend the site baselink to images that beings with /
|
||||
# TODO: Handle the cases where image link begins with //
|
||||
# TODO: Maybe add additional style choices (centered, inline, etc)
|
||||
def image(link, title, alt_text)
|
||||
if (size_match = /^(.*)\ =([0-9]+)x?([0-9]*)$/.match(link))
|
||||
link = size_match[1]
|
||||
width = size_match[2]
|
||||
height = size_match[3]
|
||||
end
|
||||
|
||||
classes = []
|
||||
if (match = regex_button.match(alt_text))
|
||||
classes.concat(match[3].split(','))
|
||||
alt_text = match[1]
|
||||
end
|
||||
|
||||
link = asset_path + link if %r{^/[^/]}.match(link)
|
||||
|
||||
img_str = "<img src=\"#{link}\""
|
||||
img_str += " title=\"#{title}\"" unless title.to_s.empty?
|
||||
img_str += " alt=\"#{alt_text}\"" unless alt_text.to_s.empty?
|
||||
img_str += " width=\"#{width}\"" unless width.to_s.empty?
|
||||
img_str += " height=\"#{height}\"" unless height.to_s.empty?
|
||||
img_str += " class=\"#{classes.join(' ')}\"" unless classes.empty?
|
||||
img_str += ' />'
|
||||
img_str
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# This is used to process links that contain double backticks.
|
||||
# For example:
|
||||
# [click me](``Window``)
|
||||
# This allows for the text of a link to be different than the name
|
||||
# of the symbol you are linking to.
|
||||
def process_links_with_double_backticks(document)
|
||||
# Skip the warning, we don't want docs or links to them
|
||||
if !@site_config['skip_docs'].nil? && (@site_config['skip_docs'] == 'true')
|
||||
return
|
||||
end
|
||||
|
||||
document.gsub!(/(\[([^\]]+)\])\(``([^`]+)``\)/) do |str|
|
||||
url = Regexp.last_match[3]
|
||||
text = Regexp.last_match[2]
|
||||
text_in_brackets = Regexp.last_match[1]
|
||||
|
||||
language, symbol = parse_symbol(url)
|
||||
entry = docs_lookup(symbol, language)
|
||||
Jekyll.logger.warn('Backtick Warning:', "Could not find symbol '#{text}'") if entry.nil?
|
||||
|
||||
entry ? (text_in_brackets + "#{backtick_link(entry)}") : text
|
||||
end
|
||||
end
|
||||
|
||||
def process_double_backticks(document)
|
||||
# Skip the warning, we don't want docs or links to them
|
||||
if !@site_config['skip_docs'].nil? && (@site_config['skip_docs'] == 'true')
|
||||
return
|
||||
end
|
||||
|
||||
document.gsub!(/([^`]+|\A)``([^`]+)``/) do |str|
|
||||
language, symbol = parse_symbol(Regexp.last_match[2])
|
||||
entry = docs_lookup(symbol, language)
|
||||
if entry.nil?
|
||||
Jekyll.logger.warn('Backtick Warning:', "Could not find symbol '#{Regexp.last_match[2]}'")
|
||||
language.nil? ? str : ('``' + Regexp.last_match[2][language.size+1..-1] + '``')
|
||||
else
|
||||
symbol_str = Regexp.last_match[2]
|
||||
symbol_str = symbol_str[language.size+1..-1] unless language.nil?
|
||||
"#{Regexp.last_match[1]}[`#{symbol_str}`]#{backtick_link(entry)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def backtick_link(symbol)
|
||||
"(#{symbol[:url]} \"DOCS:#{symbol[:name]}\")"
|
||||
end
|
||||
|
||||
# Split the documentation string into language and symbol name.
|
||||
def parse_symbol(str)
|
||||
match = /^([a-z]*:)?([A-Za-z0-9_:\.\ ]*)/.match(str)
|
||||
language = match[1]
|
||||
language = language[0..-2].downcase unless language.nil?
|
||||
name = match[2]
|
||||
return language, name
|
||||
end
|
||||
|
||||
def docs_lookup(name, language)
|
||||
return nil if name.nil?
|
||||
@site_config[:docs][:symbols].find do |symbol|
|
||||
symbol[:name].downcase == name.downcase &&
|
||||
(language.nil? ? true : symbol[:language] == language)
|
||||
end
|
||||
end
|
||||
|
||||
def embed(url)
|
||||
if (match = regex_youtube_video.match(url))
|
||||
youtube(match[2])
|
||||
elsif (match = regex_youtube_playlist.match(url))
|
||||
youtube_playlist(match[3])
|
||||
elsif (match = regex_vimeo_video.match(url))
|
||||
vimeo(match[1])
|
||||
elsif (match = regex_slideshare.match(url))
|
||||
slideshare(match[1])
|
||||
elsif (match = regex_gist.match(url))
|
||||
gist(match[2])
|
||||
end
|
||||
end
|
||||
|
||||
def set_classes(html, classes)
|
||||
doc = Nokogiri::HTML::DocumentFragment.parse(html)
|
||||
doc.child['class'] = classes.join(' ')
|
||||
doc.to_html
|
||||
end
|
||||
|
||||
def youtube(id)
|
||||
'<div class="embed embed--youtube"><div class="video-wrapper">' \
|
||||
'<iframe width="640" height="360" frameborder="0" allowfullscreen' \
|
||||
" src=\"//www.youtube.com/embed/#{id}?rel=0\" ></iframe>" \
|
||||
'</div></div>'
|
||||
end
|
||||
|
||||
def youtube_playlist(id)
|
||||
'<div class="embed embed--youtube"><div class="video-wrapper">' \
|
||||
'<iframe frameborder="0" allowfullscreen'\
|
||||
" src=\"//www.youtube.com/embed/videoseries?list=#{id}\" ></iframe>" \
|
||||
'</div></div>'
|
||||
end
|
||||
|
||||
def vimeo(id)
|
||||
'<div class="embed embed--vimeo"><div class="video-wrapper">' \
|
||||
'<iframe width="500" height="281" frameborder="0"' \
|
||||
' webkitallowfullscreen mozallowfullscreen allowfullscreen' \
|
||||
" src=\"//player.vimeo.com/video/#{id}\"></iframe>" \
|
||||
'</div></div>'
|
||||
end
|
||||
|
||||
def slideshare(id)
|
||||
'<div style="width: 100%; height: 0px; position: relative; padding-bottom: 63%;">' \
|
||||
"<iframe src=\"https://www.slideshare.net/slideshow/embed_code/key/#{id}\"" \
|
||||
' frameborder="0" allowfullscreen style="width: 100%; height: 100%; position: absolute;">' \
|
||||
'</iframe>'\
|
||||
'</div>'
|
||||
end
|
||||
|
||||
def gist(id)
|
||||
'<div class="embed embed--gist">' \
|
||||
"<script src=\"//gist.github.com/#{id}.js\"></script>" \
|
||||
'</div>'
|
||||
end
|
||||
|
||||
def baseurl
|
||||
@site_config['baseurl'] || ''
|
||||
end
|
||||
|
||||
def asset_path
|
||||
@site_config['asset_path'] || ''
|
||||
end
|
||||
|
||||
def link_sdk(url, title, content)
|
||||
|
||||
end
|
||||
|
||||
def regex_youtube_video
|
||||
%r{youtube\.com/(watch\?v=|v/|embed/)([a-z0-9A-Z\-_]*)}
|
||||
end
|
||||
|
||||
def regex_youtube_playlist
|
||||
%r{^(https?://)?([w]{3}\.)?youtube\.com/playlist\?list=([a-z0-9A-Z\-]*)}
|
||||
end
|
||||
|
||||
def regex_vimeo_video
|
||||
%r{vimeo.com/video/([0-9]+)}
|
||||
end
|
||||
|
||||
def regex_slideshare
|
||||
%r{slideshare.net/slideshow/embed_code/key/([a-z0-9A-Z]*)}
|
||||
end
|
||||
|
||||
def regex_gist
|
||||
%r{^(https?://)?gist.github\.com/(.*)}
|
||||
end
|
||||
|
||||
def regex_button
|
||||
/^(.*)\ (>|>)\{?([a-z,0-9\-]*)\}?$/
|
||||
end
|
||||
|
||||
def regex_link_data
|
||||
/^(.*)\ (>|>)\{([a-z\-_,:0-9A-Z]+)\}$/
|
||||
end
|
||||
|
||||
def shortcode_to_platform(shortcode)
|
||||
platforms = {
|
||||
'CP' => 'cloudpebble',
|
||||
'LC' => 'local'
|
||||
}
|
||||
platforms[shortcode]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
42
devsite/plugins/tag_alert.rb
Normal file
42
devsite/plugins/tag_alert.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
module Jekyll
|
||||
class AlertBlock < Liquid::Block
|
||||
alias_method :render_block, :render
|
||||
|
||||
def initialize(tag_name, text, tokens)
|
||||
super
|
||||
@type = text.strip
|
||||
end
|
||||
|
||||
def render(context)
|
||||
site = context.registers[:site]
|
||||
converter = site.find_converter_instance(::Jekyll::Converters::Markdown)
|
||||
content = converter.convert(render_block(context))
|
||||
|
||||
if @type == "important"
|
||||
return "<div class=\"alert alert--fg-white alert--bg-dark-red\">" << "<strong>Important</strong><br/>" << "#{content}" << "</div>"
|
||||
end
|
||||
if @type == "notice"
|
||||
return "<div class=\"alert alert--fg-white alert--bg-purple\">" << "<strong>Notice</strong><br/>" << "#{content}" << "</div>"
|
||||
end
|
||||
|
||||
Jekyll.logger.error "Liquid Error:", "Alert type '#{@type}' is not valid. Use 'important' or 'notice'."
|
||||
return ''
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('alert', Jekyll::AlertBlock)
|
31
devsite/plugins/tag_asset_css.rb
Normal file
31
devsite/plugins/tag_asset_css.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Liquid tag for including a style tag.
|
||||
class TagAssetCss < Liquid::Tag
|
||||
def initialize(tag_name, text, tokens)
|
||||
super
|
||||
@text = text
|
||||
end
|
||||
|
||||
def render(context)
|
||||
style = context[@text]
|
||||
site = context.registers[:site]
|
||||
unless %r{^//}.match(style)
|
||||
style = "#{site.config['asset_path']}/css/#{style}.css"
|
||||
end
|
||||
"<link rel=\"stylesheet\" type=\"text/css\" href=\"#{style}\">"
|
||||
end
|
||||
end
|
||||
Liquid::Template.register_tag('asset_css', TagAssetCss)
|
31
devsite/plugins/tag_asset_js.rb
Normal file
31
devsite/plugins/tag_asset_js.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Liquid tag for including a script tag.
|
||||
class TagAssetJs < Liquid::Tag
|
||||
def initialize(tag_name, text, tokens)
|
||||
super
|
||||
@text = text
|
||||
end
|
||||
|
||||
def render(context)
|
||||
script = context[@text]
|
||||
site = context.registers[:site]
|
||||
unless %r{^//}.match(script)
|
||||
script = "#{site.config['asset_path']}/js/#{script}.js"
|
||||
end
|
||||
"<script type=\"text/javascript\" src=\"#{script}\"></script>"
|
||||
end
|
||||
end
|
||||
Liquid::Template.register_tag('asset_js', TagAssetJs)
|
35
devsite/plugins/tag_author_link.rb
Normal file
35
devsite/plugins/tag_author_link.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Creates a link to a blog post author page and uses their full name when known.
|
||||
class TagAuthorLink < Liquid::Tag
|
||||
def initialize(tag_name, text, tokens)
|
||||
super
|
||||
@text = text
|
||||
end
|
||||
|
||||
def render(context)
|
||||
author_name = context[@text]
|
||||
site = context.registers[:site]
|
||||
author = site.data['authors'][author_name]
|
||||
if author
|
||||
url = "#{site.baseurl}/blog/authors/#{author_name}/"
|
||||
"<a href=\"#{url}\">#{author['name']}</a>"
|
||||
else
|
||||
author_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('author_link', TagAuthorLink)
|
36
devsite/plugins/tag_author_photo.rb
Normal file
36
devsite/plugins/tag_author_photo.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
class TagAuthorPhoto < Liquid::Tag
|
||||
def initialize(tag_name, text, tokens)
|
||||
super
|
||||
pieces = text.split(' ')
|
||||
@name = pieces[0]
|
||||
@size = pieces[1].to_i
|
||||
end
|
||||
|
||||
def render(context)
|
||||
author_name = context[@name]
|
||||
site = context.registers[:site]
|
||||
author = site.data['authors'][author_name]
|
||||
unless author && author['photo']
|
||||
author = site.data['authors']['pebble']
|
||||
end
|
||||
photo = author['photo']
|
||||
photo = site.config['asset_path'] + photo if %r{^/[^/]}.match(photo)
|
||||
"<img src=\"#{photo}\" width=#{@size} height=#{@size}>"
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('author_photo', TagAuthorPhoto)
|
58
devsite/plugins/tag_cloudpebble_edit_gist.rb
Normal file
58
devsite/plugins/tag_cloudpebble_edit_gist.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Liquid inline tag to produce an Edit Gist in CloudPebble button
|
||||
class TagCloudPebbleEditGist < Liquid::Tag
|
||||
def initialize(tag_name, gist_id, tokens)
|
||||
super
|
||||
@gist_id = gist_id.strip
|
||||
end
|
||||
|
||||
def render(context)
|
||||
page = context.registers[:page]
|
||||
extension = File.extname(page['name'])
|
||||
if extension == '.md'
|
||||
render_markdown
|
||||
else
|
||||
render_html
|
||||
end
|
||||
end
|
||||
|
||||
def render_markdown
|
||||
"[#{content} >{#{markdown_classes}}](#{url})"
|
||||
end
|
||||
|
||||
def render_html
|
||||
"<a href=\"#{url}\" title=\"\" class=\"#{html_classes}\">#{content}</a>"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def url
|
||||
"https://cloudpebble.net/ide/gist/#{@gist_id}"
|
||||
end
|
||||
|
||||
def html_classes
|
||||
'btn btn--wide btn--pink'
|
||||
end
|
||||
|
||||
def markdown_classes
|
||||
'wide,pink'
|
||||
end
|
||||
|
||||
def content
|
||||
'Edit in CloudPebble'
|
||||
end
|
||||
end
|
||||
Liquid::Template.register_tag('cloudpebble_edit_gist', TagCloudPebbleEditGist)
|
43
devsite/plugins/tag_git_contributors.rb
Normal file
43
devsite/plugins/tag_git_contributors.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Liquid tag that displays the names of everyone who contributed on the file.
|
||||
class TagGitContributors < Liquid::Tag
|
||||
def initialize(tag_name, text, tokens)
|
||||
super
|
||||
@text = text
|
||||
end
|
||||
|
||||
def render(context)
|
||||
list = '<ul class="git-contributors">'
|
||||
contributors(context).each do |name|
|
||||
list += "<li>#{name}</li>" unless name.empty?
|
||||
end
|
||||
list += '</ul>'
|
||||
list
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def contributors(context)
|
||||
site = context.registers[:site]
|
||||
page = context[@text]
|
||||
file_path = page['relative_path'] || page['path']
|
||||
full_path = './' + site.config['source'] + file_path
|
||||
names = `git log --follow --format='%aN |' "#{full_path}" | sort -u`
|
||||
names.split('|').map { |name| name.strip }
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('git_contributors', TagGitContributors)
|
67
devsite/plugins/tag_guide_link.rb
Normal file
67
devsite/plugins/tag_guide_link.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
class TagGuideLink < Liquid::Tag
|
||||
def initialize(tag_name, text, tokens)
|
||||
super
|
||||
@text = text.strip
|
||||
end
|
||||
|
||||
def make_html(title, url)
|
||||
return "<a href=\"#{url}\"><em>#{title}</em></a>"
|
||||
end
|
||||
|
||||
def render(context)
|
||||
guide_path = @text.split('#')[0].strip
|
||||
guide_hash = (@text.split('#').length > 1 ? @text.split('#')[1] : '').strip
|
||||
if guide_hash.length > 1
|
||||
guide_hash = (guide_hash.split(' ')[0]).strip
|
||||
end
|
||||
|
||||
# Custom title?
|
||||
guide_title = nil
|
||||
index = @text.index('"')
|
||||
if index != nil
|
||||
guide_title = (@text.split('"')[1]).strip
|
||||
guide_title = guide_title.gsub('"', '')
|
||||
guide_path = guide_path.split(' ')[0]
|
||||
end
|
||||
|
||||
site = context.registers[:site]
|
||||
site.collections['guides'].docs.each do |guide|
|
||||
path = guide.relative_path.sub(/^_guides\//, '').sub(/\.md$/, '')
|
||||
|
||||
# Check if it's a 'section/guide' path
|
||||
if path.index('/') != nil
|
||||
if path == guide_path
|
||||
return make_html(guide_title != nil ? guide_title : guide.data['title'],
|
||||
"#{guide.url}#{guide_hash == '' ? '' : "##{guide_hash}"}")
|
||||
end
|
||||
end
|
||||
|
||||
# Check if it's a 'section' path
|
||||
site.data['guides'].each do |id, group|
|
||||
if id == guide_path
|
||||
return make_html(guide_title != nil ? guide_title : group['title'], "/guides/#{guide_path}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# No match
|
||||
Jekyll.logger.error "Liquid Error:", "Could not find the guide or section for #{@text}."
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('guide_link', TagGuideLink)
|
35
devsite/plugins/tag_highlight.rb
Normal file
35
devsite/plugins/tag_highlight.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'pygments'
|
||||
|
||||
# Liquid block tag to run code through Pygments syntax highlighter.
|
||||
class Highlight < Liquid::Block
|
||||
def initialize(tag_name, markup, tokens)
|
||||
super
|
||||
options = JSON.parse(markup)
|
||||
return unless options
|
||||
@language = options['language']
|
||||
@classes = options['classes']
|
||||
@options = options['options'] || {}
|
||||
end
|
||||
|
||||
def render(context)
|
||||
str = Pygments.highlight(super.strip, lexer: @language, options: @options)
|
||||
str.gsub!(/<div class="highlight"><pre>/,
|
||||
"<div class=\"highlight #{@classes}\"><pre>")
|
||||
str
|
||||
end
|
||||
end
|
||||
Liquid::Template.register_tag('highlight', Highlight)
|
38
devsite/plugins/tag_if_starts_with.rb
Normal file
38
devsite/plugins/tag_if_starts_with.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
class TagIfStartsWith < Liquid::Block
|
||||
def initialize(tag_name, text, tokens)
|
||||
super
|
||||
matches = /^([A-Za-z\.\_\-\/]+) ([A-Za-z\.\_\-\/\']+)$/.match(text.strip)
|
||||
@pieces = {
|
||||
:outer => matches[1],
|
||||
:inner => matches[2]
|
||||
}
|
||||
@text = text
|
||||
end
|
||||
|
||||
def render(context)
|
||||
outer = context[@pieces[:outer]]
|
||||
inner = context[@pieces[:inner]]
|
||||
if inner.nil? || outer.nil?
|
||||
return ""
|
||||
end
|
||||
if outer.downcase.start_with?(inner.downcase)
|
||||
return super.to_s.strip
|
||||
end
|
||||
""
|
||||
end
|
||||
end
|
||||
Liquid::Template.register_tag('if_starts_with', TagIfStartsWith)
|
30
devsite/plugins/tag_import_js.rb
Normal file
30
devsite/plugins/tag_import_js.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
class TagImportJs < Liquid::Tag
|
||||
|
||||
def initialize(tag_name, text, tokens)
|
||||
super
|
||||
@text = text
|
||||
end
|
||||
|
||||
def render(context)
|
||||
site = context.registers[:site]
|
||||
filename = File.join(site.source, "_js", @text).strip
|
||||
File.read(filename)
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('import_js', TagImportJs)
|
||||
|
35
devsite/plugins/tag_markdown.rb
Normal file
35
devsite/plugins/tag_markdown.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
module Jekyll
|
||||
class MarkdownBlock < Liquid::Block
|
||||
alias_method :render_block, :render
|
||||
|
||||
def initialize(tag_name, markup, tokens)
|
||||
super
|
||||
end
|
||||
|
||||
# Uses the default Jekyll markdown parser to
|
||||
# parse the contents of this block
|
||||
#
|
||||
def render(context)
|
||||
site = context.registers[:site]
|
||||
converter = site.find_converter_instance(::Jekyll::Converters::Markdown)
|
||||
converter.convert(render_block(context))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('markdown', Jekyll::MarkdownBlock)
|
||||
|
39
devsite/plugins/tag_platform.rb
Normal file
39
devsite/plugins/tag_platform.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
module Jekyll
|
||||
class PlatformBlock < Liquid::Block
|
||||
alias_method :render_block, :render
|
||||
|
||||
def initialize(tag_name, text, tokens)
|
||||
super
|
||||
@platform = text.strip
|
||||
end
|
||||
|
||||
def render(context)
|
||||
if (@platform == "local") || (@platform == "cloudpebble")
|
||||
site = context.registers[:site]
|
||||
converter = site.find_converter_instance(::Jekyll::Converters::Markdown)
|
||||
content = converter.convert(super)
|
||||
|
||||
return "<div class=\"platform-specific\" data-sdk-platform=\"#{@platform}\">#{content}</div>"
|
||||
end
|
||||
|
||||
Jekyll.logger.error "Liquid Error:", "Platform '#{@platform}' is not valid. Use 'local' or 'cloudpebble'."
|
||||
return ''
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('platform', Jekyll::PlatformBlock)
|
50
devsite/plugins/tag_screenshot_viewer.rb
Normal file
50
devsite/plugins/tag_screenshot_viewer.rb
Normal file
|
@ -0,0 +1,50 @@
|
|||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'json'
|
||||
|
||||
class TagScreenshotViewer < Liquid::Block
|
||||
def render(context)
|
||||
site = context.registers[:site]
|
||||
data = JSON.parse(super)
|
||||
|
||||
viewer_html = '<div class="screenshot-viewer">'
|
||||
|
||||
viewer_html += '<div class="screenshot-viewer__tabs js-screenshot-tabs">'
|
||||
data['platforms'].each do |platform|
|
||||
viewer_html += "<h4 data-platform=\"#{platform['hw']}\">#{platform['hw']}</h4>"
|
||||
end
|
||||
viewer_html += '</div>'
|
||||
|
||||
viewer_html += '<div class="screenshot-viewer__screenshots">'
|
||||
data['platforms'].each do |platform|
|
||||
viewer_html += "<div class=\"screenshot-viewer__platform\" data-platform=\"#{platform['hw']}\">"
|
||||
image_url = make_image_url(data, platform)
|
||||
viewer_html += "<img src=\"#{site.config['asset_path']}#{image_url}\" class=\"pebble-screenshot pebble-screenshot--#{platform['wrapper']}\" />"
|
||||
viewer_html += '</div>'
|
||||
end
|
||||
viewer_html += '</div>'
|
||||
|
||||
viewer_html += '</div>'
|
||||
viewer_html
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def make_image_url(data, platform)
|
||||
File.dirname(data['image']) + '/' + File.basename(data['image'], File.extname(data['image'])) +
|
||||
"~#{platform['hw']}" + File.extname(data['image'])
|
||||
end
|
||||
end
|
||||
Liquid::Template.register_tag('screenshot_viewer', TagScreenshotViewer)
|
Loading…
Add table
Add a link
Reference in a new issue