From a860b0c0635500e628f91d26082a5df477f454e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20=C3=96qvist?= <jesper@llbit.se> Date: Tue, 25 Apr 2017 23:25:55 +0200 Subject: [PATCH] Basic JastAdd Docs This is an Angular 4.0 application for viewing JastAdd documentation. CodeMirror is used to display JastAdd source code. --- .angular-cli.json | 8 +- .gitignore | 2 + README.md | 4 +- TODO | 5 + package.json | 3 +- src/app/app.component.css | 118 +++++++++++++++++ src/app/app.component.html | 3 - src/app/app.component.spec.ts | 45 ++++--- src/app/app.component.ts | 98 +++++++++++++- src/app/app.module.ts | 44 ++++-- .../declared-at/declared-at.component.spec.ts | 25 ++++ src/app/declared-at/declared-at.component.ts | 26 ++++ src/app/doc.ts | 26 ++++ src/app/editor.directive.spec.ts | 8 ++ src/app/editor.directive.ts | 35 +++++ src/app/inherited-members.ts | 13 ++ src/app/member-filter.service.spec.ts | 15 +++ src/app/member-filter.service.ts | 15 +++ src/app/member.ts | 40 ++++++ src/app/name-filter.pipe.ts | 15 +++ src/app/package-entry.ts | 11 ++ src/app/package.service.ts | 20 +++ src/app/package.ts | 12 ++ src/app/parameter.ts | 14 ++ src/app/parameters.component.ts | 34 +++++ src/app/selection.service.ts | 13 ++ src/app/source-view/source-view.component.css | 0 .../source-view/source-view.component.html | 3 + .../source-view/source-view.component.spec.ts | 25 ++++ src/app/source-view/source-view.component.ts | 30 +++++ src/app/source.service.spec.ts | 15 +++ src/app/source.service.ts | 17 +++ src/app/string-filter.pipe.spec.ts | 8 ++ src/app/string-filter.pipe.ts | 15 +++ src/app/type-details.component.css | 83 ++++++++++++ src/app/type-details.component.html | 125 ++++++++++++++++++ src/app/type-details.component.ts | 81 ++++++++++++ src/app/type-ref.component.ts | 48 +++++++ src/app/type-ref.ts | 54 ++++++++ src/app/type.service.ts | 20 +++ src/app/type.ts | 69 ++++++++++ src/assets/arrow_down_24px.svg | 66 +++++++++ src/assets/hamburger_24px.svg | 81 ++++++++++++ src/assets/hdots_24px.svg | 82 ++++++++++++ src/assets/search_grey_24px.svg | 65 +++++++++ src/assets/search_white_24px.svg | 65 +++++++++ src/assets/vdots_24px.svg | 82 ++++++++++++ src/favicon.ico | Bin 5430 -> 1150 bytes src/index.html | 3 +- src/styles.css | 6 + 50 files changed, 1652 insertions(+), 43 deletions(-) create mode 100644 TODO delete mode 100644 src/app/app.component.html create mode 100644 src/app/declared-at/declared-at.component.spec.ts create mode 100644 src/app/declared-at/declared-at.component.ts create mode 100644 src/app/doc.ts create mode 100644 src/app/editor.directive.spec.ts create mode 100644 src/app/editor.directive.ts create mode 100644 src/app/inherited-members.ts create mode 100644 src/app/member-filter.service.spec.ts create mode 100644 src/app/member-filter.service.ts create mode 100644 src/app/member.ts create mode 100644 src/app/name-filter.pipe.ts create mode 100644 src/app/package-entry.ts create mode 100644 src/app/package.service.ts create mode 100644 src/app/package.ts create mode 100644 src/app/parameter.ts create mode 100644 src/app/parameters.component.ts create mode 100644 src/app/selection.service.ts create mode 100644 src/app/source-view/source-view.component.css create mode 100644 src/app/source-view/source-view.component.html create mode 100644 src/app/source-view/source-view.component.spec.ts create mode 100644 src/app/source-view/source-view.component.ts create mode 100644 src/app/source.service.spec.ts create mode 100644 src/app/source.service.ts create mode 100644 src/app/string-filter.pipe.spec.ts create mode 100644 src/app/string-filter.pipe.ts create mode 100644 src/app/type-details.component.css create mode 100644 src/app/type-details.component.html create mode 100644 src/app/type-details.component.ts create mode 100644 src/app/type-ref.component.ts create mode 100644 src/app/type-ref.ts create mode 100644 src/app/type.service.ts create mode 100644 src/app/type.ts create mode 100644 src/assets/arrow_down_24px.svg create mode 100644 src/assets/hamburger_24px.svg create mode 100644 src/assets/hdots_24px.svg create mode 100644 src/assets/search_grey_24px.svg create mode 100644 src/assets/search_white_24px.svg create mode 100644 src/assets/vdots_24px.svg diff --git a/.angular-cli.json b/.angular-cli.json index 98a7d80..9bb6f94 100644 --- a/.angular-cli.json +++ b/.angular-cli.json @@ -8,6 +8,7 @@ "root": "src", "outDir": "dist", "assets": [ + "data", "assets", "favicon.ico" ], @@ -19,9 +20,12 @@ "testTsconfig": "tsconfig.spec.json", "prefix": "app", "styles": [ - "styles.css" + "styles.css", + "../node_modules/codemirror/lib/codemirror.css", + "../node_modules/codemirror/theme/mbo.css" + ], + "scripts": [ ], - "scripts": [], "environmentSource": "environments/environment.ts", "environments": { "dev": "environments/environment.ts", diff --git a/.gitignore b/.gitignore index 54bfd20..5198bf6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. +/src/data/ + # compiled output /dist /tmp diff --git a/README.md b/README.md index 45d47d7..397cf56 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# RdView +# RagDoc View + +This is a viewer for JastAdd documentation generated by RagDoc Builder. This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.1. diff --git a/TODO b/TODO new file mode 100644 index 0000000..56b233b --- /dev/null +++ b/TODO @@ -0,0 +1,5 @@ +* add thrown type descriptions +* add type hierarchy in type details +* add structured production representation +* make current member filter more noticeable +* Add declared-at info for types. diff --git a/package.json b/package.json index 7db8573..e19d786 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "@angular/router": "^4.0.0", "core-js": "^2.4.1", "rxjs": "^5.1.0", - "zone.js": "^0.8.4" + "zone.js": "^0.8.4", + "codemirror": "5.25.2" }, "devDependencies": { "@angular/cli": "1.0.1", diff --git a/src/app/app.component.css b/src/app/app.component.css index e69de29..fec4f10 100644 --- a/src/app/app.component.css +++ b/src/app/app.component.css @@ -0,0 +1,118 @@ +nav, article, h1 { + font-family: "Roboto",Helvetica,sans-serif; +} +.selected { + background-color: #BBD8DC !important; +} +.types { + list-style-type: none; + margin: 0; + padding: 0; + background-color: #607D8B; +} +.types li { + cursor: pointer; + position: relative; + left: 0; + background-color: #EEE; + margin: 1px 0; + padding: .3em 0 0 .7em; + height: 1.6em; +} +.types li:hover { + background-color: #CFD8DC; +} +.type .text { + position: relative; + top: -3px; +} +.topnav { + z-index: 5; + position: fixed; + top: 0; + left: 0; + right: 0; + box-sizing: border-box; + height: 65px; + background-color: #CFD8DC; + box-shadow: 0 0 10px grey; +} +.topnav img { + visibility: hidden; + display: inline-block; + box-sizing: border-box; + width: 65px; + height: 65px; + padding: 15px; + position: absolute; +} +.topnav h1 { + display: inline-block; + vertical-align: middle; + box-sizing: border-box; + line-height: 65px; + padding: 0; + margin: 0 0 0 1em; + pointer-events: none; + white-space: nowrap; + overflow: hidden; +} +.sidenav { + width: 16em; + overflow-y: auto; + overflow-x: hidden; + z-index: 4; + position: fixed; + top: 65px; + left: 0; + bottom: 0; + box-shadow: 0 0 8px #888888; + background: white; +} +.package { + padding: .5em; +} +.group { + cursor: pointer; + padding: .5em .5em .5em 1em; + font-weight: bold; +} +.group img { + float: right; +} +article { + margin-left: 16em; + margin-top: 65px; + margin-bottom: 4em; + padding-top: 1em; + padding-left: 1em; +} +.search { + padding-left: 1em; + padding-top: 1em; + padding-right: 1em; + padding-bottom: 0.7em; +} +.search input { + border: 4px solid grey; + border-radius: 4px; + background: white url('../assets/search_grey_24px.svg') 10px 8px no-repeat; + padding: 10px 20px 10px 40px; + font-size: 16px; + width: 144px; +} + +@media all and (max-width: 1024px) { + .hidemenu { + visibility: hidden; + } + .topnav h1 { + padding: 0 0 0 64px; + } + .topnav img { + visibility: visible; + } + article { + margin-left: 1em; + } +} diff --git a/src/app/app.component.html b/src/app/app.component.html deleted file mode 100644 index b6931b5..0000000 --- a/src/app/app.component.html +++ /dev/null @@ -1,3 +0,0 @@ -<h1> - {{title}} -</h1> diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index c740bcd..7769024 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -1,32 +1,33 @@ -import { TestBed, async } from '@angular/core/testing'; - import { AppComponent } from './app.component'; -describe('AppComponent', () => { +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +describe('AppComponent', function () { + let de: DebugElement; + let comp: AppComponent; + let fixture: ComponentFixture<AppComponent>; + beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ - AppComponent - ], - }).compileComponents(); + declarations: [ AppComponent ] + }) + .compileComponents(); })); - it('should create the app', async(() => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.debugElement.componentInstance; - expect(app).toBeTruthy(); - })); + beforeEach(() => { + fixture = TestBed.createComponent(AppComponent); + comp = fixture.componentInstance; + de = fixture.debugElement.query(By.css('h1')); + }); - it(`should have as title 'app works!'`, async(() => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.debugElement.componentInstance; - expect(app.title).toEqual('app works!'); - })); + it('should create component', () => expect(comp).toBeDefined() ); - it('should render title in a h1 tag', async(() => { - const fixture = TestBed.createComponent(AppComponent); + it('should have expected <h1> text', () => { fixture.detectChanges(); - const compiled = fixture.debugElement.nativeElement; - expect(compiled.querySelector('h1').textContent).toContain('app works!'); - })); + const h1 = de.nativeElement; + expect(h1.innerText).toMatch(/angular/i, + '<h1> should say something about "Angular"'); + }); }); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index ff63e05..1b084c3 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,10 +1,100 @@ import { Component } from '@angular/core'; +import { OnInit } from '@angular/core'; + +import { Package } from './package'; +import { Type } from './type'; +import { PackageService } from './package.service'; +import { SelectionService } from './selection.service'; +import { Member } from './member'; +import { Parameter } from './parameter'; + +import { ActivatedRoute, Params } from '@angular/router'; +import { Location } from '@angular/common'; @Component({ selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.css'] + styleUrls: ['./app.component.css'], + template: ` +<nav class="topnav"> + <img src="assets/hamburger_24px.svg" (click)="showMenu = !showMenu"><h1>{{title}}</h1> +</nav> + +<nav class="sidenav" [class.hidemenu]="!showMenu"> + <div class="search"><input [(ngModel)]="filter" placeholder="Search..."></div> + + <ng-template ngFor let-package [ngForOf]="packages"> + <div *ngIf="filteredPackage(package)"> + <div class="package"><b>{{package.name}}</b></div> + <ng-template ngFor let-group [ngForOf]="package.groups"> + <div *ngIf="filteredGroup(group)"> + <div class="group" (click)="group.hidden = !group.hidden">{{typeKindNames[group.kind]}}<img src="assets/vdots_24px.svg" *ngIf="!group.hidden"><img src="assets/hdots_24px.svg" *ngIf="group.hidden"></div> + <ul class="types" [hidden]="group.hidden"> + <li *ngFor="let type of group.members | nameFilter:filter" class="type" + [class.selected]="type.id === selectedType" + [routerLink]="['/type', type.id]" + (click)="showMenu = false"> + {{type.name}} + </li> + </ul> + </div> + </ng-template> + </div> + </ng-template> +</nav> + +<article> + <router-outlet></router-outlet> +</article> + `, + providers: [ + PackageService, + SelectionService, + ], }) -export class AppComponent { - title = 'app works!'; +export class AppComponent implements OnInit { + title = 'ExtendJ API Documentation'; + showMenu = false; + packages : Package[]; + filter = ''; + selectedType = ''; + + private typeKindNames = { + 'ast-class': 'AST CLASSES', + 'interface': 'INTERFACES', + 'class': 'CLASSES', + }; + + constructor(private packageService: PackageService, + private selectionService: SelectionService) { + selectionService.selection$.subscribe(id => this.selectedType = id); + } + + ngOnInit(): void { + this.packageService.getPackages().then(packages => this.packages = packages); + } + + declaredAt(member: Member): string { + if (member.doc) { + return `${member.doc.ragFile}:${member.doc.line}`; + } else { + return ""; + } + } + + filteredPackage(pkg: Package): boolean { + var filter = this.filter.toLowerCase(); + for (var i = 0; i < pkg.groups.length; i++) { + if (this.filteredGroup(pkg.groups[i])) { + return true; + } + } + return false; + } + + filteredGroup(group: any): boolean { + var filter = this.filter.toLowerCase(); + var filtered = group.members.filter(member => member.name.toLowerCase().indexOf(filter) >= 0); + return filtered.length > 0; + } } + diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 67ae491..f65502b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,20 +1,46 @@ +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; +import { RouterModule } from '@angular/router'; -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; +import { TypeDetailsComponent } from './type-details.component'; +import { ParametersComponent } from './parameters.component'; +import { TypeReferenceComponent } from './type-ref.component'; +import { NameFilterPipe } from './name-filter.pipe'; +import { StringFilterPipe } from './string-filter.pipe'; +import { SourceViewComponent } from './source-view/source-view.component'; +import { EditorDirective } from './editor.directive'; +import { DeclaredAtComponent } from './declared-at/declared-at.component'; @NgModule({ - declarations: [ - AppComponent - ], - imports: [ + imports: [ BrowserModule, FormsModule, - HttpModule + HttpModule, + RouterModule.forRoot([ + { + path: 'type/:id', + component: TypeDetailsComponent + }, + { + path: 'source/:filename/:line', + component: SourceViewComponent + } + ]), + ], + declarations: [ + AppComponent, + TypeDetailsComponent, + ParametersComponent, + TypeReferenceComponent, + NameFilterPipe, + StringFilterPipe, + SourceViewComponent, + EditorDirective, + DeclaredAtComponent, ], - providers: [], - bootstrap: [AppComponent] + bootstrap: [ AppComponent ] }) export class AppModule { } diff --git a/src/app/declared-at/declared-at.component.spec.ts b/src/app/declared-at/declared-at.component.spec.ts new file mode 100644 index 0000000..806276d --- /dev/null +++ b/src/app/declared-at/declared-at.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DeclaredAtComponent } from './declared-at.component'; + +describe('DeclaredAtComponent', () => { + let component: DeclaredAtComponent; + let fixture: ComponentFixture<DeclaredAtComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DeclaredAtComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DeclaredAtComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/declared-at/declared-at.component.ts b/src/app/declared-at/declared-at.component.ts new file mode 100644 index 0000000..12d3c65 --- /dev/null +++ b/src/app/declared-at/declared-at.component.ts @@ -0,0 +1,26 @@ +import { Component, OnInit, Input } from '@angular/core'; + +import {Doc} from '../doc'; + +@Component({ + selector: 'declared-at', + template: ` + Declared at <a [routerLink]="['/source', filename, line]">{{filepath}}:{{line}}.</a> + `, +}) +export class DeclaredAtComponent implements OnInit { + + @Input() doc: Doc; + filename: string; + filepath: string; + line: string; + + constructor() { } + + ngOnInit() { + this.filepath = this.doc.ragFile; + this.filename = this.filepath.replace(/\/|\\/g, '_'); + this.line = String(this.doc.line); + } + +} diff --git a/src/app/doc.ts b/src/app/doc.ts new file mode 100644 index 0000000..d201353 --- /dev/null +++ b/src/app/doc.ts @@ -0,0 +1,26 @@ +export class Doc { + ast: string; + ragFile: string; + line: number; + description: string; + apilevel: string; + params: string[] + + paramDesc(name: string): string { + if (this.params) { + for (var i = 0; i < this.params.length; i++) { + var param = this.params[i]; + var index = param.indexOf(' '); + if (index >= 0 && param.substring(0, index) === name) { + return ' : ' + param.substring(index + 1); + } + } + } + return ''; + } + + static fromJson(json: any): Doc { + var obj = Object.create(Doc.prototype); + return Object.assign(obj, json); + } +} diff --git a/src/app/editor.directive.spec.ts b/src/app/editor.directive.spec.ts new file mode 100644 index 0000000..f385018 --- /dev/null +++ b/src/app/editor.directive.spec.ts @@ -0,0 +1,8 @@ +import { EditorDirective } from './editor.directive'; + +describe('EditorDirective', () => { + it('should create an instance', () => { + const directive = new EditorDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/editor.directive.ts b/src/app/editor.directive.ts new file mode 100644 index 0000000..ee368c7 --- /dev/null +++ b/src/app/editor.directive.ts @@ -0,0 +1,35 @@ +import { Directive, ElementRef, Input, OnChanges } from '@angular/core'; + +import * as CodeMirror from 'codemirror'; + +import 'codemirror/mode/clike/clike'; +import 'codemirror/addon/selection/active-line'; + +@Directive({ + selector: '[appEditor]' +}) +export class EditorDirective implements OnChanges { + editor: any; + @Input() sourceText: string; + @Input() sourceLine: number; + + constructor(public element: ElementRef) { + this.editor = new CodeMirror.fromTextArea(element.nativeElement, { + mode: 'text/x-java', + theme: 'mbo', + lineNumbers: true, + styleActiveLine: true, + lineWrapping: false, + }); + this.editor.setSize('100%', '100%'); + } + + ngOnChanges() { + this.editor.setValue(this.sourceText || 'no source'); + if (this.sourceLine) { + var line = this.sourceLine - 1; + this.editor.scrollIntoView({line: line + 30, ch: 0}); + this.editor.setCursor({line: line, ch: 0}); + } + } +} diff --git a/src/app/inherited-members.ts b/src/app/inherited-members.ts new file mode 100644 index 0000000..bb6cd91 --- /dev/null +++ b/src/app/inherited-members.ts @@ -0,0 +1,13 @@ +import {TypeRef} from './type-ref'; + +export class InheritedMembers { + superclass: TypeRef; + members: string[]; + + static fromJson(json: any): InheritedMembers { + return { + superclass: TypeRef.fromJson(json.superclass), + members: json.members as string[], + }; + } +} diff --git a/src/app/member-filter.service.spec.ts b/src/app/member-filter.service.spec.ts new file mode 100644 index 0000000..13aa2f5 --- /dev/null +++ b/src/app/member-filter.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { MemberFilterService } from './member-filter.service'; + +describe('MemberFilterService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [MemberFilterService] + }); + }); + + it('should ...', inject([MemberFilterService], (service: MemberFilterService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/member-filter.service.ts b/src/app/member-filter.service.ts new file mode 100644 index 0000000..0f44d01 --- /dev/null +++ b/src/app/member-filter.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@angular/core'; +import {Subject} from 'rxjs/Subject'; + +@Injectable() +export class MemberFilterService { + private filter = new Subject<string>(); + + filter$ = this.filter.asObservable(); + + constructor() { } + + setFilter(filter: string) { + this.filter.next(filter); + } +} diff --git a/src/app/member.ts b/src/app/member.ts new file mode 100644 index 0000000..6327b45 --- /dev/null +++ b/src/app/member.ts @@ -0,0 +1,40 @@ +import {Parameter} from './parameter'; +import {Doc} from './doc'; +import {TypeRef} from './type-ref'; + +export class Member { + name: string; + type: TypeRef; + doc: Doc; + parameters: Parameter[]; + throws: TypeRef[]; + + static fromJson(json: any): Member { + var params: Parameter[] = []; + var doc: Doc = undefined; + if (json.doc) { + doc = Doc.fromJson(json.doc); + } + if (json.params) { + params = json.params as Parameter[]; + } + var throws: TypeRef[] = undefined; + if (json.throws) { + throws = (json.throws as TypeRef[]).map(TypeRef.fromJson); + } + if (json.type) { + return Object.assign({}, json, { + type: TypeRef.fromJson(json.type), + parameters: params.map(param => Parameter.fromJson(param)), + doc: doc, + throws: throws, + }); + } else { + return Object.assign({}, json, { + parameters: params.map(param => Parameter.fromJson(param)), + doc: doc, + throws: throws, + }); + } + } +} diff --git a/src/app/name-filter.pipe.ts b/src/app/name-filter.pipe.ts new file mode 100644 index 0000000..45e8434 --- /dev/null +++ b/src/app/name-filter.pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'nameFilter' +}) + +export class NameFilterPipe implements PipeTransform { + transform(items: any[], filter: string): any { + if (!items || !filter) { + return items; + } + return items.filter(item => item.name.toLowerCase().indexOf(filter.toLowerCase()) >= 0); + } +} + diff --git a/src/app/package-entry.ts b/src/app/package-entry.ts new file mode 100644 index 0000000..716e779 --- /dev/null +++ b/src/app/package-entry.ts @@ -0,0 +1,11 @@ +import {TypeRef} from './type-ref'; + +export class PackageEntry { + kind: string; + name: string; + id: string; + + static fromJson(json: any): PackageEntry { + return json as PackageEntry; + } +} diff --git a/src/app/package.service.ts b/src/app/package.service.ts new file mode 100644 index 0000000..b440d72 --- /dev/null +++ b/src/app/package.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { Headers, Http } from '@angular/http'; + +import 'rxjs/add/operator/toPromise'; + +import { Package } from './package'; + +@Injectable() +export class PackageService { + private packageUrl = 'data/packages.json'; + + constructor(private http: Http) { } + + getPackages(): Promise<Package[]> { + return this.http.get(this.packageUrl) + .toPromise() + .then(response => (response.json().data as Package[]).map(pkg => Package.fromJson(pkg))) + .catch(res => Promise.reject(`Failed to load packages: ${res}`)); + } +} diff --git a/src/app/package.ts b/src/app/package.ts new file mode 100644 index 0000000..dd797eb --- /dev/null +++ b/src/app/package.ts @@ -0,0 +1,12 @@ +import { PackageEntry } from './package-entry'; + +export class Package { + name: string; + groups: any[]; + + static fromJson(json: any): Package { + return json as Package; + } +} + + diff --git a/src/app/parameter.ts b/src/app/parameter.ts new file mode 100644 index 0000000..929502a --- /dev/null +++ b/src/app/parameter.ts @@ -0,0 +1,14 @@ +import {TypeRef} from './type-ref'; + +export class Parameter { + type: TypeRef; + name: string; + + static fromJson(json: any): Parameter { + return { + type: TypeRef.fromJson(json.t), + name: json.n, + }; + } +} + diff --git a/src/app/parameters.component.ts b/src/app/parameters.component.ts new file mode 100644 index 0000000..1bd2227 --- /dev/null +++ b/src/app/parameters.component.ts @@ -0,0 +1,34 @@ +import { Component, Input, ComponentFactoryResolver } from '@angular/core'; + +import {Parameter} from './parameter'; + +@Component({ + selector: 'parameters', + styles: [` + .parameters { + display: inline; + height: 1.2em; + overflow: hidden; + text-overflow: ellipsis; + } + .parameter { + display: inline; + } + .sep { + display: inline; + margin-right: .3em; + } + `], + template: ` + <div class="parameters"> + <div *ngFor="let param of params; let isLast=last" class="parameter"> + <type-ref [type]="param.type"></type-ref> {{param.name}}<div *ngIf="!isLast" class="sep">,</div> + </div> + </div> + ` +}) +export class ParametersComponent { + @Input() params : Parameter[]; + + constructor(private _componentFactoryResolver: ComponentFactoryResolver) { } +} diff --git a/src/app/selection.service.ts b/src/app/selection.service.ts new file mode 100644 index 0000000..312d8cc --- /dev/null +++ b/src/app/selection.service.ts @@ -0,0 +1,13 @@ +import {Injectable} from '@angular/core'; +import {Subject} from 'rxjs/Subject'; + +@Injectable() +export class SelectionService { + private selection = new Subject<string>(); + + selection$ = this.selection.asObservable(); + + select(id: string) { + this.selection.next(id); + } +} diff --git a/src/app/source-view/source-view.component.css b/src/app/source-view/source-view.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/source-view/source-view.component.html b/src/app/source-view/source-view.component.html new file mode 100644 index 0000000..6c33c8c --- /dev/null +++ b/src/app/source-view/source-view.component.html @@ -0,0 +1,3 @@ +<p> + source-view works! +</p> diff --git a/src/app/source-view/source-view.component.spec.ts b/src/app/source-view/source-view.component.spec.ts new file mode 100644 index 0000000..bf324c2 --- /dev/null +++ b/src/app/source-view/source-view.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SourceViewComponent } from './source-view.component'; + +describe('SourceViewComponent', () => { + let component: SourceViewComponent; + let fixture: ComponentFixture<SourceViewComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SourceViewComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SourceViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/source-view/source-view.component.ts b/src/app/source-view/source-view.component.ts new file mode 100644 index 0000000..52af73e --- /dev/null +++ b/src/app/source-view/source-view.component.ts @@ -0,0 +1,30 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Params } from '@angular/router'; + +import 'rxjs/add/operator/switchMap'; + +import {SourceService} from '../source.service'; + +@Component({ + selector: 'app-source-viewer', + providers: [ SourceService ], + template: `<textarea appEditor [sourceText]="source" [sourceLine]="line">{{source}}</textarea>`, +}) +export class SourceViewComponent implements OnInit { + source: string; + line = 0; + + constructor(private sourceService: SourceService, + private route: ActivatedRoute) { } + + ngOnInit() { + this.route.params.switchMap((params: Params) => { + this.line = +params['line']; + return this.sourceService.getSource(params['filename']); + }) + .subscribe(source => { + this.source = source; + }); + } + +} diff --git a/src/app/source.service.spec.ts b/src/app/source.service.spec.ts new file mode 100644 index 0000000..a7dd4a3 --- /dev/null +++ b/src/app/source.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { SourceService } from './source.service'; + +describe('SourceService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [SourceService] + }); + }); + + it('should ...', inject([SourceService], (service: SourceService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/source.service.ts b/src/app/source.service.ts new file mode 100644 index 0000000..4bceace --- /dev/null +++ b/src/app/source.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { Headers, Http } from '@angular/http'; + +import 'rxjs/add/operator/toPromise'; + +@Injectable() +export class SourceService { + + constructor(private http: Http) { } + + getSource(filename: String): Promise<string> { + return this.http.get(`data/${filename}`) + .toPromise() + .then(response => response.text()) + .catch(res => Promise.reject(`Failed to load source: ${name}: ${res}.`)); + } +} diff --git a/src/app/string-filter.pipe.spec.ts b/src/app/string-filter.pipe.spec.ts new file mode 100644 index 0000000..4068ad8 --- /dev/null +++ b/src/app/string-filter.pipe.spec.ts @@ -0,0 +1,8 @@ +import { StringFilterPipe } from './string-filter.pipe'; + +describe('StringFilterPipe', () => { + it('create an instance', () => { + const pipe = new StringFilterPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/string-filter.pipe.ts b/src/app/string-filter.pipe.ts new file mode 100644 index 0000000..bd815e6 --- /dev/null +++ b/src/app/string-filter.pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'stringFilter' +}) +export class StringFilterPipe implements PipeTransform { + + transform(items: any[], filter: string): any { + if (!items || !filter) { + return items; + } + return items.filter(item => item.toLowerCase().indexOf(filter.toLowerCase()) >= 0); + } + +} diff --git a/src/app/type-details.component.css b/src/app/type-details.component.css new file mode 100644 index 0000000..8787da5 --- /dev/null +++ b/src/app/type-details.component.css @@ -0,0 +1,83 @@ +.doc-signature { + display: inline-block; + height: 1.3em; + width: calc(100% - 50px); /* Avoids line wrap in summary. */ + overflow: hidden; + vertical-align: middle; +} +.attribute-kind { + color: #888; +} +.member-type { + width: 12em; + overflow: hidden; + display: inline-block; + vertical-align: top; +} +.member-name { + font-weight: bold; +} +.doc-preview { + display: inline; + float: right; + width: 40%; + line-height: 1.2em; + height: 1.2em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: top; +} +summary { + cursor: pointer; + padding: .5em 0 .5em 0; +} +.member-details { + padding-bottom: .5em; +} +.members { + list-style-type: none; + margin: 0; + padding: 0; +} +.members li { + background-color: #EEE; + margin: .2em 0; + padding: 0 .7em 0 .7em; + border-radius: 4px; +} +.return { + display: inline-block; +} +.sep { + display: inline; + margin-right: .3em; +} +.filter input { + border: 4px solid grey; + border-radius: 4px; + background: white url('../assets/search_grey_24px.svg') 10px 8px no-repeat; + padding: 10px 20px 10px 40px; + font-size: 16px; + width: 144px; +} +.filter button { + border: 4px solid grey; + border-radius: 4px; + padding: 10px; + font-size: 16px; + background: lightgrey; +} + + +@media all and (max-width: 1024px) { + .doc-preview { + visibility: hidden; + } +} +@media all and (max-width: 500px) { + .member-type { + visibility: hidden; + width: 0; + } +} diff --git a/src/app/type-details.component.html b/src/app/type-details.component.html new file mode 100644 index 0000000..93344b7 --- /dev/null +++ b/src/app/type-details.component.html @@ -0,0 +1,125 @@ +<div *ngIf="type"> + <h2>{{type.kind}} {{type.name}}</h2> + <p *ngIf="type.superclass">extends <type-ref [type]="type.superclass"></type-ref> + <ng-container *ngIf="type.superinterfaces">implements <ng-container *ngFor="let iface of type.superinterfaces; let isLast = last"><type-ref [type]="iface"></type-ref><div *ngIf="!isLast" class="sep">, </div></ng-container></ng-container> + </p> + <div *ngIf="type.doc"> + <p [innerHTML]="type.doc.description"> + + <p *ngIf="type.doc.astdecl">JastAdd production: {{type.doc.astdecl}} + + <p *ngIf="type.doc && type.doc.ragFile"><declared-at [doc]="type.doc"></declared-at> + </div> + <div class="filter"> + <input [(ngModel)]="filter" placeholder="Filter members..."> + <button (click)="clearFilter()" [hidden]="filter === ''">Clear filter</button> + </div> + + <div *ngIf="filteredMembers('constr')"> + <h3>Constructors</h3> + <ul class="members"> + <li *ngFor="let member of type.groups['constr'] | nameFilter:filter"> + <details> + <summary> + <div class="doc-signature"><span class="member-name">{{member.name}}</span> ( <parameters [params]="member.parameters"></parameters> ) + <div class="doc-preview" *ngIf="member.doc" [innerHTML]="member.doc.description"></div></div> + </summary> + + <div class="member-details"> + <p *ngIf="member.doc" [innerHTML]="member.doc.description"></p> + <p *ngIf="member.doc && member.doc.ragFile"><declared-at [doc]="member.doc"></declared-at> + <p *ngFor="let param of member.parameters; let index = index"><b>Parameter {{index+1}}</b> <type-ref [type]="param.type"></type-ref> <b>{{param.name}}</b><span *ngIf="member.doc" [innerHTML]="paramDesc(member.doc, param.name)"></span></p> + <p *ngIf="member.throws">Throws <ng-container *ngFor="let excp of member.throws; let isLast=last"><type-ref [type]="excp"></type-ref><div *ngIf="!isLast" class="sep">, </div></ng-container></p> + </div> + </details> + </li> + </ul> + </div> + + <div *ngIf="filteredMembers('attr')"> + <h3>Attributes</h3> + <ul class="members"> + <li *ngFor="let member of type.groups['attr'] | nameFilter:filter"> + <details> + <summary> + <div class="doc-signature"><div class="member-type"><span *ngIf="member.doc" class="attribute-kind">{{member.doc.attribute}}</span> <type-ref [type]="member.type"></type-ref></div> <span class="member-name">{{member.name}}</span> ( <parameters [params]="member.parameters"></parameters> ) + <div class="doc-preview" *ngIf="member.doc" [innerHTML]="member.doc.description"></div></div> + </summary> + + <div class="member-details"> + <p *ngIf="member.doc" [innerHTML]="member.doc.description"></p> + <p *ngIf="member.doc && member.doc.ragFile"><declared-at [doc]="member.doc"></declared-at> + <p *ngFor="let param of member.parameters; let index = index"><b>Parameter {{index+1}}</b> <type-ref [type]="param.type"></type-ref> <b>{{param.name}}</b><span *ngIf="member.doc" [innerHTML]="paramDesc(member.doc, param.name)"></span></p> + <p><b>Returns</b> <type-ref [type]="member.type"></type-ref><ng-container *ngIf="member.doc && member.doc.return"> : <span class="return" *ngIf="member.doc" [innerHTML]="member.doc.return"></span></ng-container> + </div> + </details> + </li> + </ul> + </div> + + <div *ngIf="type.inherited_attributes"> + <ng-container *ngFor="let inherited of type.inherited_attributes"> + <ng-container *ngIf="inheritedMembers(inherited)"> + <h3>Attributes inherited from <type-ref [type]="inherited.superclass"></type-ref></h3> + <ng-container *ngFor="let member of inherited.members | stringFilter:filter; let isLast = last"><type-ref [type]="inherited.superclass" [name]="member" [filter]="member"></type-ref><ng-container *ngIf="!isLast">, </ng-container></ng-container> + </ng-container> + </ng-container> + </div> + + <div *ngIf="filteredMembers('field')"> + <h3>Fields</h3> + <ul class="members"> + <li *ngFor="let member of type.groups['field'] | nameFilter:filter"> + <details> + <summary> + <div class="doc-signature"><div class="member-type"><type-ref [type]="member.type"></type-ref></div> <span class="member-name">{{member.name}}</span> + <div class="doc-preview" *ngIf="member.doc" [innerHTML]="member.doc.description"></div></div> + </summary> + + <p *ngIf="member.doc" [innerHTML]="member.doc.description"></p> + <p *ngIf="member.doc && member.doc.ragFile"><declared-at [doc]="member.doc"></declared-at> + </details> + </li> + </ul> + </div> + + <div *ngIf="type.inherited_fields"> + <ng-container *ngFor="let inherited of type.inherited_fields"> + <ng-container *ngIf="inheritedMembers(inherited)"> + <h3>Fields inherited from <type-ref [type]="inherited.superclass"></type-ref></h3> + <ng-container *ngFor="let member of inherited.members | stringFilter:filter; let isLast = last"><type-ref [type]="inherited.superclass" [name]="member" [filter]="member"></type-ref><ng-container *ngIf="!isLast">, </ng-container></ng-container> + </ng-container> + </ng-container> + </div> + + <div *ngIf="filteredMembers('method')"> + <h3>Methods</h3> + <ul class="members"> + <li *ngFor="let member of type.groups['method'] | nameFilter:filter"> + <details> + <summary> + <div class="doc-signature"><div class="member-type"><type-ref [type]="member.type"></type-ref></div> <span class="member-name">{{member.name}}</span> ( <parameters [params]="member.parameters"></parameters> ) + <div class="doc-preview" *ngIf="member.doc" [innerHTML]="member.doc.description"></div></div> + </summary> + + <div class="member-details"> + <p *ngIf="member.doc" [innerHTML]="member.doc.description"></p> + <p *ngIf="member.doc && member.doc.ragFile"><declared-at [doc]="member.doc"></declared-at> + <p *ngFor="let param of member.parameters; let index = index"><b>Parameter {{index+1}}</b> <type-ref [type]="param.type"></type-ref> <b>{{param.name}}</b><span *ngIf="member.doc" [innerHTML]="paramDesc(member.doc, param.name)"></span></p> + <p><b>Returns</b> <type-ref [type]="member.type"></type-ref><ng-container *ngIf="member.doc && member.doc.return"> : <span class="return" *ngIf="member.doc" [innerHTML]="member.doc.return"></span></ng-container> + <p *ngIf="member.throws">Throws <ng-container *ngFor="let excp of member.throws; let isLast=last"><type-ref [type]="excp"></type-ref><div *ngIf="!isLast" class="sep">, </div></ng-container></p> + </div> + </details> + </li> + </ul> + </div> + + <div *ngIf="type.inherited_methods"> + <ng-container *ngFor="let inherited of type.inherited_methods"> + <ng-container *ngIf="inheritedMembers(inherited)"> + <h3>Methods inherited from <type-ref [type]="inherited.superclass"></type-ref></h3> + <ng-container *ngFor="let member of inherited.members | stringFilter:filter; let isLast = last"><type-ref [type]="inherited.superclass" [name]="member" [filter]="member"></type-ref><ng-container *ngIf="!isLast">, </ng-container></ng-container> + </ng-container> + </ng-container> + </div> +</div> diff --git a/src/app/type-details.component.ts b/src/app/type-details.component.ts new file mode 100644 index 0000000..3d54cd6 --- /dev/null +++ b/src/app/type-details.component.ts @@ -0,0 +1,81 @@ +import { Component } from '@angular/core'; +import { OnInit } from '@angular/core'; + +import { Package } from './package'; +import { Type } from './type'; +import { TypeService } from './type.service'; +import { MemberFilterService } from './member-filter.service'; +import { SelectionService } from './selection.service'; +import { Member } from './member'; +import { InheritedMembers } from './inherited-members'; +import {Doc} from './doc'; +import { Parameter } from './parameter'; + +import { ActivatedRoute, Params } from '@angular/router'; +import { Location } from '@angular/common'; + +import 'rxjs/add/operator/switchMap'; + +@Component({ + selector: 'type-details', + styleUrls: ['./type-details.component.css'], + templateUrl: './type-details.component.html', + providers: [ + TypeService, + MemberFilterService, + ], +}) +export class TypeDetailsComponent implements OnInit { + type: Type = Object.create(Type.prototype); + filter: string = ''; + + constructor(private typeService: TypeService, + private memberFilterService: MemberFilterService, + private route: ActivatedRoute, + private location: Location, + private selectionService: SelectionService) { + memberFilterService.filter$.subscribe(filter => this.filter = filter); + } + + ngOnInit() { + this.route.params.switchMap((params: Params) => { + return this.typeService.getType(params['id']) + }) + .subscribe(type => { + this.selectionService.select(type.id); + this.type = type; + }); + } + + declaredAt(doc: Doc): string { + return `${doc.ragFile}:${doc.line}`; + } + + paramDesc(doc: Doc, name: string): string { + if (doc) { + return doc.paramDesc(name); + } + return ''; + } + + filteredMembers(kind: string): boolean { + if (this.type.groups && this.type.groups[kind]) { + var filter = this.filter.toLowerCase(); + var filtered = this.type.groups[kind].filter(item => + item.name.toLowerCase().indexOf(filter) >= 0) + return filtered.length > 0; + } + return false; + } + + inheritedMembers(inherited: InheritedMembers): boolean { + var filter = this.filter.toLowerCase(); + var filtered = inherited.members.filter(item => item.toLowerCase().indexOf(filter) >= 0) + return filtered.length > 0; + } + + clearFilter() { + this.filter = ''; + } +} + diff --git a/src/app/type-ref.component.ts b/src/app/type-ref.component.ts new file mode 100644 index 0000000..34ad060 --- /dev/null +++ b/src/app/type-ref.component.ts @@ -0,0 +1,48 @@ +import { Component, Input, ComponentFactoryResolver } from '@angular/core'; + +import {Type} from './type'; +import { MemberFilterService } from './member-filter.service'; + +@Component({ + selector: 'type-ref', + styles: [` + .sep { + display: inline; + margin-right: .3em; + } + .usertype { + color: #8c1339; + text-decoration: none; + } + .non-usertype { + color: #444; + } + `], + template: `<a *ngIf="type.id; else elseBlock" class="usertype" [routerLink]="['/type', type.id]" (click)="onClick()">{{getName()}}</a><!-- + --><ng-template #elseBlock><span class="non-usertype">{{getName()}}</span></ng-template><!-- + --><ng-container *ngIf="type.args && !name"><!-- + --><<ng-container *ngFor="let arg of type.args; let isLast=last"><type-ref [type]="arg"></type-ref><div *ngIf="!isLast" class="sep">,</div></ng-container>><!-- + --></ng-container>` +}) +export class TypeReferenceComponent { + @Input() type : Type; + @Input() name : string; + @Input() filter : string; + + constructor(private _componentFactoryResolver: ComponentFactoryResolver, + private memberFilterService: MemberFilterService) { } + + getName(): string { + if (this.name) { + return this.name; + } else { + return this.type.name; + } + } + + onClick() { + if (this.filter) { + this.memberFilterService.setFilter(this.filter); + } + } +} diff --git a/src/app/type-ref.ts b/src/app/type-ref.ts new file mode 100644 index 0000000..154d4a6 --- /dev/null +++ b/src/app/type-ref.ts @@ -0,0 +1,54 @@ +export class TypeRef { + name: string; + id: string; + args: TypeRef[]; + + static fromJson(json: any): TypeRef { + var args: TypeRef[] = undefined; + if (json.a) { + args = (json.a as TypeRef[]).map(TypeRef.fromJson); + } + if (json.u) { + // User type. + if (json.i) { + return { + name: json.u, + id: TypeRef.typeId(json.u, json.i), + args: args, + }; + } else { + return { + name: json.u, + id: TypeRef.simpleName(json.u), + args: args, + }; + } + } else { + // Library or built-in type. + return { + name: json.n, + id: undefined, + args: args, + }; + } + } + + // Remove array and type args. + static simpleName(name: string): string { + return TypeRef.strip('<', TypeRef.strip('[', name)); + } + + static strip(tok: string, name: string): string { + const index = name.indexOf(tok); + if (index < 0) { + return name; + } else { + var simple = name.substring(0, index); + return simple; + } + } + + static typeId(name: string, idPattern: string): string { + return idPattern.replace('%', TypeRef.simpleName(name)); + } +} diff --git a/src/app/type.service.ts b/src/app/type.service.ts new file mode 100644 index 0000000..cb9eebf --- /dev/null +++ b/src/app/type.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { Headers, Http } from '@angular/http'; + +import 'rxjs/add/operator/toPromise'; + +import { Type } from './type'; + +@Injectable() +export class TypeService { + + constructor(private http: Http) { } + + getType(id: string): Promise<Type> { + const typeUrl = `data/${id}.json`; + return this.http.get(typeUrl) + .toPromise() + .then(response => Type.fromJson(response.json().data)) + .catch(res => Promise.reject(`Failed to load type: ${id}: ${res}.`)); + } +} diff --git a/src/app/type.ts b/src/app/type.ts new file mode 100644 index 0000000..6ad43f6 --- /dev/null +++ b/src/app/type.ts @@ -0,0 +1,69 @@ +import {Doc} from './doc'; +import {Member} from './member'; +import {TypeRef} from './type-ref'; +import {InheritedMembers} from './inherited-members'; + +export class Type { + kind: string; + name: string; + pkg: string; + mods: string[]; + id: string; + doc: Doc; + groups: { [id: string] : Member[]; }; + args: TypeRef[]; // Type arguments. + superclass: TypeRef; + superinterfaces: TypeRef[]; + inherited_methods: InheritedMembers[]; + inherited_attributes: InheritedMembers[]; + inherited_fields: InheritedMembers[]; + + static fromJson(json: any): Type { + var groups = {}; + if (json.groups) { + for (var i = 0; i < json.groups.length; i++) { + var group = json.groups[i]; + if (group.members) { + groups[group.kind] = (group.members as Member[]).map(member => Member.fromJson(member)); + } + } + } + var superclass: TypeRef = undefined; + if (json.superclass) { + superclass = TypeRef.fromJson(json.superclass); + } + var superinterfaces: TypeRef[] = undefined; + if (json.superinterfaces) { + superinterfaces = (json.superinterfaces as TypeRef[]).map(TypeRef.fromJson); + } + var inherited_methods: InheritedMembers[] = undefined; + if (json.inherited_methods) { + inherited_methods = (json.inherited_methods as any[]).map(inherited => + InheritedMembers.fromJson(inherited)); + } + var inherited_attributes: InheritedMembers[] = undefined; + if (json.inherited_attributes) { + inherited_attributes = (json.inherited_attributes as any[]).map(inherited => + InheritedMembers.fromJson(inherited)); + } + var inherited_fields: InheritedMembers[] = undefined; + if (json.inherited_fields) { + inherited_fields = (json.inherited_fields as any[]).map(inherited => + InheritedMembers.fromJson(inherited)); + } + var args: TypeRef[] = undefined; + if (json.args) { + args = (json.args as any[]).map(arg => TypeRef.fromJson(arg)); + } + return Object.assign({}, json, { + groups: groups, + id: TypeRef.typeId(json.name, json.id), + superclass: superclass, + superinterfaces: superinterfaces, + inherited_methods: inherited_methods, + inherited_attributes: inherited_attributes, + inherited_fields: inherited_fields, + }); + } +} + diff --git a/src/assets/arrow_down_24px.svg b/src/assets/arrow_down_24px.svg new file mode 100644 index 0000000..5630615 --- /dev/null +++ b/src/assets/arrow_down_24px.svg @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="arrow_down_24px.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="31.678384" + inkscape:cx="5.4015737" + inkscape:cy="12.166913" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + units="px" + inkscape:snap-page="true" + inkscape:window-width="1920" + inkscape:window-height="1017" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1028.3622)"> + <path + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 2.2910156,9.7011719 c -1.2697658,0 -2.30078123,1.0290281 -2.30078123,2.2988281 0,0.7108 0.32929283,1.337966 0.83593751,1.759766 l 7.92773432,7.929687 c 1.6731978,1.673198 4.3295148,1.76227 6.4648438,0 l 7.927734,-7.929687 C 23.653129,13.337966 23.982422,12.7108 23.982422,12 c 0,-1.2698 -1.031015,-2.2988281 -2.300781,-2.2988281 -0.672306,0 -1.271002,0.2938062 -1.691407,0.7539061 l -7.156209,7.154463 c -0.876709,1.039344 -0.910342,0.784861 -1.796652,-0.101233 L 3.9824219,10.455078 C 3.5620177,9.9949781 2.9633216,9.7011719 2.2910156,9.7011719 Z" + transform="translate(0,1028.3622)" + id="path4195" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ssccccssccccs" /> + </g> +</svg> diff --git a/src/assets/hamburger_24px.svg b/src/assets/hamburger_24px.svg new file mode 100644 index 0000000..19f1b5d --- /dev/null +++ b/src/assets/hamburger_24px.svg @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="hamburger_24px.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.627417" + inkscape:cx="2.1195307" + inkscape:cy="9.0194555" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + units="px" + inkscape:snap-bbox="true" + inkscape:snap-page="true" + inkscape:window-width="1920" + inkscape:window-height="1017" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1028.3621)"> + <rect + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="rect4136" + width="24" + height="4.6875" + x="0" + y="1028.3621" /> + <rect + y="1038.0182" + x="0" + height="4.6875" + width="24" + id="rect4163" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + <rect + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="rect4165" + width="24" + height="4.6880002" + x="0" + y="1047.6741" /> + </g> +</svg> diff --git a/src/assets/hdots_24px.svg b/src/assets/hdots_24px.svg new file mode 100644 index 0000000..90e6351 --- /dev/null +++ b/src/assets/hdots_24px.svg @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="hdots_24px.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.4" + inkscape:cx="3.1793573" + inkscape:cy="10.235898" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + units="px" + inkscape:snap-page="true" + inkscape:window-width="1920" + inkscape:window-height="1017" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1028.3622)"> + <g + id="g4164" + transform="matrix(0,1,-1,0,1052.3981,1027.706)" + style="fill:#666666"> + <circle + r="2.2991071" + cy="1030.6882" + cx="12.656249" + id="path4136" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + <circle + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="circle4160" + cx="12.656249" + cy="1040.3981" + r="2.2991071" /> + <circle + r="2.2991071" + cy="1050.1079" + cx="12.656249" + id="circle4162" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + </g> + </g> +</svg> diff --git a/src/assets/search_grey_24px.svg b/src/assets/search_grey_24px.svg new file mode 100644 index 0000000..468bbf8 --- /dev/null +++ b/src/assets/search_grey_24px.svg @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="search_grey_24px.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.627417" + inkscape:cx="-4.9515371" + inkscape:cy="9.0194555" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + units="px" + inkscape:snap-bbox="true" + inkscape:snap-page="true" + inkscape:window-width="1920" + inkscape:window-height="1017" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1028.3621)"> + <path + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;filter-blend-mode:normal;filter-gaussianBlur-deviation:0" + d="m 8.5185547,1028.9784 a 7.6455922,7.6455922 0 0 0 -7.64648437,7.6464 7.6455922,7.6455922 0 0 0 7.64648437,7.6465 7.6455922,7.6455922 0 0 0 3.7832033,-1.0078 l 8.482422,8.4824 2.34375,-2.3437 -8.371094,-8.3711 a 7.6455922,7.6455922 0 0 0 1.408203,-4.4063 7.6455922,7.6455922 0 0 0 -7.6464843,-7.6464 z m 0,2.5859 a 5.0602331,5.0602331 0 0 1 5.0605473,5.0605 5.0602331,5.0602331 0 0 1 -5.0605473,5.0606 5.0602331,5.0602331 0 0 1 -5.0605469,-5.0606 5.0602331,5.0602331 0 0 1 5.0605469,-5.0605 z" + id="path4155" + inkscape:connector-curvature="0" /> + </g> +</svg> diff --git a/src/assets/search_white_24px.svg b/src/assets/search_white_24px.svg new file mode 100644 index 0000000..14c170f --- /dev/null +++ b/src/assets/search_white_24px.svg @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="search_white_24px.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.627417" + inkscape:cx="2.1195307" + inkscape:cy="9.0194555" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + units="px" + inkscape:snap-bbox="true" + inkscape:snap-page="true" + inkscape:window-width="1920" + inkscape:window-height="1017" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1028.3621)"> + <path + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 8.5185547,1028.9784 a 7.6455922,7.6455922 0 0 0 -7.64648437,7.6464 7.6455922,7.6455922 0 0 0 7.64648437,7.6465 7.6455922,7.6455922 0 0 0 3.7832033,-1.0078 l 8.482422,8.4824 2.34375,-2.3437 -8.371094,-8.3711 a 7.6455922,7.6455922 0 0 0 1.408203,-4.4063 7.6455922,7.6455922 0 0 0 -7.6464843,-7.6464 z m 0,2.5859 a 5.0602331,5.0602331 0 0 1 5.0605473,5.0605 5.0602331,5.0602331 0 0 1 -5.0605473,5.0606 5.0602331,5.0602331 0 0 1 -5.0605469,-5.0606 5.0602331,5.0602331 0 0 1 5.0605469,-5.0605 z" + id="path4155" + inkscape:connector-curvature="0" /> + </g> +</svg> diff --git a/src/assets/vdots_24px.svg b/src/assets/vdots_24px.svg new file mode 100644 index 0000000..0d0f125 --- /dev/null +++ b/src/assets/vdots_24px.svg @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="vdots_24px.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.4" + inkscape:cx="3.1793573" + inkscape:cy="10.235898" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + units="px" + inkscape:snap-page="true" + inkscape:window-width="1920" + inkscape:window-height="1017" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1028.3622)"> + <g + id="g4164" + transform="translate(-0.65624905,-0.03587129)" + style="fill:#666666"> + <circle + r="2.2991071" + cy="1030.6882" + cx="12.656249" + id="path4136" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + <circle + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="circle4160" + cx="12.656249" + cy="1040.3981" + r="2.2991071" /> + <circle + r="2.2991071" + cy="1050.1079" + cx="12.656249" + id="circle4162" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + </g> + </g> +</svg> diff --git a/src/favicon.ico b/src/favicon.ico index 8081c7ceaf2be08bf59010158c586170d9d2d517..49ef308e4ec2f538edbac743b32ea687fa81da4a 100644 GIT binary patch literal 1150 zcmZQzU}Ruq5D);-3Je)63=Con3=A3!3=9Gc3=9ek5OD?)z`R}^+4-G14GX$-r_AkC zmm)zc!_szL!3CW<?-q3G{0HIro!SK;{UCD|bZOTt=+yQD@p0ivlT_GI^mpi<SkR%f zb3v!hACP$qI&}`C>s{2LZL^?LH+n&rZW;(L?9{mdRSR|l8vEbkc5M}8GZu7eZ$ndq zO&?rrVW)N$s(#&#==#^s4f(%&ZN>lH>&pJGnCt-2+o7|2ewVfcvR)8-VW-Y2Wc?@h z^!$JN^y&Y5_wN0F{P^+zCy(#^-#9Pi|NKs!QjmIdyr5Gjd104MJ}7(_cIo~5@a`3g z{#UPF{l9Tx{r`ho>dv6+MdvT+vskx&>)!tnX|?}7B8&ffMHT-qZJ7A~{ISJgys%TJ zYeA<@^@2{FJaoMv{=yF3qPcS?{%`G>{=d0*_W!2t>HlZVnfrh9l8pbm*Azg)0g^U$ zfz-qB{00S%McrEF3p#Xm!@^>5uPL&66!yYSoue?lAbMe^c05cUG4%Wn-4h^vFubfs zO=Ll*&Tl+=pUv;kj#$*8?Y^*E*JW;pwmwWRj9$>GyB(_k_rgw{tqVJKE<pJpw||}4 zV9W(mi;Z5;spkxe<HfC-k|6m79opGQ`d`oK(-s1W6N^_hX!9)S)M=aFsap%mf7tZ> ysApjK;LgDCft`V&ff<THY<>oY27U&H4~!6efRTX#h9B@VFg)UCVE8A`zyJUQb9{{e literal 5430 zcmZQzU}Ruo5D;KsU|>*S$Y5b$Py?|PAp8{q3=DlN3=A3!3=AM8AYn!Z5Xr#czyeZ( zivR!r&xj418$Jv*GrWJ_KzG-1BfZ1c*wkPXGdFx_X>R!NxVhoO|K^4d{u}6S1!F_q z-D{2Yk1J!-0}?Yge5hz{_;58uA4o3<BlJVe+4kE|cTc~u{waQt9vB9>)!gvmZ-37x z|HA^G{<km!=>=g_{h>jJ{(F1u`){DT<D-$@AzLK<?I3$6_rLoO0=XG4|AY9*?vIW* z0+RaQ-g*V%AH6-*NcuN})H~Zg`v3LI-~W4ef5p(hW%HB&@813VZ(+I}tlvO)doPmy zn;`ulykhC6|G$6#`+xJsPcXi5<Kur2{{8#!|7lZiLiKL_Z>YO_Ka&2>AiW?Q==1dd zpFjWpKYjB1|CUXk|8Lyz`2XWaU;lsm_UFI7%`Uip1Kr(Ek@Wus>4o8ayTAT_`}WU& zYtsk+t;~1)|M33j|D{XrBk2Xj>5iWu{pN-bm0|i|bV~ela9Gt=zWU$SbL~IK?Vj$S za0lr_U=#h*{N{!aePQ~L>B|>?{J(PP$N$%_zyII5<tduo5HpPRk2{(hKFmhe2V+;2 zy#l-8-@pI=!-5ZC)o-MCIMv+nVJ%D#GHq%6@c*-?zyBXS@(Qb7P<k}f+f!|B_z>iV ztH^p`?Eap2|5K7qVAXG^yX&rj?oL-w7=p05;X_Sx!-uWrh7X^^^xK+0MAN(LsiE%f zR%89+`XD{HFw{-c%?$5<HqhPq+CX<#x3S(aOKj>H82<mK1cS;MQ0d~pz`y`2XBt2S z4{9;PfX!}V#LNvJcAFXA{cfap;IXm(DSKk{P(zKm;lp@y!-pRsaRE#F+x{Er9X@TW ze@dPjc3?BZ-0-2bx#7cy5PLyo7zo4chopN0-5vjp^^UGJ(Lc+L%?xtH%ncvPBFb%$ zoyZv7euz5^^>+U@)<51!j(spa=7tYB%?%%}G&g*RQU>AoKg51e9t2@Sy}cie^-spb z%p{gJH+<M^Zusygs$IzbhOx2xA7nQ&HqtwI-&p^YH6i=W4If6D8$Ntb*k01?hqwit zMvoiopO(et4rD(^1V8&<UiA8ZS<x#nwlRMMt-D}$gJ|6TFE2g!zpmyY82fo2fR|TB zdIxXfu^(<nXu#9|pfVa<SpWb3zq<T2Tn-UF*zJ!Dht$a+y}y3_{qN$m2X4Qi-oCfk z>^C=jD2*@|<hO_a4;}ahG6IZGpZZR;{hK#E0qX$~>()Mk+YO2bP~N~{zdySD8Obj| zM!<2f-%~U<VEW(5e)oTnpFx^H;TPbG2z!wI2D;n+n;KkVMt1|Q`tJHQP<0M6?f?J9 z3qBIGf9e!a^g`6{-SZs9?-2X9{x{Y?ZH&eKrRe?#@$0JJKn(l;|Np0tfB$dU^aTlj z{=aGCV-)=UJ;J@Q(MNIEe<Bl${f9yJp<`>)NB>{G_yczXv5d24-(s~J6b8on$ERbl z|1P@SF#hBT?}@b*t}ZwGG*0`C^p5VuVn5Du$HngP|8L*^!OfmN<^BK6)R+G=Q=fy| zF`%~0gud%=`8#($|2Nb_EsH_^hG8SUL!d;0(!T(;(_#L_Mz31_32y$LU9fgEBK;pe z1ZmfS6xCE+!0C688;r1&0od$B77Oxu`v31=km>*b|M~O(zn9CC|K^4e`y)aRLnQwH z|Ns2?_y49wu(mw*FhDN@%ncuEBin_Gy>HJ~xIMF`e}LP+Yv(h#cyISLynYAyA5sQf zV}+#wbHj(Rxa>q0PfbLWN3UM~`EO<N;D4b1L3la+>C-Q8zW`zfcE7{qO!QBiBH7=G zY!5#6)ys(dU0(DOocCd#pELUo5q^ib%UJ(pHj@2o@!5$iS5@{JX5IhWw|@Tr`xlZg ze*XCT-`N3C&%peNi$*R3%ncu&Mz#YV+sfqO|L4zs!`%R5tX_%UCxG}9m;K0PfVttr z$N20-mz&TF=?}x~2GujZ-usCTdyt!u%K&r3hwy$Xx?R}#PBxFh{b`u}yLVx)XF&Gg z!bW=gL4_)~48q^WgtP_F-LZ7hN0{~hqa%-y<adypkjnsa?DzMAlt(8|z9HFeko};( z0#f>CH8*@%X>RxsXB~v@e-MAqt}p+y(od0KKd23Q;JLB>vB@U-=R{%lBh%)F4~5MQ zA5JzmeE1S%2R<C&{TOdO1Mw%w&i${A^^eas)<1=_?ThRN7#rI62K7O3q)$ls53vU| zzZvT7dvC0NY^jO<8FiR>l+fme51q{oAMQtPqr>VSnEi%&dp{fN9bIp%f69;&y9ntu oH+-0AZusz`nc=<PhI)Ix8|fY0ZmfSAK5j-xKe9a9Z~)!805(+b-T(jq diff --git a/src/index.html b/src/index.html index 78ba4dc..f20bd8b 100644 --- a/src/index.html +++ b/src/index.html @@ -2,8 +2,9 @@ <html> <head> <meta charset="utf-8"> - <title>RdView</title> + <title>JastAdd API Docs</title> <base href="/"> + <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> diff --git a/src/styles.css b/src/styles.css index 90d4ee0..55e5ebc 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1 +1,7 @@ /* You can add global styles to this file, and also import other style files */ +a:hover { + background: #ff4; +} +::selection { + background: #ff4; +} -- GitLab