doing 1.0.93 → 2.0.6.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHORS +19 -0
  3. data/CHANGELOG.md +616 -0
  4. data/COMMANDS.md +1181 -0
  5. data/Gemfile +2 -0
  6. data/Gemfile.lock +110 -0
  7. data/LICENSE +23 -0
  8. data/README.md +15 -699
  9. data/Rakefile +79 -0
  10. data/_config.yml +1 -0
  11. data/bin/doing +1055 -494
  12. data/doing.gemspec +34 -0
  13. data/doing.rdoc +1839 -0
  14. data/example_plugin.rb +209 -0
  15. data/generate_completions.sh +5 -0
  16. data/img/doing-colors.jpg +0 -0
  17. data/img/doing-printf-wrap-800.jpg +0 -0
  18. data/img/doing-show-note-formatting-800.jpg +0 -0
  19. data/lib/completion/_doing.zsh +203 -0
  20. data/lib/completion/doing.bash +449 -0
  21. data/lib/completion/doing.fish +329 -0
  22. data/lib/doing/array.rb +8 -0
  23. data/lib/doing/cli_status.rb +70 -0
  24. data/lib/doing/colors.rb +136 -0
  25. data/lib/doing/configuration.rb +312 -0
  26. data/lib/doing/errors.rb +109 -0
  27. data/lib/doing/hash.rb +31 -0
  28. data/lib/doing/hooks.rb +59 -0
  29. data/lib/doing/item.rb +155 -0
  30. data/lib/doing/log_adapter.rb +344 -0
  31. data/lib/doing/markdown_document_listener.rb +174 -0
  32. data/lib/doing/note.rb +59 -0
  33. data/lib/doing/pager.rb +95 -0
  34. data/lib/doing/plugin_manager.rb +208 -0
  35. data/lib/doing/plugins/export/csv_export.rb +48 -0
  36. data/lib/doing/plugins/export/html_export.rb +83 -0
  37. data/lib/doing/plugins/export/json_export.rb +140 -0
  38. data/lib/doing/plugins/export/markdown_export.rb +85 -0
  39. data/lib/doing/plugins/export/taskpaper_export.rb +34 -0
  40. data/lib/doing/plugins/export/template_export.rb +141 -0
  41. data/lib/doing/plugins/import/cal_to_json.scpt +0 -0
  42. data/lib/doing/plugins/import/calendar_import.rb +76 -0
  43. data/lib/doing/plugins/import/doing_import.rb +144 -0
  44. data/lib/doing/plugins/import/timing_import.rb +78 -0
  45. data/lib/doing/string.rb +348 -0
  46. data/lib/doing/symbol.rb +16 -0
  47. data/lib/doing/time.rb +18 -0
  48. data/lib/doing/util.rb +186 -0
  49. data/lib/doing/version.rb +1 -1
  50. data/lib/doing/wwid.rb +1868 -2349
  51. data/lib/doing/wwidfile.rb +117 -0
  52. data/lib/doing.rb +43 -3
  53. data/lib/examples/commands/autotag.rb +63 -0
  54. data/lib/examples/commands/wiki.rb +81 -0
  55. data/lib/examples/plugins/hooks.rb +22 -0
  56. data/lib/examples/plugins/say_export.rb +202 -0
  57. data/lib/examples/plugins/templates/wiki.css +169 -0
  58. data/lib/examples/plugins/templates/wiki.haml +27 -0
  59. data/lib/examples/plugins/templates/wiki_index.haml +18 -0
  60. data/lib/examples/plugins/wiki_export.rb +87 -0
  61. data/lib/templates/doing-markdown.erb +5 -0
  62. data/man/doing.1 +964 -0
  63. data/man/doing.1.html +711 -0
  64. data/man/doing.1.ronn +600 -0
  65. data/package-lock.json +3 -0
  66. data/rdoc_to_mmd.rb +42 -0
  67. data/rdocfixer.rb +13 -0
  68. data/scripts/generate_bash_completions.rb +211 -0
  69. data/scripts/generate_fish_completions.rb +204 -0
  70. data/scripts/generate_zsh_completions.rb +168 -0
  71. metadata +82 -7
  72. data/lib/doing/helpers.rb +0 -191
  73. data/lib/doing/markdown_export.rb +0 -16
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doing
4
+ class Items < Array
5
+ def from(path)
6
+ return [] unless path
7
+ path = File.expand_path(path)
8
+
9
+ if File.exist?(path) && File.file?(path) && File.stat(path).size.positive?
10
+ input = IO.read(File.expand_path(input))
11
+ input = input.force_encoding('utf-8') if input.respond_to? :force_encoding
12
+ end
13
+
14
+ section = 'Uncategorized'
15
+ lines = input.split(/[\n\r]/)
16
+ current = 0
17
+
18
+ lines.each do |line|
19
+ next if line =~ /^\s*$/
20
+
21
+ if line =~ /^(\S[\S ]+):\s*(@\S+\s*)*$/
22
+ section = Regexp.last_match(1)
23
+ @sections << { original: line, title: section }
24
+ current = 0
25
+ elsif line =~ /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*)/
26
+ date = Regexp.last_match(1).strip
27
+ title = Regexp.last_match(2).strip
28
+ item = Item.new(date, title, section)
29
+ @items.push(item)
30
+ current += 1
31
+ elsif current.zero?
32
+ # if content[section][:items].length - 1 == current
33
+ @other_content_top.push(line)
34
+ elsif line =~ /^\S/
35
+ @other_content_bottom.push(line)
36
+ else
37
+ prev_item = @items[current - 1]
38
+ prev_item.note = Note.new unless prev_item.note
39
+
40
+ prev_item.note.add(line)
41
+ # end
42
+ end
43
+ end
44
+ Hooks.trigger :post_read, self
45
+ end
46
+
47
+ def section_titles
48
+ @sections.map { |s| s[:title] }
49
+ end
50
+
51
+ ##
52
+ ## @brief Adds a section.
53
+ ##
54
+ ## @param title (String) The new section title
55
+ ##
56
+ def add_section(title)
57
+ if section_titles.include?(title.cap_first)
58
+ Doing.logger.debug('Skipped': 'Section already exists')
59
+ return
60
+ end
61
+
62
+ @sections << { original: "#{title}:", title: title }
63
+ Doing.logger.info('Added section:', %("#{title.cap_first}"))
64
+ end
65
+
66
+ ##
67
+ ## @brief Attempt to match a string with an existing section
68
+ ##
69
+ ## @param frag (String) The user-provided string
70
+ ## @param guessed (Boolean) already guessed and failed
71
+ ##
72
+ def guess_section(frag, guessed: false, suggest: false)
73
+ return 'All' if frag =~ /^all$/i
74
+ frag ||= wwid.config['current_section']
75
+
76
+ @sections.each { |sect| return sect[:title].cap_first if frag.downcase == sect[:title].downcase }
77
+
78
+ section = false
79
+ re = frag.split('').join('.*?')
80
+ sections.each do |sect|
81
+ next unless sect =~ /#{re}/i
82
+
83
+ Doing.logger.debug('Section match:', %(Assuming "#{sect}" from "#{frag}"))
84
+ section = sect
85
+ break
86
+ end
87
+
88
+ return section if suggest
89
+
90
+ unless section || guessed
91
+ alt = WWID.guess_view(frag, guessed: true, suggest: true)
92
+ if alt
93
+ meant_view = WWID.yn("Did you mean `doing view #{alt}`?", default_response: 'n')
94
+ raise Errors::InvalidSection, "Run again with `doing view #{alt}`" if meant_view
95
+ end
96
+
97
+ res = WWID.yn("Section #{frag} not found, create it", default_response: 'n')
98
+
99
+ if res
100
+ add_section(frag.cap_first)
101
+ WWID.write(@doing_file)
102
+ return frag.cap_first
103
+ end
104
+
105
+ raise Errors::InvalidSection, "Unknown section: #{frag}"
106
+ end
107
+ section ? section.cap_first : guessed
108
+ end
109
+
110
+ def section_items(section)
111
+ section = guess_section(section)
112
+ return @items if section =~ /all/i
113
+
114
+ @items.filter { |i| i.section == section }
115
+ end
116
+ end
117
+ end
data/lib/doing.rb CHANGED
@@ -1,4 +1,6 @@
1
- require 'doing/version.rb'
1
+ # frozen_string_literal: true
2
+
3
+ require 'doing/version'
2
4
  require 'time'
3
5
  require 'date'
4
6
  require 'yaml'
@@ -8,7 +10,45 @@ require 'tempfile'
8
10
  require 'chronic'
9
11
  require 'haml'
10
12
  require 'json'
11
- require 'doing/helpers'
12
- require 'doing/markdown_export'
13
+ require 'logger'
14
+ require 'safe_yaml/load'
15
+ require 'doing/hash'
16
+ require 'doing/colors'
17
+ require 'doing/string'
18
+ require 'doing/time'
19
+ require 'doing/array'
20
+ require 'doing/symbol'
21
+ require 'doing/util'
22
+ require 'doing/configuration'
23
+ require 'doing/item'
24
+ require 'doing/note'
25
+ require 'doing/wwidfile'
13
26
  require 'doing/wwid'
27
+ require 'doing/log_adapter'
28
+ require 'doing/errors'
29
+ require 'doing/hooks'
30
+ require 'doing/plugin_manager'
31
+ require 'doing/pager'
14
32
  # require 'doing/markdown_document_listener'
33
+
34
+ # Main doing module
35
+ module Doing
36
+ class << self
37
+ #
38
+ # @brief Fetch the logger
39
+ #
40
+ # @return the LogAdapter instance.
41
+ #
42
+ def logger
43
+ @logger ||= LogAdapter.new((ENV['DOING_LOG_LEVEL'] || :info).to_sym)
44
+ end
45
+
46
+ def config
47
+ @config ||= Configuration.new
48
+ end
49
+
50
+ def config_with(file, options = {})
51
+ @config = Configuration.new(file, options: options)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example command that calls an existing command (tag) with
4
+ # preset options
5
+ desc 'Autotag last entry or filtered entries'
6
+ command :autotag do |c|
7
+ # Preserve some switches and flags. Values will be passed
8
+ # to tag command.
9
+ c.desc 'Section'
10
+ c.arg_name 'SECTION_NAME'
11
+ c.flag %i[s section], default_value: 'All'
12
+
13
+ c.desc 'How many recent entries to autotag (0 for all)'
14
+ c.arg_name 'COUNT'
15
+ c.flag %i[c count], default_value: 1, must_match: /^\d+$/, type: Integer
16
+
17
+ c.desc 'Don\'t ask permission to autotag all entries when count is 0'
18
+ c.switch %i[force], negatable: false, default_value: false
19
+
20
+ c.desc 'Autotag last entry (or entries) not marked @done'
21
+ c.switch %i[u unfinished], negatable: false, default_value: false
22
+
23
+ c.desc 'Autotag the last X entries containing TAG.
24
+ Separate multiple tags with comma (--tag=tag1,tag2), combine with --bool'
25
+ c.arg_name 'TAG'
26
+ c.flag [:tag]
27
+
28
+ c.desc 'Autotag entries matching search filter,
29
+ surround with slashes for regex (e.g. "/query.*/"),
30
+ start with single quote for exact match ("\'query")'
31
+ c.arg_name 'QUERY'
32
+ c.flag [:search]
33
+
34
+ c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
35
+ c.arg_name 'BOOLEAN'
36
+ c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
37
+
38
+ c.desc 'Select item(s) to tag from a menu of matching entries'
39
+ c.switch %i[i interactive], negatable: false, default_value: false
40
+
41
+ c.action do |global, options, _args|
42
+ # Force some switches and flags. We're using the tag
43
+ # command with settings that would invoke autotagging.
44
+
45
+ # Force enable autotag
46
+ options[:a] = true
47
+ options[:autotag] = true
48
+
49
+ # No need for date values
50
+ options[:d] = false
51
+ options[:date] = false
52
+
53
+ # Don't remove any tags
54
+ options[:rename] = nil
55
+ options[:regex] = false
56
+ options[:r] = false
57
+ options[:remove] = false
58
+
59
+ cmd = commands[:tag]
60
+ action = cmd.send(:get_action, nil)
61
+ action.call(global, options, [])
62
+ end
63
+ end
@@ -0,0 +1,81 @@
1
+ desc 'Output a tag wiki'
2
+ command :wiki do |c|
3
+ c.desc 'Section to rotate'
4
+ c.arg_name 'SECTION_NAME'
5
+ c.flag %i[s section], default_value: 'All'
6
+
7
+ c.desc 'Tag filter, combine multiple tags with a comma, use with --bool'
8
+ c.arg_name 'TAG'
9
+ c.flag [:tag]
10
+
11
+ c.desc 'Tag boolean (AND,OR,NOT)'
12
+ c.arg_name 'BOOLEAN'
13
+ c.flag %i[b bool], must_match: REGEX_BOOL, default_value: 'OR'
14
+
15
+ c.desc 'Include entries older than date'
16
+ c.arg_name 'DATE_STRING'
17
+ c.flag [:before]
18
+
19
+ c.desc 'Include entries newer than date'
20
+ c.arg_name 'DATE_STRING'
21
+ c.flag [:after]
22
+
23
+ c.desc 'Search filter, surround with slashes for regex (/query/), start with single quote for exact match ("\'query")'
24
+ c.arg_name 'QUERY'
25
+ c.flag [:search]
26
+
27
+ c.desc %(
28
+ Date range to include, or a single day to filter date on.
29
+ Date range argument should be quoted. Date specifications can be natural language.
30
+ To specify a range, use "to" or "through": `doing show --from "monday to friday"`
31
+ )
32
+ c.arg_name 'DATE_OR_RANGE'
33
+ c.flag %i[f from]
34
+
35
+ c.desc 'Only show items with recorded time intervals'
36
+ c.switch [:only_timed], default_value: false, negatable: false
37
+
38
+ c.action do |global, options, args|
39
+ wwid = global[:wwid]
40
+ tags = wwid.tag_groups([], opt: options)
41
+
42
+ wiki = Doing::Plugins.plugins.dig(:export, 'wiki', :class)
43
+
44
+ tags.each do |tag, items|
45
+ export_options = { page_title: tag, is_single: false, options: options }
46
+
47
+ raise RuntimeError, 'Missing plugin "wiki"' unless wiki
48
+
49
+ out = wiki.render(wwid, items, variables: export_options)
50
+
51
+ if out
52
+ FileUtils.mkdir_p('doing_wiki')
53
+ File.open(File.join('doing_wiki', tag + '.html'), 'w') do |f|
54
+ f.puts out
55
+ end
56
+ end
57
+ end
58
+
59
+ template = if wwid.config['export_templates']['wiki_index'] && File.exist?(File.expand_path(wwid.config['export_templates']['wiki_index']))
60
+ IO.read(File.expand_path(wwid.config['export_templates']['wiki_index']))
61
+ else
62
+ wiki.template('wiki_index')
63
+ end
64
+ style = if wwid.config['export_templates']['wiki_css'] && File.exist?(File.expand_path(wwid.config['export_templates']['wiki_css']))
65
+ IO.read(File.expand_path(wwid.config['export_templates']['wiki_css']))
66
+ else
67
+ wiki.template('wiki_css')
68
+ end
69
+ tags_out = tags.map { |t| {url: "#{t}.html"} }
70
+ engine = Haml::Engine.new(template)
71
+ index_out = engine.render(Object.new,
72
+ { :@tags => tags.each_with_object([]) { |(tag, items), arr| arr << { name: tag, count: items.count } }, :@page_title => "Tags wiki", :@style => style })
73
+
74
+ if index_out
75
+ File.open(File.join('doing_wiki', 'index.html'), 'w') do |f|
76
+ f.puts index_out
77
+ end
78
+ Doing.logger.warn("Wiki written to doing_wiki directory")
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doing
4
+ # Hooks.register :post_config do |wwid|
5
+ # wwid.config['twizzle'] = 'Fo shizzle'
6
+ # wwid.write_config(File.expand_path('~/Desktop/wwidconfig.yml'))
7
+ # end
8
+
9
+ # Hooks.register :post_read, priority: 10 do |wwid|
10
+ # Doing.logger.warn('Hook 1:', 'triggered priority 10')
11
+ # Doing.logger.warn('Hook 2:', wwid.config['twizzle'])
12
+ # end
13
+
14
+ # Hooks.register :post_read, priority: 100 do |wwid|
15
+ # Doing.logger.warn('Hook 2:', 'triggered priority 100')
16
+ # end
17
+
18
+ Hooks.register :post_write do |filename|
19
+ res = `/bin/bash /Users/ttscoff/scripts/after_doing.sh`.strip
20
+ Doing.logger.debug('Hooks:', res) unless res =~ /^\.\.\.$/
21
+ end
22
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ # title: Export plugin example
4
+ # description: Speak the most recent entry (macOS)
5
+ # author: Brett Terpstra
6
+ # url: https://brettterpstra.com
7
+
8
+ # Example
9
+ #
10
+ # doing show -o sayit
11
+ #
12
+ # ## Configuration
13
+ #
14
+ # Change what the plugin says by generating a template with
15
+ # `doing template --type say`, saving it to a file, and
16
+ # putting the path to that file in `export_templates->say` in
17
+ # .doingrc.
18
+ #
19
+ # export_templates:
20
+ # say: /path/to/template.txt
21
+ #
22
+ # Use a different voice by adding a `say_voice` key to your
23
+ # .doingrc. Use `say -v ?` to see available voices.
24
+ #
25
+ # say_voice: Zarvox
26
+
27
+ module Doing
28
+ ##
29
+ ## @brief Plugin class
30
+ ##
31
+ class SayExport
32
+ include Doing::Util
33
+
34
+ #-------------------------------------------------------
35
+ ## Plugin Settings. A plugin must have a self.settings
36
+ ## method that returns a hash with plugin settings.
37
+ ##
38
+ ## trigger: (required) Regular expression to match
39
+ ## FORMAT when used with `--output FORMAT`. Registered
40
+ ## name of plugin must be able to match the trigger, but
41
+ ## alternatives can be included
42
+ ##
43
+ ## templates: (optional) Array of templates this plugin
44
+ ## can export (plugin must have :template method)
45
+ ##
46
+ ## Each template is a hash containing:
47
+ ## - name: display name for template
48
+ ## - trigger: regular expression for
49
+ ## `template --type FORMAT`
50
+ ##
51
+ ## If a template is included, a config key will
52
+ ## automatically be added for the user to override
53
+ ## The config key will be available at:
54
+ ##
55
+ ## wwid.config['export_templates'][PLUGIN_NAME]
56
+ ##
57
+ ## config: (optional) A Hash which will be
58
+ ## added to the main configuration in the plugins section.
59
+ ## Options defined here are included when config file is
60
+ ## created or updated with `config --update`. Use this to
61
+ ## add new configuration keys, not to override existing
62
+ ## ones.
63
+ ##
64
+ ## The configuration keys will be available at:
65
+ ##
66
+ ## wwid.config['plugins'][PLUGIN_NAME][KEY]
67
+ ##
68
+ ## @brief Method to return plugin settings (required)
69
+ ##
70
+ ## @return Hash of settings for this plugin
71
+ ##
72
+ def self.settings
73
+ {
74
+ trigger: 'say(?:it)?',
75
+ templates: [
76
+ { name: 'say', trigger: 'say(?:it)?' }
77
+ ],
78
+ config: {
79
+ 'say_voice' => 'Fiona'
80
+ }
81
+ }
82
+ end
83
+
84
+
85
+ #-------------------------------------------------------
86
+ ## Output a template. Only required if template(s) are
87
+ ## included in settings. The method should return a
88
+ ## string (not output it to the STDOUT).
89
+ ##
90
+ ## @brief Method to return template (optional)
91
+ ##
92
+ ## @param trigger The trigger passed to the
93
+ ## template function. When this
94
+ ## method defines multiple
95
+ ## templates, the trigger can be
96
+ ## used to determine which one is
97
+ ## output.
98
+ ##
99
+ ## @return (String) template contents
100
+ ##
101
+ def self.template(trigger)
102
+ return unless trigger =~ /^say(it)?$/
103
+ 'On %date, you were %title, recorded in section %section%took'
104
+ end
105
+
106
+
107
+ ##
108
+ ## @brief Render data received from an output
109
+ ## command
110
+ ##
111
+ ## @param wwid The wwid object with config
112
+ ## and public methods
113
+ ## @param items An array of items to be output
114
+ ## { <Date>date, <String>title,
115
+ ## <String>section, <Array>note }
116
+ ## @param variables Additional variables including
117
+ ## flags passed to command
118
+ ## (variables[:options])
119
+ ##
120
+ ## @return (String) Rendered output
121
+ ##
122
+ def self.render(wwid, items, variables: {})
123
+ return if items.nil? || items.empty?
124
+
125
+ # the :options key includes the flags passed to the
126
+ # command that called the plugin use `puts
127
+ # variables.inspect` to see properties and methods
128
+ # when run
129
+ opt = variables[:options]
130
+
131
+ # This plugin just grabs the last item in the `items`
132
+ # list (which could be the oldest or newest, depending
133
+ # on the sort order of the command that called the
134
+ # plugin). Most of the time you'll want to use :each
135
+ # or :map to generate output.
136
+ i = items[-1]
137
+
138
+ # Format the item. Items are a hash with 3 keys: date,
139
+ # title, and section (parent section) Start time is in
140
+ # item.date. The wwid object has some methods for
141
+ # calculation and formatting, including
142
+ # wwid.item.end_date to convert the @done
143
+ # timestamp to an end date.
144
+ if opt[:times]
145
+ interval = i.interval
146
+
147
+ if interval
148
+ took = '. You finished on '
149
+ finished_at = i.end_date
150
+ took += finished_at.strftime('%A %B %e at %I:%M%p')
151
+
152
+ d, h, m = wwid.format_time(interval)
153
+ took += ' and it took'
154
+ took += " #{d.to_i} days" if d.to_i.positive?
155
+ took += " #{h.to_i} hours" if h.to_i.positive?
156
+ took += " #{m.to_i} minutes" if m.to_i.positive?
157
+ end
158
+ end
159
+
160
+ date = i.date.strftime('%A %B %e at %I:%M%p')
161
+ title = i.title.gsub(/ @done\(.*?\)/, '').gsub(/@/, 'hashtag ')
162
+ tpl = template('say')
163
+
164
+ if wwid.config['export_templates'].key?('say')
165
+ cfg_tpl = wwid.config['export_templates']['say']
166
+ tpl = cfg_tpl unless cfg_tpl.nil? || cfg_tpl.empty?
167
+ end
168
+ output = tpl.dup
169
+ output.gsub!(/%date/, date)
170
+ output.gsub!(/%title/, title)
171
+ output.gsub!(/%section/, i.section)
172
+ output.gsub!(/%took/, took || '')
173
+
174
+ # Debugging output
175
+ # warn "Saying: #{output}"
176
+
177
+ # To provide results on the command line after the
178
+ # command runs, add to the wwid.results array. Results
179
+ # are provided on STDERR unless doing is run with
180
+ # `--stdout`
181
+ Doing.logger.info('Spoke the last entry. Did you hear it?')
182
+
183
+ # This export runs a command for fun, most plugins won't
184
+ voice = wwid.config['plugins']['say']['say_voice'] || 'Alex'
185
+ `say -v "#{voice}" "#{output}"`
186
+
187
+ # Return the result (don't output to terminal with puts or print)
188
+ output
189
+ end
190
+
191
+ # Register the plugin with doing.
192
+ # Doing::Plugins.register 'NAME', TYPE, Class
193
+ #
194
+ # Name should be lowercase, no spaces
195
+ #
196
+ # TYPE is :import or :export
197
+ #
198
+ # Class is the plugin class (e.g. Doing::SayExport), or
199
+ # self if called within the class
200
+ Doing::Plugins.register 'say', :export, self
201
+ end
202
+ end
@@ -0,0 +1,169 @@
1
+ body {
2
+ background: #fff;
3
+ color: #333;
4
+ font-family: Helvetica,arial,freesans,clean,sans-serif;
5
+ font-size: 21px;
6
+ line-height: 1.5;
7
+ text-align: justify;
8
+ }
9
+
10
+ @media only screen and (max-width: 900px) {
11
+ body {
12
+ font-size: calc(12px + 1vw);
13
+ }
14
+
15
+ .date,
16
+ .note {
17
+ font-size: calc(8px + 1vw)!important;
18
+ }
19
+ }
20
+
21
+ h1 {
22
+ margin-bottom: 1em;
23
+ margin-left: .1em;
24
+ position: relative;
25
+ text-align: left;
26
+ }
27
+
28
+ ul {
29
+ list-style-position: outside;
30
+ position: relative;
31
+ text-align: left;
32
+ padding-left: 0;
33
+ }
34
+
35
+ article > ul > li {
36
+ display: grid;
37
+ grid-template-columns: 14ch auto;
38
+ line-height: 1.2;
39
+ list-style-type: none;
40
+ padding-left: 10px;
41
+ position: relative;
42
+ word-break: break-word;
43
+ transition: background .2s ease-in-out;
44
+ }
45
+
46
+ article > ul > li:hover {
47
+ background: rgba(150,150,150,.05);
48
+ }
49
+
50
+ .date {
51
+ color: #7d9ca2;
52
+ font-size: 17px;
53
+ padding: 15px 1ch 0 0;
54
+ text-align: right;
55
+ white-space: nowrap;
56
+ transition: color .2s ease-in-out;
57
+ }
58
+
59
+ .entry {
60
+ border-left: solid 1px #ccc;
61
+ line-height: 1.2;
62
+ padding: 10px 10px 10px 3ch;
63
+ text-indent: -2ch;
64
+ }
65
+
66
+ .tag {
67
+ color: #999;
68
+ transition: color 1s ease-in;
69
+ }
70
+
71
+ .note {
72
+ color: #aaa;
73
+ display: block;
74
+ font-size: 17px;
75
+ line-height: 1.1;
76
+ padding: 1em 0 0 2ch;
77
+ position: relative;
78
+ transition: color .2s ease-in-out;
79
+ }
80
+
81
+ li:hover .note {
82
+ color: #777;
83
+ }
84
+
85
+ li:hover .tag {
86
+ color: rgb(182, 120, 125);
87
+ }
88
+
89
+ li:hover .date {
90
+ color: rgb(100, 169, 165);
91
+ }
92
+
93
+ .note li {
94
+ margin-bottom: .5em;
95
+ list-style: none;
96
+ position: relative;
97
+ }
98
+
99
+ .note li:before {
100
+ color: #ddd;
101
+ content: '\25BA';
102
+ font-size: 12px;
103
+ font-weight: 300;
104
+ left: -3ch;
105
+ position: absolute;
106
+ top: .25em;
107
+ }
108
+
109
+ .time {
110
+ background: #f9fced;
111
+ border-bottom: dashed 1px #ccc;
112
+ color: #729953;
113
+ font-size: 15px;
114
+ margin-right: 4px;
115
+ padding: 0 5px;
116
+ position: relative;
117
+ text-align: right;
118
+ }
119
+
120
+ table td {
121
+ border-bottom: solid 1px #ddd;
122
+ height: 24px;
123
+ }
124
+
125
+ caption {
126
+ border-bottom: solid 1px #aaa;
127
+ margin: 10px 0;
128
+ text-align: left;
129
+ }
130
+
131
+ table {
132
+ margin: 50px 0 0 211px;
133
+ width: 400px;
134
+ }
135
+
136
+ th {
137
+ padding-bottom: 10px;
138
+ }
139
+
140
+ th, td {
141
+ padding-right: 20px;
142
+ }
143
+
144
+ table {
145
+ margin: 50px 0 2em 16ch;
146
+ max-width: 400px;
147
+ }
148
+
149
+ .section {
150
+ border-left: solid 1px rgb(182, 120, 125);
151
+ border-radius: 25px;
152
+ border-right: solid 1px rgb(182, 120, 125);
153
+ color: rgb(182, 120, 125);
154
+ font-size: .8em;
155
+ line-height: 1 !important;
156
+ padding: 0 4px;
157
+ transition: background .4s ease-in, color .4s ease-in;
158
+ }
159
+
160
+ li:hover .section {
161
+ color: #fff;
162
+ background: rgb(182, 120, 125);
163
+ }
164
+
165
+ a:link {
166
+ background-color: rgba(203, 255, 251, .15);
167
+ color: #64a9a5;
168
+ text-decoration: none;
169
+ }